Getting Started - Labelling Service
Introduction
This guide shows you step by step how to integrate the labeling service into your application.
Prerequisites
The Labeling Service requires a valid user. Find out more in our Getting Started - Authorization guide.
TL:TR
Used Endpoints
In short, you need to call this endpoint on the Labelling Service to get matching labels.
Description | HTTP Method | Process Controller Endpoint | Link to API Doc |
---|---|---|---|
Analyze and label transactions | POST |
| https://docs.finapi.io/?product=labelling#post-/labels/detailed |
Process Overview
cURL Example
Translated into cURL it looks like the following:
Step 1 - Send Transactions to Labelling Service
curl --location 'https://di-labelling-finapi-general-sandbox.finapi.io/api/v1/labels/detailed' \
--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": [
"AS9313222111"
],
"mandateRef": "MR123",
"counterpartAccountIban": "DE63500105177545638664",
"labels": [
"INSURANCE",
"LIABILITYINSURANCE"
]
}
}
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.
Field | Type | Mandatory |
---|---|---|
| This flag determines whether additional information of the labels should be output. These are for example the Level Of Detail. | no |
| In some cases, the labels can overlap (e.g. the | no |
| This field works like | no |
| This array contains the transactions to be labeled. | yes |
| 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. | yes |
| The If no ID is available for accounts, we recommend using a UUID. | yes |
| The amount of the transaction is primarily responsible for determining the direction of the payment in Labelling. | yes |
| 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. | no |
| The counterpart name should also always be included. This is especially important if the counterpart is a company or public agency. | no |
| Type of payment, for example "Transfer order". | no |
| 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. | no |
| SWIFT transaction type code. Example: This field should always be sent, if available, to get the best possible results. | no |
| The SEPA purpose code. Example: This field should always be sent, if available, to get the best possible results. | no |
| 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. | no |
| The mandate reference is a unique reference, given by the payment receiver for a direct debit mandate. | no |
| IBAN of the counterpart. This is especially important if the counterpart is a company or public agency. | no |
| BIC of the counterpart. | no |
| IBAN of the account | no |
| Account holder name | no |
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.
@Service
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
.uri(URI.create("${labellingUrl}${URI_LABELS}"))
// 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
.POST(
HttpRequest.BodyPublishers.ofString(
objectMapper.writeValueAsString(transactionRequest)
)
).build()
}
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.
@Service
class LabelledTransactionService(
@Value("\${finapi.instances.labelling.url}") private val labellingUrl: String,
private val objectMapper: ObjectMapper
) {
@Throws(RuntimeException::class)
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(
createRequest(
transactionRequest = transactionRequest,
accessToken = accessToken
),
HttpResponse.BodyHandlers.ofString()
)
// check for status code is 2xx or log and throw an exception
StatusCodeCheckUtils.checkStatusCodeAndLogErrorMessage(
response = response,
errorMessage = "Unable to label transactions"
)
// return the object of the mapped result
return objectMapper.readValue(response.body(), LabelledTransactionsResponseModel::class.java)
}
[...]
}
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.