AHOI
AHOI provides a turnkey multi-banking API that connects fintechs with banks in order to retrieve their customers' financial data. Using our multi-banking API, you can access the basic financial data of customers of almost all german banks via public interfaces.
Feature Overview
AHOI offers traditional banking functionality alongside various transaction refinements.
You can use AHOI to:
- manage accesses
- manage portfolios
- obtain securities
- manage bank accounts
- issue SEPA transfers
- obtain transactions
- generate monthly summaries with sums of incoming and outgoing transactions per bank account
- use automatic pattern detection to identify (or define) recurring transactions
- use automatic contract detection to identify contracts from transactions
- obtain a balance forecast based on identified transaction patterns
- make use of automatic transaction categorization
- manage portfolios
Quick Start Basics
This Cookbook enables you to get started quickly with AHOI. Before you begin, please register for an AHOI Sandbox Manager account.
Once you are registered, we recommend using our API Explorer to familiarize yourself with the API. The API Explorer contains the complete endpoint documentation. It allows you to test all AHOI endpoints and assists you with OAuth 2.0 authorization.
For general information about the AHOI API please refer to overview page.
Overview
Here are the steps necessary to proceed from registering a user to acquiring all of that user's transactions:
- Register with the Sandbox Manager
- Get a registration token
- Register the user
- Get a banking token
- List all providers
- Get access data for selected providers
- Create an access for the user
- Get all accounts
- Get all transactions for the account
AHOI OAuth Flow
The AHOI OAuth flow (step 2 to 4) as sequence diagram.
STEP 1: Register with the Sandbox Manager
The first step you need to complete before you can use the AHOI API is to register with the Sandbox Manager. With every registration we create a bank access with accounts, portfolios and transactions for our testing bank, the Sandbank. You can use the this bank access for your own testing purposes. All of the credentials you need can be found in the Sandbox Manager. You can also enter your own test data.
STEP 2: Get a registration token
The registration token is used to register an actual user with AHOI. To obtain a registration token, the external application authenticates with the application credentials. Further endpoints are inaccessible with this token.
You need to add a basic authorization header with your clientId
and clientSecret
to your request to obtain the registration token. You’ll find the credentials in the Sandbox Manager. These values must be encoded with base64 in a single string.
Example: Encode basic auth credentials in Java
String credentials = String.format("%s:%s", <clientId>, <clientSecret>);
String AUTH_BASIC_BASE64 = Base64.getEncoder()
.encodeToString(credentials.getBytes( StandardCharsets.UTF_8 ));
Request
POST /auth/v1/oauth/token?grant_type=client_credentials HTTP/1.1
Authorization: Basic <AUTH_BASIC_BASE64>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"access_token": "<REGISTRATION_TOKEN>",
"token_type": "bearer",
"expires_in": 1199,
"scope": "ANON ENC_DIS",
"jti": "8423faa7-e2ea-47c1-82a7-ace341506da8"
}
The "expires_in" value in the response contains the number of seconds until expiration. This token will expire in about 20 minutes.
STEP 3: User registration
Please insert the <REGISTRATION_TOKEN>
fetched in the previous step for the "bearer" token as the "Authorization" header in a request to the "user registration" endpoint.
Request
POST /ahoi/api/v2/registration HTTP/1.1
Authorization: Bearer <REGISTRATION_TOKEN>
Response
HTTP/1.1 201 CREATED
Content-Type: application/json; charset=utf-8
{
"installation": "<INSTALLATION_ID>"
}
STEP 4: Get a banking token
With the <INSTALLATION_ID>
you can now get a banking token. This token authenticates the actual user with AHOI and can be used by your application to execute banking actions on behalf of the user.
With this token, all endpoints can be used except registration, for which you need a registration token.
You need to send a X-Authorization-Ahoi
header with this request. The header value is an encoded JSON string that consists of the <INSTALLATION_ID>
,
a random 16-character string as nonce and the current date in ISO 8601 format.
{
"installationId":"<INSTALLATION_ID>",
"nonce":"0wYWLarLDBrWU7B2I1Go4A==",
"timestamp":"2018-11-01T11:32:44.413Z"
}
Encode this data with Base64URL to create the required header.
Example: Create X-Authorization-Ahoi header in Java
String installationId = <INSTALLATION_ID>;
// create nonce
byte[] nonce = new byte[32];
SecureRandom.getInstanceStrong().nextBytes(nonce);
String nonceBase64Enc = Base64.getEncoder().encodeToString(nonce);
// create timestamp
String timeStr = Instant.now().toString();
// create json string
String xAuthAhoiJson = String.format("{\"installationId\":\"%s\",\"nonce\":\"%s\",\"timestamp\":\"%s\"}",
installationId, nonceBase64Enc, timeStr);
// encode encrypted header value
String XAUTH_AHOI_BASE64_URL_ENC = Base64.getUrlEncoder().withoutPadding()
.encodeToString(xAuthAhoiJson.getBytes( StandardCharsets.UTF_8 ));
Request
POST /auth/v1/oauth/token?grant_type=client_credentials HTTP/1.1
Authorization: Basic <AUTH_BASIC_BASE64>
X-Authorization-Ahoi: <XAUTH_AHOI_BASE64_URL_ENC>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"access_token": "<BANKING_TOKEN>",
"token_type": "bearer",
"expires_in": 3599,
"scope": "ACCESS_C ACCESS_R ACCESS_U … TRANSFER_C ENC_DIS",
"CONTEXT_ID": "Jz5Hq14Uc6Y3vo…VuuF3u9OLqpkHXXG9yXPDWIbgevo8yNe",
"jti": "3ee3c5aa-77f0-44cb-aec1-69c8665a4bec"
}
Once again, the expires_in
value in the response contains the number of seconds until expiration. This token will expire in about 60 minutes.
From now on, you have to include this banking token ("access_token") in each of your requests for this user.
STEP 5: List all providers
To set up the bank accounts, a banking access needs to be created first. For this the "id" of the provider (i.e., bank) is required.
You can obtain a list of all available providers via the "list bank providers" endpoint.
Request
GET /ahoi/api/v2/providers HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{
"type": "BankProvider",
"id": "<PROVIDER_ID>",
"name": "Sandbank",
"location": "Hamburg",
"accessDescription": null,
"supported": true,
"bankCode": "99994000",
"bic": "TESTBICXXXX"
}
]
STEP 6: Get provider access data
Next you will need the fieldDescriptions
of the provider, which describe the required fields to authenticate with the bank.
Later these should be used to create a user interface to enter the access credentials.
For the time being we will use them to send the required values when creating the access.
These fieldDescriptions
are available at each provider resource which can be accessed via the "get provider" endpoint.
Request
GET /ahoi/api/v2/providers/<PROVIDER_ID> HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "BankProvider",
"id": "<PROVIDER_ID>",
"name": "Sandbank",
"location": "Hamburg",
"accessDescription": {
"infoText": "Sofern Ihr Institut Ihnen keine separate Benutzerkennung mitgeteilt hat, geben Sie bitte unter Benutzerkennung Ihre Kontonummer ein. In Abhängigkeit von Ihrem Kreditinstitut können zusätzliche Informationen, wie beispielsweise eine Kundennummer, hinterlegt werden. Diese Eingabe ist meistens optional.",
"fieldDescriptions": [
{
"id": "USERNAME",
"label": "Benutzerkennung",
"masked": false,
"format": "DEFINITELYALPHANUMERIC",
"transferredEncrypted": false,
"lengthMin": 1,
"lengthMax": 30
},
{
"id": "PIN",
"label": "PIN",
"masked": true,
"transferredEncrypted": false,
"format": "UNSPECIFIED",
"lengthMin": 5,
"lengthMax": 5
}
]
},
"supported": true,
"bankCode": "99994000",
"bic": "TESTBICXXXX"
}
STEP 7: Create an access
Now you have all the information you need to create a banking access. From the previous step you know that you need a USERNAME
and a PIN
for the Sandbank. Please refer to Sandbox Manager for the requested values.
Accesses are created via the "get a new access" endpoint.
Create Access Request
POST /ahoi/api/v2/accesses/async HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Content-Type: application/json
{
"providerId": "<PROVIDER_ID>",
"type": "BankAccess",
"accessFields": {
"USERNAME": "myuser",
"PIN": "12345"
}
}
Create Access Response
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
{
"type": "AccountSetupTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "CREATED",
"cause": "NONE",
"accessId": null,
"bankMessages": []
}
You receive the task with which AHOI is creating an Access and storing all supported Accounts from the bank. The task is working in the background and will make information available via the task resource.
You can poll its status via the "fetch state of task" endpoint or by simply registering a webhook to receive notifications.
Task request
GET /ahoi/api/v2/tasks/<TASK_ID> HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Task Response (in progress)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountSetupTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "IN_PROGRESS",
"cause": "NONE",
"accessId": null,
"bankMessages": []
}
When you receive this state please poll again until the state is either SUCCESS
or FAILURE
.
Task Response (success)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountSetupTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "SUCCESS",
"cause": "NONE",
"accessId": "<ACCESS_ID>",
"bankMessages": []
}
Response (error)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountSetupTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "FAILURE",
"cause": "AUTHORIZATION_WRONG",
"accessId": null,
"bankMessages": [
{
"level": "ERROR",
"message": "Fehlerhafte Authorisierung!",
"errorCode": "UNCLASSIFIED"
}
]
}
STEP 8: Get all accounts
With the access you have created, you can now acquire the related accounts by using the <ACCESS_ID>
as returned by the successful task at the "list accounts" endpoint.
Request
GET /ahoi/api/v2/accesses/<ACCESS_ID>/accounts HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{
"type": "BankAccount",
"id": "<ACCOUNT_ID>",
"name": "Giro Classic",
"..."
},
{
"type": "BankAccount",
"id": "f0629fd2-0f95-4f67-953d-e62bda0fa77d",
"name": "Giro Classic",
"..."
},
{
"type": "BankAccount",
"id": "4267646a-656e-44d7-a06b-854d4e53dc9e",
"name": "Depot",
"..."
}
STEP 9: Get all transactions
For each account you can now list all transactions by using the <ACCESS_ID>
and the <ACCOUNT_ID>
at the "list transactions for account" endpoint.
Request
GET /ahoi/api/v2/accesses/<ACCESS_ID>/accounts/<ACCOUNT_ID>/transactions HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{
"type": "GiroTransaction",
"id": "17b1f6bd-034d-4e17-a3df-e7f02e52ca3e",
"transactionPatternId": "8c47a610-2069-447b-b09c-6e4053ba6f3e",
"amount": {
"value": -6522,
"currency": "EUR"
},
"bookingDate": "2018-10-13T12:00:00Z",
"valueDate": "2018-10-13T12:00:00Z",
"creditor": "Stadtreinigung Musterstadt",
…
},
{
"type": "GiroTransaction",
"id": "e3a4d5ec-db2e-4900-bfd2-963a4e258fde",
"transactionPatternId": "f9b9f252-a096-493c-81bd-4fc11488d616",
"amount": {
"value": -5250,
"currency": "EUR"
},
"bookingDate": "2018-10-13T12:00:00Z",
"valueDate": "2018-10-13T12:00:00Z",
"creditor": "Rundfunk ARD, ZDF, DRadio",
…
},
…
]
Quick Start Account Refresh
Overview
An account refresh in AHOI retrieves latest transactions from the bank and updates the account balance.
These are the steps to execute the account refresh using the AHOI API without webhook notifications:
- Trigger the account refresh
- Poll the task state
- (in case of a required TAN) Fetch the TAN challenge
- (in case of a required TAN) Submit the TAN
- Fetch refreshed account / transactions
STEP 1: Trigger the account refresh
The first step in executing a single account refresh using the AHOI API is to trigger it. As a response you will receive a task, which indicates the current state of the account refresh.
An account refresh can be triggered via the "refresh account" endpoint.
Request
PUT /ahoi/api/v2/accesses/<ACCESS_ID>/accounts/<ACCOUNT_ID>/refresh HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
{
"type": "AccountRefreshTask",
"origin": "VAULT",
"state": "CREATED",
"cause": "NONE",
"id": "<TASK_ID>",
"bankMessages": [],
"accountId": "<account_Id>",
"accessId": "<access_Id>"
}
STEP 2: Poll the task state
The second step is to poll the task state. Therefore you need to query the task resource using the <TASK_ID>
returned in the response to the first step.
The task status is polled via the "fetch state of task" endpoint.
Request
GET /ahoi/api/v2/tasks/<TASK_ID> HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Poll for the task until its state leaves IN_PROGRESS
. After this two things can happen. Either the task results in a SUCCESS
, then you can directly go to STEP 5.
Or the tasks results in ACTION_REQUIRED
, then you have to provide a TAN.
Response (Case #1: SUCCESS)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountRefreshTask",
"origin": "VAULT",
"state": "SUCCESS",
"cause": "NONE",
"id": "<TASK_ID>",
"bankMessages": null,
"accountId": "<ACCOUNT_ID>",
"accessId": "<ACCESS_ID>"
}
Response (Case #2: ACTION_REQUIRED)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountRefreshTask",
"origin": "VAULT",
"state": "ACTION_REQUIRED",
"cause": "AUTHORIZATION_REQUIRED",
"id": "<TASK_ID>",
"bankMessages": null,
"accountId": "<ACCOUNT_ID>",
"accessId": "<ACCESS_ID>"
}
STEP 3: Fetch the TAN challenge
If you have to provide a TAN ("cause": "AUTHORIZATION_REQUIRED"
), the third step is to fetch the TAN challenge provided by the bank which is executing the account refresh.
The challenge is required to provide the proper TAN authorization method.
Task challenges are obtained via the "fetch task challenge" endpoint. You can find a challenge for the AHOI Sandbank in the example response below.
Request
GET /ahoi/api/v2/tasks/<TASK_ID>/authorizations HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"challenge" : "Bitte geben Sie die TAN ***<TAN>*** für den Connector ein!",
"additionalInformation" : "...",
"tanGuiUrl" : "/ahoi/webui/task/<TASK_ID>/tan",
"type" : "CHIPTAN"
}
To make things easier for you we send you the expected TAN in our test environment inside the challenge
field, so you can directly use it for the next step.
With real bank accounts you would get a description where you find your TAN (e.g. "Bitte geben Sie ihre smsTAN ein.") or information to initialize the TAN generation on special device (e.g. chipTAN or photoTAN).
We will soon add more details on this topic here.
STEP 4: Submit the TAN
The fourth step is to supply the TAN authorization, which can be done via two ways: - REST-API - AHOI TAN UI (browser)
Request via REST-API
For TAN providing via API, use the "authorize a task" endpoint.
PUT /ahoi/api/v2/tasks/<TASK_ID>/authorizations HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Content-Type: application/json
{
"type": "TanChallengeResponse",
"response": "<TAN>"
}
Response (SUCCESS)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "AccountRefreshTask",
"id": "<TASK_ID>",
"origin" : "VAULT",
"state": "SUCCESS",
"cause": "NONE",
"bankMessages": []
}
Response (FAILED, because TAN was invalid)
{
"type": "TransferTask",
"id": "<TASK_ID>",
"state": "FAILURE",
"cause": "AUTHORIZATION_WRONG",
"bankMessages": [
{
"level": "ERROR",
"message": "Die TAN ist falsch!",
"errorCode": "TAN_INVALID"
}
]
}
Request via TAN UI
To help you enter your TAN more easily, you can use the AHOI TAN UI.
Simply copy the tanGuiUrl
from STEP 3, call it with your favorite browser and follow the instructions.
For more details see the chapter AHOI TAN UI.
STEP 5: Fetch the refreshed account / transactions
After the account is refreshed, you can retrieve it. Just use the account resource as shown below (e.g., if you want to view your account balance) or you can fetch the latest transactions via the transaction resource.
Request
GET /ahoi/api/v2/accesses/<ACCESS_ID>/accounts/<ACCOUNT_ID> HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
[
{
"id": "<ACCOUNT_ID>",
"name": "Checking Account",
"userDefinedName": "My Checking Account",
"owner": "Max Mustermann",
"providerId": 0,
"kind": "GIRO",
"automaticRefreshInterval": 3600,
"type": "BankAccount",
"number": "0317899806",
"bankCodeNumber": "99994000",
"bic": "TESTBICXXXX",
"iban": "DE00999940000317899806",
"currency": "EUR",
"balance": {
"amount": {
"value": 133723,
"currency": "EUR"
},
"date": "2016-10-24T13:37:00+02:00"
}
}
]
Quick Start Transfer
Overview
Steps to execute a single SEPA transfer using the AHOI API without webhook notifications:
- Submit the SEPA transfer
- Poll the task state
- Fetch the TAN challenge
- Submit the TAN
STEP 1: Submit the SEPA transfer
The first step in executing a single SEPA transfer using the AHOI API is to submit a new transfer from one of your accounts that corresponds to your access.
Transfers are created via the "create a new transfer" endpoint.
Request
POST /ahoi/api/v2/accesses/<ACCESS_ID>/accounts/<ACCOUNT_ID>/transfers HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Content-Type: application/json
{
"iban" : "<IBAN>",
"bic" : "TESTBICXXXX",
"name" : "Stan S. Stanman",
"amount" : {
"value" : 5000,
"currency" : "EUR"
},
"purpose" : "Test"
}
Response
HTTP/1.1 202 Accepted
Content-Type: application/json; charset=utf-8
{
"type" : "TransferTask",
"id" : "<TASK_ID>",
"origin" : "VAULT",
"state" : "CREATED",
"cause" : "NONE",
"bankMessages" : []
}
STEP 2: Poll the task state
The second step is to poll the task state until the TAN challenge is available. To get the current state of the transfer task, you need to
query the task resource using the <TASK_ID>
returned in the response to the first step.
Request
GET /ahoi/api/v2/tasks/<TASK_ID> HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response (success)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "TransferTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "ACTION_REQUIRED",
"cause": "AUTHORIZATION_REQUIRED",
"bankMessages": []
}
Response (error)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type": "TransferTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "FAILURE",
"cause": "BANK_COMMUNICATION_FAILED",
"bankMessages": [
{
"level": "ERROR",
"message": "Unerwarteter Fehler: TAN request response loop ran into timeout!",
"errorCode": "UNCLASSIFIED"
}
]
}
STEP 3: Fetch the TAN challenge
The third step is to fetch the TAN challenge provided by the bank that is executing the SEPA transfer. The challenge is required to provide the proper TAN authorization method.
Task challenges are obtained via the "fetch task challenge" endpoint. You can find a challenge for the AHOI Sandbank in the example response below.
Request
GET /ahoi/api/v2/tasks/<TASK_ID>/authorizations HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"type" : "TanChallenge",
"challenge" : "Bitte geben Sie die TAN ***<TAN>*** für den Connector ein!",
"additionalInformation" : "Zahlung an: <IBAN> Betrag: 50,00 EUR",
"tanGuiUrl" : "/ahoi/webui/task/<TASK_ID>/tan",
"challengeType" : "CHIPTAN"
}
To make things easier for you we send you the expected TAN in our test environment inside the challenge
field, so you can directly use it for the next step.
With real bank accounts you would get a description where you find your TAN (e.g. "Bitte geben Sie ihre smsTAN ein.") or information to initialize the TAN generation on special device (e.g. chipTAN or photoTAN).
We will soon add more details on this topic here.
STEP 4: Submit the TAN
The fourth step is to supply the TAN authorization, which is done via the "authorize a task" endpoint.
Request
PUT /ahoi/api/v2/tasks/<TASK_ID>/authorizations HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
Content-Type: application/json
{
"type": "TanChallengeResponse",
"response": "<TAN>"
}
Transfer SUCCESS
{
"type": "TransferTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "SUCCESS",
"cause": "NONE",
"bankMessages": []
}
Transfer FAILURE
{
"type": "TransferTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "FAILURE",
"cause": "BANK_COMMUNICATION_FAILED",
"bankMessages": [
{
"level": "ERROR",
"message": "Unerwarteter Fehler: TAN request response loop ran into timeout!",
"errorCode": "UNCLASSIFIED"
}
]
}
Transfer failed authorization (TAN invalid)
{
"type": "TransferTask",
"id": "<TASK_ID>",
"origin": "VAULT",
"state": "FAILURE",
"cause": "AUTHORIZATION_WRONG",
"bankMessages": [
{
"level": "ERROR",
"message": "Die TAN ist falsch!",
"errorCode": "TAN_INVALID"
}
]
}
Quick Start Webhook
Webhooks are used to deliver notificiations about events within AHOI that you or your users can respond to. For now these events consist of transfer-related actions, such as providing an answer to a challenge (e.g., a TAN).
You will also receive a notification when new transactions are detected in accounts that have already been set up.
Overview
To receive webhook-based notifications, please complete the following steps:
- Create a publicly available endpoint for AHOI webhooks
- Register your endpoint URL with the AHOI Sandbox Manager
- Send a test event
- Try triggering an event
STEP 1: Webhook endpoint
Your webhook endpoint must be able to receive POST
requests with Content-Type: application/json
.
The body will contain a JSON object in the following format for events concerning a transfer task:
{
"type": "TransferTaskWebhookMessage",
"event": "TASK_AUTHORIZATION_REQUIRED",
"clientId": "<CLIENT_ID>",
"userContextId": "<CONTEXT_ID>",
"taskId": "<TASK_ID>",
"state": "ACTION_REQUIRED",
"cause": "AUTHORIZATION_REQUIRED"
}
As a response we expect you to reply with the HTTP status code 202
(Accepted). Please queue the message and process it asynchronously instead of immediately starting to process it before you send us the reply.
You can find a swagger specification for the model and REST endpoint. Try generating your server code using the swagger editor.
STEP 2: Register
Log in to your Sandbox Manager account and visit your user profile. Here you can enter the URL to which we will send webhook notifications.
STEP 3: Send a test event
Once you have entered the webhook URL and ensured that your endpoint is available, you can use the "Verify" button to send a test request.
STEP 4: Try triggering an event
You can try and create a transfer using the AHOI API and see if you receive a TASK_AUTHORIZATION_REQUIRED
webhook event asking you to enter a
TAN challenge. After you have successfully provided one, you should receive the message TASK_SUCCESS
.
Transfer with AHOI TAN UI
AHOI TAN UI is a web component designed to simplify the TAN authorization within your application, especially using Chip-TAN optic method. It is currently in an experimental state, this means that its security features will be improved soon.
Overview
This reference guide shows the usage of AHOI TAN UI in combination with AHOI API Explorer using the steps of Quick start transfer without webhook notifications.
- Submit the SEPA transfer
- Fetch the task state
- Fetch the TAN challenge
- Submit the TAN via TAN UI
- Fetch the task state again
STEP 1: Submit the SEPA transfer
First step to execute a single SEPA transfer using AHOI-API is to submit a new transfer from one of your accounts that corresponds to your access.
Request
Response
STEP 2: Fetch the task state
Second step is to fetch the task state to see if the TAN challenge is available. To get the current state of the transfer task you need to query the task resource using the task ID returned by the response of the first step.
Request
Response
STEP 3: Fetch the TAN challenge
Third step is to fetch the TAN challenge provided by the bank, executing the SEPA transfer. The challenge is required to provide proper TAN authorization scheme.
Request
Response
STEP 4: Submit the TAN via TAN UI
Fourth step is to supply the TAN. To do this, just open the URI path shown in
the response screenshot of STEP 3 named tanGuiUrl
within a browser or click on
button named "Enter TAN".
Opening dialog for Sandbank
Dialog with TAN before submit
Dialog in success state
Note: A loading spinner is shown while waiting for the result.
STEP 5: Fetch the task state again
Please refer to STEP 2 regarding example of how to do this.
Request
Response
How to Use Extended Encryption
Overview
Here are the steps to carry out a registration with extended encryption using the AHOI API:
- Get a registration token
- Retrieve a public key
- Create
X-Ahoi-Session-Security
header - Request a registration
- Extract the
installationId
- Encrypt the
X-Authorization-Ahoi
header to fetch a banking token
STEP 1: Get a registration token
This step is identical to the step outlined in the Quick Start.
Please copy your clientId
and clientSecret
from the AHOI Sandbox Manager and encode
them using the Base64 algorithm to a single string as depicted below.
Example: Encode basic auth credentials in Java
String credentials = String.format("%s:%s", <clientId>, <clientSecret>);
byte[] credentialsBytes = credentials.getBytes(StandardCharsets.UTF_8);
String AUTH_BASIC_BASE64 = Base64.getEncoder().encodeToString(credentialsBytes);
Send this to the OAuth service using the following request. This should result in a response similar to the one shown below.
Request
POST /auth/v1/oauth/token?grant_type=client_credentials HTTP/1.1
Authorization: Basic <AUTH_BASIC_BASE64>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"access_token": "<REGISTRATION_TOKEN>",
"token_type": "bearer",
"expires_in": 1199,
"scope": "ANON",
"jti": "c6d69c1c-3f57-417b-82d6-0f288f38ef65"
}
STEP 2: Retrieve a public key
When communicating with AHOI and using extended encryption, the additional X-Ahoi-Session-Security
header needs to be added to each request in which sensitive information is transmitted.
The session security header needs to be encrypted with an AHOI public key fetched in this step.
Request
GET /ahoi/api/v2/registration/keys HTTP/1.1
Authorization: Bearer <REGISTRATION_TOKEN>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"keyId": "<KEY_ID>",
"validUntil": "2018-10-11T16:00:00Z",
"publicKey": {
"value": "<KEY>",
"specification": "RSA_2048_SHA1"
},
"publicKeySignature": {
"value": "<KEY_SIGNATURE>",
"specification": "SHA1_WITH_RSA"
}
}
Identifier | Description |
---|---|
RSA_2048_SHA1 | RSA private / public key with 2048-bit key length and OAEP padding. ("RSA/ECB/OAEPWithSHA-1AndMGF1 Padding") |
SHA1_WITH_RSA | SHA1 signature with RSA private / public key with 2048-bit key length and PKCS1 padding. ("SHA1withRSA") |
STEP 3: Create an "X-Ahoi-Session-Security" header
Why is the X-Ahoi-Session-Security
request header necessary? Sensitive data like the PIN or the installationId
are protected by HTTPS transport security
and encrypted by a symmetricKey
for each session you create while using extended encryption. This symmetricKey
must be encrypted using an asymmetric public key,
fetched in the previous step before it is send as the "sessionKey" in the X-Ahoi-Session-Security header.
The session key is used to encrypt and later to decrypt the sensitive user data in AHOI and vice versa.
The necessary steps to build the header are:
- Generate the symmetric key (AES)
- Decode the public key received from AHOI
- Encrypt the symmetric key with the AHOI public key to secure the session key
- Encode the session key with Base64URL
- Build the JSON string containing
publicKeyId
,sessionKey
andkeySpecification
- Encode the JSON string using Base64URL to create the header value
Example: How to create the "X-Ahoi-Session-Security" header in Java
// 1. Generate a simple symmetricKey
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256);
SecretKey key = generator.generateKey();
byte[] symmetricKey = key.getEncoded();
// 2. Decode and parse PublicKey
byte[] data = Base64.getUrlDecoder().decode(<KEY>);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(data));
// 3. Encrypt symmetricKey with the recieved public key
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encryptedsymmetricKey = cipher.doFinal(symmetricKey);
// 4. Encode Base64 url-safe
Base64.Encoder base64UrlEnocder = Base64.getUrlEncoder().withoutPadding();
String sessionKey = base64UrlEncoder.encodeToString(encryptedsymmetricKey);
// 5. Build JSON string
String headerTemplate = "{\"publicKeyId\":\"%s\",\"sessionKey\":\"%s\",\"keySpecification\":\"AES\"}";
String header = String.format(headerTemplate, <KEY_ID>, sessionKey);
byte[] headerBytes = header.getBytes(StandardCharsets.UTF_8);
// 6. Now encode JSON to create header value
String BASE64_ENCODED_JSON_HEADER = base64UrlEnocder.encodeToString(headerBytes);
STEP 4: User registration
After the X-Ahoi-Session-Security
request header has been built, the registration request is submitted
as detailled below. The corresponding response contains an encrypted installationId
.
User registration is done via the "user registration" endpoint.
Request
POST /ahoi/api/v2/registration HTTP/1.1
Authorization: Bearer <REGISTRATION_TOKEN>
X-Ahoi-Session-Security: <BASE64_ENCODED_JSON_HEADER>
Response
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"installation":"<ENCRYPTED_INSTALLATION_ID>"
}
STEP 5: Extract installation ID with session key (your symmetricKey)
The decryption of the installationId
from the registration response with your symmetric key is shown below.
Example: Decrypt response in Java
// 1.Decode the base64 url-safe data
byte[] encryptedInstallation = Base64.getUrlDecoder().decode(<ENCRYPTED_INSTALLATION_ID>);
// 2.Decrypt with your symmetricKey
byte[] iv = new byte[16];
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey secKey = new SecretKeySpec(symmetricKey, "AES");
cipher.init(Cipher.DECRYPT_MODE, secKey, new IvParameterSpec(iv));
String installationId = new String(cipher.doFinal(encryptedInstallation), "UTF-8");
STEP 6: Encrypt "X-Authorization-Ahoi" header to fetch the banking token
In the event that extended encryption is enabled, the X-Authorization-Ahoi
needs to be encrypted, too.
This header is used to request a banking token from the AHOI OAuth service.
We will skip the instructions here on how to create nonce and timestamp values for the header string. This is described in STEP 4 of the Quick Start.
Accesses are created via the "get a new access" endpoint.
Example: Encrypt "X-Authorization-Ahoi" header in Java
String xAuthorizationAhoiHeader = "{ \"installationId\": \"<INSTALLATION_ID>\", \"nonce\": \"<nonce>\", \"timestamp\": \"<JAVA_TIMESTAMP>\" }";
// encrypt header value
byte[] key = Base64.getUrlDecoder().decode(<appSecretKey>);
byte[] iv = Base64.getUrlDecoder().decode(<appSecretIV>);
SecretKey secKey = new SecretKeySpec(key, SymmetricAlgorithm.AES_256.getAlgorithm());
Cipher cipher = Cipher.getInstance(SymmetricAlgorithm.AES_256.getTransformation());
cipher.init(1, secKey, new IvParameterSpec(iv));
byte[] encryptedJson = cipher.doFinal(xAuthAhoiJson.getBytes(StandardCharsets.UTF_8));
// encode encrypted header value
String XAUTH_AHOI_BASE64_URL_ENC = Base64.getUrlEncoder().withoutPadding().encodeToString(encryptedJson);
STEP 7: Create an access
When creating an access with extended encryption enabled, is slightly different than described in STEP 7 of Basic Quick Start
Beside the need to supply the X-Ahoi-Session-Security
header, the credentials must be encrypted using your symmetricKey
.
Example: Encrypt access credentials in Java
byte[] iv = new byte[16];
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKey secKey = new SecretKeySpec(symmetricKey, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secKey, new IvParameterSpec(iv));
byte[] enc_username = cipher.doFinal("meinAnmeldename".getBytes(StandardCharsets.UTF_8));
byte[] enc_pin = cipher.doFinal("meinePin".getBytes(StandardCharsets.UTF_8));
String USER_ENC_BASE64 = Base64.getUrlEncoder().withoutPadding().encodeToString(enc_username);
String PIN_ENC_BASE64 = Base64.getUrlEncoder().withoutPadding().encodeToString(enc_pin);
Here is an example of how the request to AHOI might look when creating an access with extended encryption enabled.
Request
POST /ahoi/api/v2/accesses/async HTTP/1.1
Authorization: Bearer <BANKING_TOKEN>
X-Ahoi-Session-Security: <BASE64_ENCODED_JSON_HEADER>
Content-Type: application/json
{
"type" : "BankAccess",
"providerId" : "<PROVIDER_ID>",
"accessFields" : {
"USERNAME" : "<USER_ENC_BASE64>",
"PIN" : "<PIN_ENC_BASE64>"
}
}
AHOI Resources
This section provides a brief overview of the various key components of AHOI. If you're uncertain about what a resource actually represents, please begin by consulting this section.
App service
Your app-specific service that connects to AHOI.
Installation
An installation represents one context for user-specific data.
Access
An access represents a login to an assigned provider (e.g., login credentials for online banking). It includes data such as the username and PIN. Which fields are required will be described in the provider's input field descriptions.
Each access has a validation state that describes the validity of the credentials. This state might change over time if the credentials are no longer valid.
Account
An account is a grouping of financial records (e.g., a bank account). It contains information about the account name, the account holder, the account type as well as related information such as the account number, IBAN or account balance. It is associated with one or more accesses.
An automatic background refresh of transactions can be activated for every account.
Provider
A provider represents financial institutions AHOI can connect to (e.g., banks).
Transaction
A transaction represents a financial operation associated with an account. A bank account transaction contains information about the payee and the creditor, the amount, the date on which it was booked, the availability of funds and its purpose. A transaction might also be related to a recurring transaction pattern.
Task
A task represents a job which will be processed by AHOI asynchronously and possibly in multiple steps. Each task has an id, origin, state, cause and may contain bank messages.
Origin
The origin of a task is an enumeration and can either be VAULT
or PLAIN
. Vault tasks require user resources to be set up within AHOI, whereas plain tasks work without setting them up previously.
State
The state describes its tasks condition. It's an enumeration and may be CREATED
, IN_PROGRESS
, ACTION_REQUIRED
, SUCCESS
, FAILURE
or UNKNOWN
.
┌─────────────────────┐ v │ CREATED ───┬──> IN_PROGRESS ───┬──> ACTION_REQUIRED ───┬──> SUCCESS │ │ │ └───────────────────┴───────────────────────┼──> FAILED │ └──> UNKNOWN
CREATED
The task has been created successfully but processing has not been started.
IN_PROGRESS
The task is currently being processed, wait until it leaves this state.
ACTION_REQUIRED
The task is at a point where it requires user interaction, refer to its cause for details.
SUCCESS
The task has been completed successfully.
FAILURE
There has been an error and the task has failed. The cause and/or bank messages might contain additional information.
UNKNOWN
Something unexpected has happened and the task has failed, please contact us.
Cause
A cause offers additional insight to tasks which are in the ACTION_REQUIRED
or FAILURE
state. It's an enumeration and all possible values are listed below.
NONE
There is no cause. This is the usual cause whenever the task is not in ACTION_REQUIRED
state.
LOGIN_REQUIRED
The task requires a login to be able to continue. Login information from the provider is available via the "fetch login information" endpoint. Provide username and pin by sending a request to the "provide login information" endpoint of your task.
AUTHORIZATION_METHOD_REQUIRED
The financial institution has offered a choice of multiple TAN authorization methods and the task requires a selection. The available authorization methods are accessible via the "fetch authorization methods" endpoint, while the selection itself is done via the "select authorization method" endpoint.
AUTHORIZATION_REQUIRED
The task requires an TAN authorization. The challenge is available via the "fetch task challenge" endpoint. Task authorization is done via the "authorize a task" endpoint.
INTERNAL_ERROR
AHOI has run into an internal error and can't continue with the task processing.
BANK_COMMUNICATION_FAILED
There are technical problems with the communication to the server of the provider.
NO_BANK_TASK_CREATED
The task is in an invalid state which prevents AHOI from starting the communication with the provider (e.g., transfer from an account which does not support transfers).
ACCESS_WRONG
Selected access is invalid.
ACCESS_LOCKED
Selected access is locked.
BAD_REQUEST
Unable to continue with the task as the request is invalid (e.g., AHOI doesn't support the requested task for the selected provider).
LOGIN_WRONG
Authentication failed because the provided credentials are invalid.
AUTHORIZATION_METHOD_WRONG
Authorization method selection failed.
AUTHORIZATION_FAILED
Authorization failed due to unspecified problems.
AUTHORIZATION_WRONG
Authorization failed because the provided TAN is invalid.
UNKNOWN
Something unexpected has happened and the task has failed, please contact us.
Bank Messages
A list containing the messages from the financial institute.
API Explorer Tutorial
The API Explorer is integrated directly into the AHOI documentation. Follow the steps below to experience your first success with the API Explorer:
Open your Sandbox Manager so you can see your service-specific credentials.
Open the API Explorer in a second browser tab.
Copy your credentials (
clientId
,clientSecret
,appSecretIV
andappSecretKey
) from the Sandbox Manager into the respective fields in the API Explorer in the sidebar located on the left.Click on
Get Registration Token
. This obtains a registration token that appears in theToken
field.Select
Registration
in the menu on the left, thenUser Registration
, and finally selectTRY
for this endpoint. The previously anonymous user will now be registered and that user'sinstallationId
will be displayed. You don't have to remember theinstallationId
— just close the pop-up. The API Explorer will automatically put the variable into the corresponding field in the sidebar on the left. The Explorer has also automatically fetched a banking token for you, so you can now access all banking-specific AHOI endpoints. Congratulations!Set up your AHOI Sandbank access in AHOI. To do this, first take note of your AHOI Sandbank credentials (
blz
,username
,pin
) in the Sandbox Manager. Then switch toProvider
in the menu and selectList bank providers
. Insert the bank code of the AHOI Sandbank (99994000
) into thebankCode
field and execute the request withTRY
.As a result, you've now received an
id
for theprovider
. Next, selectAccess
in the menu and then select theCreate a new access
endpoint. By clicking on the JSON body sample on the right, you can add anaccessDto
to your request. Replace the values in theproviderId
,USERNAME
andPIN
fields with your own values, delete the entry forCUSTOMERNUMBER
and then execute the request. In response, you should get an HTTP status code202
with anAccessSetupTask
object with anid
andstate
CREATED
(refering to the task state).Use the
Task
menu and selectFetch state of task
. Enter the given task id and clickTRY
. Repeat this while thestate
is notSUCCESS
(orFAILURE
). In the positive case there should be theaccessId
field filled with a UUID for you to continue and try further endpoints.
Your bank account is now successfully set up and AHOI has retrieved the account data for the AHOI Sandbank. And you also have all the other
banking endpoints such as Get Account
or List Transactions
at your fingertips. Just give it a try!
FAQ
Banks
Which banks are currently supported within the sandbox system?
Right now, you can only use the AHOI Sandbank.
Why can I only use "AHOI Sandbank" within the sandbox system?
To provide access to real bank accounts, registration with the BaFin is required. Our registration is not yet complete, but we are expecting it to take place in 2020.
Which banks does AHOI support?
Currently AHOI supports all banks that offer FinTS with PIN and TAN. These include the savings and cooperative banks as well as most private banks. One exception is Commerzbank.
Which banks will AHOI connect to in the future?
- Which banks we connect in the future depends on our customers' needs. Feel free to contact us if your bank is not yet supported.
- The PSD2 requires banks to provide certain interfaces. We are working on supporting these too.
- We also plan to connect to banks via web scraping.
How do I set up a bank access?
- To set up a bank access, you first have to obtain the provider ID of the bank from AHOI. You send this to AHOI together with your banking credentials.
- For each bank, the field identifiers and the user log in instructions to be provided are different. Please check the provider information for the bank in question.
- AHOI initially sets up all accounts supported by a particular bank access. If you do not need all accounts, just delete the ones you do not need.
Accounts
My accounts are gone. What happened?
- AHOI stores your accounts under the
installationId
. Therefore you must remember and save theinstallationId
. - AHOI deletes all data under an
installationId
if there have not been any AHOI requests for thisinstallationId
within 90 days
Why do I always have to re-register with AHOI to see my account?
You don't! When you register, you receive an installationId
, which you have to save. The next time you return,
you can use this installationId
without having to re-register and you can then access your data again.
To manage data for multiple users/installations, you will have to create your own mapping of the users/installation to the corresponding installationId
.
What account types does AHOI support?
AHOI basically supports giro-type (i.e., current, checking) and securities accounts. Both account types are also offered by the AHOI Sandbank.
What account types are still planned in AHOI?
Here, too, we are guided by the needs of our customers and will implement new types of accounts provided that there is sufficient demand for them.
Transactions
How do I get new transactions from my bank?
There are two ways to get new transactions from your bank (as well as from the AHOI Sandbank).
AHOI will automatically update the accounts for a given
installationId
every other hour.To force a manual refresh use the resource
POST /accesses/{ACCESS_ID}/accounts/{ACCOUNT_ID}/refresh
. You will receive a task and once thestate
isSUCCESS
your account will be up to date.
Why am I not receiving new transactions in my AHOI Sandbank accounts?
The AHOI Sandbank accounts currently only create transactions when you register with the Sandbox Manager. New transactions are not generated on an ongoing basis. However, you can generate your own transactions:
By creating transfers to these accounts via the AHOI API. For example, if you transfer money from one demo account to another demo account, corresponding transactions are created.
You have the possibility to create transactions directly in the Sandbox Manager for a demo account.
If you want to have a new account, delete one of your maximum of five (5) accounts and create a new account based on a selected target group in your Sandbox Manager.
If you would like to fill your account with new transactions (if, for example, the last transaction is 30 days old), you can also do this in the Sandbox Manager.
Payment transactions / bank transfers / direct debits
What kind of payment transactions does AHOI currently support?
AHOI does not currently offer any payment transactions for actual accounts. You can only perform a bank transfer with our demo accounts for testing and integration purposes. We are working on support for real-world accounts.
Managing my Sandbox Manager access
I forgot my password for the Sandbox Manager. What do I do?
No problem! You can use our "Forgot password" function to securely request a new password. You will then receive an e-mail with detailed reset instructions.
I want to delete / log out / deregister my sandbox access. How can I do that?
We're sorry to see you go, but it's still a very simple process. Just log into the Sandbox Manager. Under "Settings" you'll find a section titled "Delete your sandbox access".
I want to delete data in my sandbox account without deleting the sandbox account itself. How do I do that?
Simply log into the Sandbox Manager. You can:
a) delete a single installationId
from the overview page at the bottom of the "Delete Installation Data" section, or
b) delete all installationId
s and their associated data for a clientId
at once. Consult the "Delete All Installation Data" section on the overview page.
Production use of AHOI
I want to use AHOI in one of my applications. What do I have to do?
It depends on your application. Please contact us via e-mail at ahoi-api@starfinanz.de. It helps if you're able to describe your application and the AHOI features you want to use as well as early quantitative factors (e.g., number of users, estimated number of updates per day, estimated number of new transactions), because this will enable us to better determine the costs.
Can I also use AHOI if I am not/do not have a company regulated by ZAG?
If you are only processing your own company's account data (e.g., incoming payment checks, statistics), we can offer you a solution. Simply contact us at ahoi-api@starfinanz.de — ideally with your contact details and a short description of what you would like to do, how many bank accounts are involved and how many transactions might occur per day.
Can I use AHOI for my own application as a private individual?
No, private individuals are not allowed to use AHOI in production contexts. As a private individual, you are only allowed to use the sandbox with demo accounts.
Engineering / software development
Is there a client SDK for AHOI?
We can currently offer SDKs for Java and JavaScript. However, we are unable to provide any guarantee for these clients. The clients should help you to build your applications in a straightforward way.
Contact us via e-mail at ahoi-api@starfinanz.de with questions.
Is there a way for me to generate an AHOI client?
Yes, you can generate a client by using our swagger file, but you will have to do it yourself.
Security
Why does AHOI store my bank access credentials?
AHOI frequently attempts to refresh your accounts in order to provide you with up-to-date information as well as to inform you via notifications about new events such as transactions. If we had to request your credentials every time, this convenient and automated approach wouldn't be possible.
Why does the AHOI client have to encrypt certain data? Why is transport encryption via TLS insufficient?
Mobile devices in particular are often exposed to insecure networks where communication can be intercepted and surveilled. To avoid this, certain data has to be encrypted.
Are there any security requirements for clients that I want to provide to my customers?
Yes, we have security requirements for clients using the AHOI API. However, these requirements only apply once the application goes live to your customers. On request, we can provide you with the "Security Standard for AHOI Clients" prior to the conclusion of the contract.
How is the encrypted JSON data encoded?
The encrypted JSON data must be serialized via Base64URL encoding (without padding) (see also the Quick Start section).
How can I tell AHOI to work with or without extended encryption?
Within the sandbox, you can turn this on or off in the Sandbox Manager via the "Advanced Encryption" setting. In a production environment this is configured by us for you (see also the first two questions regarding security above).
Technical
Do you support Cross-Origin Resource Sharing (CORS) requests?
AHOI is a server-to-server API and is not intended to be used directly from browser. If you want to use AHOI we expect you to connect to it via your own application service.
Are requests to AHOI rate-limited?
Yes, requests to AHOI (/ahoi/api/**
) are rate-limited with a token-bucket implementation.
Each user is limited to 100 requests per second and ip.
Any additional requests will be rejected with a 429: Too Many Requests
Error.
What does it mean when my account is locked?
After 3 successive failed login attempts an account gets locked for 15 minutes.
This means that each login attempt will result in a 423: Locked
.
Contact Us
Do you need assistance? Is anything unclear?
We're more than happy to help. Just send us an e-mail: ahoi-api@starfinanz.de
We also welcome your feedback on our documentation.
Glossary
- BaFIN: "Bundesanstalt für Finanzdientleistungsaufsicht"
- BANKING TOKEN: Extended AHOI OAuth2 token
- IBAN: "International Bank Account Number"
- JSON: "JavaScript Object Notation"
- SEPA: "Single Euro Payments Area"
- TAN: "Transaction Authorization Number"
- ZAD: "Zahlungsauslösedienst" defined by PSD2 rules
- ZAG: "Zahlungsdiensteaufsichtsgesetz" defined by German law
- 2FA: "Two Factor Authentication"
- SCA: "Strong Customer Authentication" - requiring 2FA for bank requests