- General information
- Access & Identification of TPP
- Support for this implementation on the Berlin Group API
- OAuth as a Pre-step
- Authentication endpoints
- Payment endpoints
Berlin Group Conformity : Implementation Guidelines version 1.3.6
Authorisation protocol: oAuth 2.0
Security layer: A valid QWAC Certificate for PSD2 is required to access the Berlin Group API, and should be included in every request. The official list of QTSP is available on the European Comission eIDAS Trusted List. For the N26 PSD2 Dedicated Interface API, the QWAC Certificate must be issued from a production certificate authority.
ℹ️ Certificates can be renewed by making an API call using the new certificate, which will then be onboarded automatically.
https://xs2a.tech26.de
https://xs2a.tech26.de/sandbox
- A TPP shall connect to the N26 PSD2 dedicated API by using an eIDAS valid certificate (QWAC) issued
- N26 shall check the QWAC certificate in an automated way and allow the TPP to identify themselves with the subsequent API calls
- As the result of the steps above, the TPP should be able to continue using the API without manual involvement from the N26 side
Service | Support |
---|---|
Supported SCA Approaches | Decoupled (Oauth2 as a pre-step) |
SCA Validity | 20 minutes |
Supported payment schemes | SEPA Credit Transfers, Instant SEPA Transfers |
Support of Periodic payments | Supported |
Support of Bulk payments | Not Supported |
fundsAvailable | Not Supported |
App to app redirection | Not supported |
OAuth2 is supported by this API through the authentication of a PSU in a pre-step, as per the diagram below:
Access Token | |
---|---|
Purpose | Access for API calls in one session |
How to get | 1. Make a request to GET /oauth2/authorize providing a redirectUrl and a hashed code verifier 2. Redirect users to n26 web page, where they will log in. If successful, page will be redirected to the URL provided on step 1, along with an authCode. 3. Use the authCode along with the unhashed code verifier on POST /oauth2/token |
Validity | 20 min |
Storage | NEVER |
ℹ️ PISP flow does not provide refresh tokens for security purposes
⚠️ The TPP should not use those access tokens on base URLs other thanxs2a.tech26.de
. Access tokens issued for PISP cannot be used for AISP flows.
These endpoints are used to retrieve an access token for use with the /payments endpoints.
Note: any values shown between curly braces should be taken as variables, while the ones not surrounded are to be read as literals.
This begins the authorization process. Users should be redirected to the URL supplied in the response.
GET /oauth2/authorize?client_id=PSDDE-BAFIN-000001&
scope=DEDICATED_PISP&
code_challenge=w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI&
redirect_uri=https://tpp.com/redirect&
response_type=CODE&
state=1fL1nn7m9a
HTTP/1.1
Supported query parameters:
Name of parameter | Description |
---|---|
client_id | This should match the QWAC certificate’s organization identifier.This field may be obtained by running the following command on the QWAC certificate: $ openssl x509 -in certificate.pem -noout -text |
scope | Accepted value: “DEDICATED_PISP”. Mandatory field. |
code_challenge | SHA256 hash of the code_verifier to be provided on POST /oauth2/token. Minimum size 43 characters, maximum 128. Should be Base-64 URL encoded, as per https://tools.ietf.org/html/rfc7636##section-4.2: BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) . Please refer to https://tonyxu-io.github.io/pkce-generator/ for sample values. So as an example, code_verifier should be set as “foobar” while code challenge would be “w6uP8Tcg6K2QR905Rms8iXTlksL6OD1KOWBxTK7wxPI”. Mandatory field. |
redirect_uri | URI to which users will be redirected back when the authorization process is completed. Mandatory field. |
state | Random state string which should be returned on the query string when N26 redirects back, so the TPP can link the redirection to the original authorization request. Mandatory field. |
response_type | Accepted value: “CODE”. Mandatory field. |
HTTP/1.1 302 Found
location: https://app.n26.com/open-banking?requestId=0daa152a-651a-4592-8542-47ff60799deb&state=1fL1nn7m9a&authType=XS2A
When users are redirected back from the URL supplied in the previous request (step 7 of the sequence diagram), the following two query string parameters should be extracted and verified
- state - should match the state supplied in the initiate authorization request
- code - this is the authorization code which will be used to retrieve the token
As an example, if the TPP provided “https://www.tpp.com/redirect“ as redirect_uri, after the users have successfully logged in, the TPP can expect a redirection to the following URL:
https://www.tpp.com/redirect?code=dbtF5AqOApjjSnNF5TK3w3gaEPdwtV2&state=1fL1nn7m9a
Upon receiving this redirect, the TPP can make the following request to retrieve the access token:
POST /oauth2/token?role=DEDICATED_PISP HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=dbtF5AqOApjjSnNF5TK3w3gaEPdwtV2&
code_verifier=foobar&
redirect_uri=https://tpp.com/redirect
Supported query parameters:
Name of query parameter | Description |
---|---|
role | Accepted value: “DEDICATED_PISP ” to generate a PISP-only token. Mandatory field. |
Supported form parameters:
Name of parameter | Description |
---|---|
grant_type | Accepted value: “authorization_code”. Mandatory parameter. |
code | The authorization code as returned by N26 as a parameter (“code”) on the redirect URL (step 7 of the sequence diagram). Mandatory parameter. |
code_verifier | Value of the code verifier; should match hashed code challenge fromGET /oauth2/authorize request. Mandatory parameter. |
redirect_uri | The same redirect URI that was provided to theGET /oauth2/authorize request. Optional parameter. |
Note that no refresh tokens are provided for security purposes.
HTTP/1.1 200 OK
{
"access_token": "{{access_token}}",
"token_type": "bearer",
"expires_in": {{expires_in_seconds}}
}
HTTP/1.1 400 Bad Request
{
"userMessage": {
"title": "Error",
"detail": "Please try again later."
},
"error_description": "Bad Request",
"detail": "Bad Request",
"type": "invalid_request",
"error": "invalid_request",
"title": "invalid_request",
"status": 400
}
ℹ️ Please note that the debtorAccount parameter is not mandatory; if this parameter is excluded, the payment will be executed from the customer's main account. From October 4th 2023, this behavior remains the same to users with a single IBAN. If this parameter is excluded and a user has multiple IBANs, the user will be asked to select an account for the payment in the N26 app via a push notification. Then, the payment will only be executed once the user has selected an IBAN through the new screen. Until this selection is complete:
- The “debtorAccount” value will be null in the GET payment details endpoints.
- The payment status will remain “RCVD”
- If the selection is not completed within 5 mins, the payment status will change to "RJCT"
POST /v1/berlin-group/v1/payments/sepa-credit-transfers HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
Content-Type: application/json
{
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"creditorName": "Seller",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Reference text"
}
⚠️ Allowed special characters in remittanceInformationUnstructured for N26 SEPA CT are: -():,.+?&"'/\
HTTP/1.1 201 Created
aspsp-sca-approach: DECOUPLED
{
"transactionStatus": "RCVD",
"paymentId": "fe9564bc-02d6-4822-ac5f-a294ee70cc55",
"_links": {
"status": {
"href": "/v1/berlin-group/v1/payments/sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55/status"
},
"self": {
"href": "/v1/berlin-group/v1/payments/sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55"
},
"scaStatus": {
"href": "/v1/berlin-group/v1/payments/sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55/authorisations/711df249-058c-4305-a5e7-e116b7efd480"
}
}
}
This endpoint is intended to be polled by the TPP to determine whether the users have confirmed the payment (as we are using the decoupled SCA approach).
Statuses currently supported:
Status code | Description |
---|---|
RCVD | Received. Initial status for a payment. A certification has been sent to the user’s app. |
ACCP | AcceptedCustomerProfile. User has confirmed the in-app certification and the payment has been successfully initiated. |
ACFC | AcceptedFundsChecked. User has enough funds to perform a payment, and a hold has been applied on the funds. |
ACSC | AcceptedSettlementCompleted. Payment execution process has been successfully completed by N26. This is NOT a confirmation that the beneficiary has received the funds. |
RJCT | Rejected. Payment failed to be initiated or executed. |
The final status of a payment is either ACSC or RJCT.
ACSC
is only applied after reconciliation from BundesBank which, in most cases, takes place at the end of the day. Until then, the payment may stay in the intermediate status ACFC
.
GET /v1/berlin-group/v1/payments/sepa-credit-transfers/{{paymentId}}/status HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"transactionStatus": "ACSC"
}
GET /v1/berlin-group/v1/payments/sepa-credit-transfers/{{paymentId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
- If "debtorAccount" is selected
HTTP/1.1 200 OK
{
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP"
}
- If "debtorAccount" is not selected
HTTP/1.1 200 OK
{
"debtorAccount": null,
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP"
}
GET /v1/berlin-group/v1/payments/sepa-credit-transfers/{{paymentId}}/authorisations HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"authorisationIds": [
"e93bf74e-9444-4a5e-8524-648d80848126"
]
}
GET /v1/berlin-group/v1/payments/sepa-credit-transfers/{{paymentId}}/authorisations/{{authorisationId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"scaStatus": "finalised"
}
ℹ️ Please note that the debtorAccount parameter is not mandatory; if this parameter is excluded, the payment will be executed from the customer's main account. From October 4th 2023, this behavior remains the same to users with a single IBAN. If this parameter is excluded and a user has multiple IBANs, the user will be asked to select an account for the payment in the N26 app via a push notification. Then, the payment will only be executed once the user has selected an IBAN through the new screen. Until this selection is complete:
- The “debtorAccount” value will be null in the GET payment details endpoints.
- The payment status will remain “RCVD”
- If the selection is not completed within 5 mins, the payment status will change to "RJCT"
Customers are required to accept Terms and Conditions, specifically for the SEPA Instant feature, once prior to performing the transfer. Furthermore, non-premium customers (i.e. customers with an N26 Standard account) are charged a fee for each instant transfer. This fee differs by market, ranging from €0.49-€1.99, and can be found on the N26 website of the relevant market.
POST /v1/berlin-group/v1/payments/instant-sepa-credit-transfers HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
Content-Type: application/json
{
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"creditorName": "Seller",
"creditorAccount": {
"iban": "DE02100100109307118603"
},
"remittanceInformationUnstructured": "Reference text"
}
⚠️ Allowed special characters in creditorName for N26 SEPA CT - ( :,.+?/ )
⚠️ Allowed special characters in remittanceInformationUnstructured for N26 SEPA CT - (:,.+?/-')
HTTP/1.1 201 Created
aspsp-sca-approach: DECOUPLED
{
"transactionStatus": "RCVD",
"paymentId": "a4e7c6e3-ef2f-440c-ac0f-36dcafe4551c",
"_links": {
"status": {
"href": "/v1/berlin-group/v1/payments/instant-sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55/status"
},
"self": {
"href": "/v1/berlin-group/v1/payments/instant-sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55"
},
"scaStatus": {
"href": "/v1/berlin-group/v1/payments/instant-sepa-credit-transfers/fe9564bc-02d6-4822-ac5f-a294ee70cc55/authorisations/711df249-058c-4305-a5e7-e116b7efd480"
}
}
}
Customers should accept the Term And Conditions for the SEPA Instant feature prior to performing the first transfer. In the event that the customer has not accepted the Terms And Conditions for the SEPA Instant feature, before initiating the transfer, the following response would be sent to the TPP:
HTTP/1.1 307 Temporary Redirect
Location: https://app.n26.com/login?redirect=%2Fterms-and-conditions
⚠️ : The temporary redirect is the end of the payment initiation. After the customer has accepted the Terms and Conditions, the TPP is required to make a new POST request to initiate the payment again in order for it to be executed.
This endpoint is intended to be polled by the TPP to determine whether the users have confirmed the payment (as we are using the decoupled SCA approach).
Payment final status will be applied no later then 15 minutes.
Statuses currently supported:
Status code | Description |
---|---|
RCVD | Received. Initial status for a payment. A certification has been sent to the user’s app. |
ACCP | AcceptedCustomerProfile. User has confirmed the in-app certification and the payment has been successfully initiated. |
ACFC | AcceptedFundsChecked. User has enough funds to perform a payment, and a hold has been applied on the funds. |
ACSC | AcceptedSettlementCompleted. Payment execution process has been successfully completed by N26. This is NOT a confirmation that the beneficiary has received the funds. |
RJCT | Rejected. Payment failed to be initiated or executed. |
The final status of a payment is either ACSC or RJCT.
GET /v1/berlin-group/v1/payments/instant-sepa-credit-transfers/{{paymentId}}/status HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"transactionStatus": "ACSC"
}
GET /v1/berlin-group/v1/payments/instant-sepa-credit-transfers/{{paymentId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
- If "debtorAccount" is selected
HTTP/1.1 200 OK
{
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP"
}
- If "debtorAccount" is not selected
HTTP/1.1 200 OK
{
"debtorAccount": null,
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP"
}
GET /v1/berlin-group/v1/payments/instant-sepa-credit-transfers/{{paymentId}}/authorisations HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"authorisationIds": [
"e93bf74e-9444-4a5e-8524-648d80848126"
]
}
GET /v1/berlin-group/v1/payments/instant-sepa-credit-transfers/{{paymentId}}/authorisations/{{authorisationId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"scaStatus": "finalised"
}
ℹ️ Please note that the debtorAccount parameter is not mandatory; if this parameter is excluded, the periodic payment will be executed from the customer's main account. From October 4th 2023, this behavior remains the same to users with a single IBAN. If this parameter is excluded and a user has multiple IBANs, the user will be asked to select an account for the payment in the N26 app via a push notification. Then, the payment will only be executed once the user has selected an IBAN through the new screen. Until this selection is complete:
- The “debtorAccount” value will be null in the GET payment details endpoints.
- The payment status will remain “RCVD”
- If the selection is not completed within 5 mins, the payment status will change to "RJCT"
POST /v1/berlin-group/v1/periodic-payments/sepa-credit-transfers HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
Content-Type: application/json
{
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"instructedAmount": {
"currency": "EUR",
"amount": "123.50"
},
"creditorAccount":
{
"iban": "DE73500105175658455178"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "Reference text",
"startDate": "2023-01-30",
"endDate": "2024-01-30"
"frequency": "WEEK"
}
Name of parameter | Type | Usage |
---|---|---|
startDate | ISODate | Mandatory |
endDate | ISODate | Optional |
frequency | FrequencyCode | Mandatory |
- Supported frequency codes:
WEEK
,MNTH
,QUTR
,SEMI
,YEAR
. - The
startDate
cannot be in the past, or more than a year in the future.
⚠️ Allowed special characters in creditorName for N26 SEPA CT - ( :,.+?/ )
⚠️ Allowed special characters in remittanceInformationUnstructured for N26 SEPA CT - (:,.+?/-')
HTTP/1.1 201 Created
{
"transactionStatus": "RCVD",
"paymentId": "ead303f5-8404-4b1d-96ae-167d402bbd69",
"_links": {
"self": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69"
},
"status": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69/status"
},
"scaStatus": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69/authorisations/ce172bad-18b8-47cb-9ac5-31b73f320c32"
}
}
}
This endpoint provides statuses on both the creation and deletion of periodic payments. The statuses provide information on the rule itself, and not the subsequent execution of payments. Once the rule has been successfully created, the final status would be "ACCP". Once the rule has been successfully deleted, the final status would be "CANC".
As users are required to provide confirmation for both the creation and deletion of periodic payments, and we are using the decoupled SCA approach, this endpoint is intended to be polled by the TPP.
Statuses currently supported:
Status code | Description |
---|---|
RCVD | Received. Initial status for creating a periodic payment. A certification has been sent to the user’s app. |
ACCP | AcceptedCustomerProfile. User has confirmed the in-app certification to create a periodic payment, and the periodic payment has been successfully created. |
RJCT | Rejected. Status for payment when an in-app certification to create a periodic payment expired or was denied by the user. |
CANC | Cancelled. User has confirmed the in-app certification to delete a periodic payment, and the periodic payment has been successfully deleted. |
⚠️ When a periodic payment is being deleted, the status will only change from ACCP to CANC once the in-app certification to delete the periodic payment has been confirmed by the user. If the in-app certification expires or is denied by the user, the status will remain ACCP.
GET /v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/{{paymentId}}/status HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"transactionStatus": "ACCP"
}
GET /v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/{{paymentId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
- If "debtorAccount" is selected
HTTP/1.1 200 OK
{
"debtorAccount": {
"iban": "DE40100100103307118608"
},
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP",
"startDate": "2023-01-30",
"endDate": "2024-01-30",
"frequency": "WEEK"
}
- If "debtorAccount" is not selected
HTTP/1.1 200 OK
{
"debtorAccount": null,
"debtorName": "Buyer",
"instructedAmount": {
"amount": 0.12,
"currency": "EUR"
},
"creditorAccount": {
"iban": "DE96100110012627266269"
},
"creditorName": "Seller",
"remittanceInformationUnstructured": "reference text",
"transactionStatus": "ACCP",
"startDate": "2023-01-30",
"endDate": "2024-01-30",
"frequency": "WEEK"
}
GET /v1/berlin-group/v1/payments/periodic-payments/sepa-credit-transfers/{{paymentId}}/authorisations HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"authorisationIds": [
"e93bf74e-9444-4a5e-8524-648d80848126"
]
}
GET /v1/berlin-group/v1/payments/periodic-payments/sepa-credit-transfers/{{paymentId}}/authorisations/{{authorisationId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 200 OK
{
"scaStatus": "finalised"
}
DELETE /v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/{{paymentId}} HTTP/1.1
Authorization: bearer {{access_token}}
X-Request-ID: {{Unique UUID}}
HTTP/1.1 202 Accepted
{
"transactionStatus": "ACCP",
"paymentId": "ead303f5-8404-4b1d-96ae-167d402bbd69",
"_links": {
"self": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69"
},
"status": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69/status"
},
"scaStatus": {
"href": "/v1/berlin-group/v1/periodic-payments/sepa-credit-transfers/ead303f5-8404-4b1d-96ae-167d402bbd69/authorisations/ce172bad-18b8-47cb-9ac5-31b73f320c32"
}
}
}
HTTP/1.1 400 Bad Request
{
"title": "Resource Blocked.",
"code": "RESOURCE_BLOCKED",
"detail": "There's a change already pending for this periodic payment."
}
This endpoint is currently not supported.
This endpoint is currently not supported.