Skip to main content
Skip table of contents

Getting Started - Labelling Service


This guide shows you step by step how to integrate the labeling service into your application.


The Labeling Service requires a valid user. Find out more in our Getting Started - Authorization guide.


Used Endpoints

In short, you need to call this endpoint on the Labelling Service to get matching labels.


HTTP Method

Process Controller Endpoint

Link to API Doc

Analyze and label transactions



Process Overview

cURL Example

Translated into cURL it looks like the following:

Step 1 - Send Transactions to Labelling Service
curl --location '' \
--header 'Content-Type: application/json' \
--data '{
  "transactions": [
      "transactionId": "4e760145-2e65-4242-ac33-488943528c93",
      "accountId": "4e760145-2e65-4242-ac33-488943528c93",
      "amount": -23.99,
      "purpose": "Vertrag AS-9313222111 Kfz-Versicherung Kfz-Vers. PIR-FD 123 01.02.23 - 31.11.23",
      "counterpartName": "Allianz Versicherungs AG",
      "type": "Ueberweisungsauftrag",
      "typeCodeZka": "600",
      "typeCodeSwift": "NCHG",
      "sepaPurposeCode": "ACCT",
      "counterpartCreditorId": "DE10ZZZ00000051878",
      "counterpartMandateReference": "MR123",
      "counterpartAccountIban": "DE63500105177545638664",
      "counterpartBic": "DGHYDEH1XXX",
      "accountIBAN": "DE82500105175392237546",
      "accountHolderName": "John Doe"

The result looks like this:

  "transactions": [
      "transactionId": "4e760145-2e65-4242-ac33-488943528c93",
      "accountId": "4e760145-2e65-4242-ac33-488943528c93",
      "creditorId": "DE10ZZZ00000051878",
      "bic": "DGHYDEH1XXX",
      "contractNumbers": [
      "mandateRef": "MR123",
      "counterpartAccountIban": "DE63500105177545638664",
      "labels": [


Implementation Guide

See a full working project here: finAPI Data Intelligence Product Platform Examples (Bitbucket)
Code from this guide can be found here: Labelling of Transactions (Bitbucket)
Environment overview can be found here: Environments

The guidelines and the example project are written in Kotlin with Spring Boot. But it is easily adoptable for other languages and frameworks.
For the HTTP connection, we are using here plain Java HttpClient, to be not restrictive for the client selection and that everything is transparent.

Only the actual functionality is discussed in this guideline. Used helper classes or the models can be looked up in the source code.

However, we always recommend using a generated client from our API, which reduces the effort of creating models and functions to access the endpoints and eliminates potential sources of error.

To learn how to generate and apply the API client using the Process Controller "Create user and exchange with access_token" as an example, please see the Getting Started - Code Generator (SDK) Integration.

Please also note that the code presented here and also the repository is only an illustration of a possible implementation. Therefore, the code is kept very simple and should not be used 1:1 in a production environment.

Step 1 - Send Transactions to Labelling Service

Labelling transactions with the Labelling Service requires an access_token.

To learn how to obtain a token, please read our Getting Started - Authorization guide.

The Labelling Service has no connection to any data source.
This means that the more fields of the transactions are sent to the labeling service, the better the result. Filling in the mandatory fields only usually does not provide any results.

To create labels we use the /api/v1/labels/detailed endpoint on the Labelling Service.

As a body, we need to send an array, which is containing the transactions, which should be labeled.

For more information have a look at our Labelling Service Analyze and label transactions (with detailing) API Reference.

Theoretically, any number of transactions can be sent in one request. However, this can lead to timeouts.
To avoid this, we recommend transmitting larger quantities of transactions in junk of 500-1,000 transactions.

This may well be done in parallel, spreading the processing over several servers even in finAPI, which can significantly reduce the overall labeling time.





This flag determines whether additional information of the labels should be output. These are for example the Level Of Detail.



In some cases, the labels can overlap (e.g. the CARLOAN label is part of BANKANDCREDITLOANANDINTEREST and also of MOBILITY. Depending on the order of the level of detail 1 labels, which represents the main interest of the client, a label can become more significant than another. See the for defaults.



This field works like levelOfDetail1Priority, but provides a more finegrained prioritization for all INCOME sublabels at the level of detail 2. See the for defaults.



This array contains the transactions to be labeled.



Defines a unique ID of a transaction. In the response, this unique ID is used as a bracket to allow mapping on the client side between the requested transaction and the returned data and labels.

If no ID is available for transactions, we recommend using a UUID.



The accountId is a unique identifier of the associated account. Since it can happen that a transactionId is not unique, this ID provides a way to provide uniqueness about the transaction and the account.

If no ID is available for accounts, we recommend using a UUID.



The amount of the transaction is primarily responsible for determining the direction of the payment in Labelling.



About the purpose of use can be determined some labels. In addition, it allows, among other things, the extraction of further data.

This field should always be sent to get the best possible results.



The counterpart name should also always be included. This is especially important if the counterpart is a company or public agency.



Type of payment, for example "Transfer order".



Type code of the transaction (Zentraler Kreditausschuss). Possible values range from 1 through 999.

This field should always be sent, if available, to get the best possible results.



SWIFT transaction type code. Example: NCHG

This field should always be sent, if available, to get the best possible results.



The SEPA purpose code. Example: CCRD

This field should always be sent, if available, to get the best possible results.



The creditorID is a unique identifier of companies and allows us to apply special rules for them.

This field should always be sent, if available, to get the best possible results.



The mandate reference is a unique reference, given by the payment receiver for a direct debit mandate.



IBAN of the counterpart. This is especially important if the counterpart is a company or public agency.



BIC of the counterpart.



IBAN of the account



Account holder name


First, we create a new service LabelledTransactionService. This gets the host URL and the basepath (/api/v1) of the labeling service.
Additionally, the constructor gets the ObjectMapper Jackson at this point so we can map between JSON and objects.

In the function createRequest() the accessToken is also passed.
This can of course also be obtained from a SecurityContext or another place. For this example, however, it makes the procedure in this way more obvious.

The LabelledTransactionsRequestModel is based on the request body of the endpoint and should ideally be generated from the OpenAPI specification.
For this example, all models were stored as code in the Bitbucket repository.

class LabelledTransactionService(
    @Value("\${finapi.instances.labelling.url}") private val labellingUrl: String,
    private val objectMapper: ObjectMapper
) {
     * Create a HttpRequest with configured URLs and required headers.
    private fun createRequest(
        transactionRequest: LabelledTransactionsRequestModel,
        accessToken: String
    ): HttpRequest {
        return HttpRequest.newBuilder()
            // build URL for https://<labelling>/api/v1/labels/detailed
            // set Content-Type header to application/json
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            // add the access token as bearer token
            .header(HttpHeaders.AUTHORIZATION, "Bearer $accessToken")
            // add the body

    companion object {
        const val URI_LABELS = "/labels/detailed"

Now that we can create a request, we create a HttpClient to be able to send the request.

After that, the status code should be checked, and the result JSON mapped into a LabelledTransactionsResponseModel object using the ObjectMapper.
You can find the model definition for that also in the Bitbucket repository.

The status code check is only adopted in a very rudimentary way here and should be implemented more precisely for proper integration.
However, this check is sufficient for the example.

class LabelledTransactionService(
    @Value("\${finapi.instances.labelling.url}") private val labellingUrl: String,
    private val objectMapper: ObjectMapper
) {
    fun labelTransactions(
        transactionRequest: LabelledTransactionsRequestModel,
        accessToken: String
    ): LabelledTransactionsResponseModel {
        // get an access token with Process Controller
        val client = HttpClient.newBuilder().build()

        // send the request and fetch the response
        val response = client.send(
                transactionRequest = transactionRequest,
                accessToken = accessToken

        // check for status code is 2xx or log and throw an exception
            response = response,
            errorMessage = "Unable to label transactions"

        // return the object of the mapped result
        return objectMapper.readValue(response.body(),

After these methods have been implemented correctly, the calling method can now map the relevant results of the label back to the transactions and output or save them.

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.