DTC PSD2 TPP OpenAPI Documentation

Version: 1.0 · Berlin Group NextGenPSD2 · 2026-05-15

This document describes the DTC Open API endpoints for DTC acting as a Third Party Provider (TPP) under the EU PSD2 directive. The surface implements two of the three TPP sub-roles defined by Berlin Group NextGenPSD2:

The shared Consent lifecycle endpoints and the unauthenticated SCA Callback endpoint are documented alongside.

Table of Contents

  1. Overview
    1. PSD2 Roles & Glossary
    2. End-to-end Flow
    3. Security Standards
  2. Quick Start
    1. Disclaimer
    2. Before Integration
    3. OAuth 2.0 Authentication
    4. Request Signature
    5. Common Headers
  3. Common Conventions
    1. Request / Response Envelope
    2. PSD2 Error Codes
  4. Consent Lifecycle Endpoints
  5. AISP Endpoints
  6. PISP Endpoints
  7. SCA Callback

1. Overview

1.1 PSD2 Roles & Glossary

TermFull NameMeaning in this API
PSUPayment Service UserThe end-user; an account holder at an EU bank (the ASPSP) who authorises DTC to access their accounts or initiate payments on their behalf.
TPPThird Party ProviderThe licensed intermediary. In this surface DTC plays the TPP role.
ASPSPAccount Servicing PSPThe PSU's holding bank / EMI (any EU PSD2-licensed ASPSP). DTC speaks to the ASPSP over Berlin Group APIs on behalf of the PSU.
AISPAccount Information SPTPP sub-role for reading accounts / balances / transactions.
PISPPayment Initiation SPTPP sub-role for initiating payments from the PSU's account at the ASPSP.
SCAStrong Customer AuthenticationTwo-factor authentication performed by PSU on the ASPSP side (password + OTP / biometric).
ConsentThe credential issued by the ASPSP (after PSU SCA) that lets the TPP exercise a scope (AIS / PIS) for a finite window.

1.2 End-to-end Flow

+-----+  HTTPS  +---------------+  HTTPS  +-----------+  PSD2  +-------+
| PSU | <-----> |  DTC Wallet   | <-----> |  DTC TPP  | <----> | ASPSP |
|     |         |  (Client App) |         |  (this)   |        | (Bank)|
+-----+         +---------------+         +-----------+        +-------+
  ^                                            |                  ^
  |             SCA redirect / poll            |                  |
  +--------------------------------------------+------------------+
      (PSU is redirected to the ASPSP for SCA, then back to DTC)

Every business call goes through DTC: the wallet client authenticates to DTC with OAuth 2.0 + HMAC-SHA512 signature; DTC translates that into a Berlin Group request to the ASPSP. The SCA itself is delegated to the ASPSP — the PSU never enters bank credentials in the DTC UI.

1.3 Security Standards

The PSD2 TPP Open API provides four layers of security:


2. Quick Start

2.1 Disclaimer

Important Use of these APIs is subject to dtcpay's review and approval. dtcpay reserves the right, at its sole discretion, to approve, restrict, suspend, or revoke access at any time. PSD2 TPP endpoints additionally require valid EU regulator licensing on the partner side (AISP / PISP).

2.2 Before Integration

  1. Request for account registration on the DTC Wallet platform.
  2. Log in with the username and password issued by the DTC team and go to the API Key Management page.
  3. Click + Create to open the dialog.
  4. Enter the Name and IP Whitelist, then submit.
  5. The API Key, API Secret, and Sign Key will be generated and shown one time only. Save them safely before closing the dialog.
  6. Request the PSD2 auth modules to be granted on your client: PSD2_AIS, PSD2_PIS.

2.3 OAuth 2.0 Authentication

2.3.1 Usage

To use OAuth 2.0, first call Fetch Access Token. Then add the header Authorization: Bearer {access_token} to every PSD2 Open API request.

If the access token expires you can re-fetch it or use the Fetch Access Token by Refresh Token API. We recommend fetching a new access token before the previous one expires.

Note Only one access token can exist at a time per client. We recommend caching the access token on your side, keyed by client id, and refreshing it before expiry.

2.3.2 Fetch Access Token

POST /openapi/auth/oauth2/token

Exchange your API Key + API Secret for an access token using the OAuth 2.0 Client Credentials grant.

Request sample
curl -X "POST" "https://{host}/openapi/auth/oauth2/token" \
     -H 'Content-Type: multipart/form-data; charset=utf-8; boundary=__X_PAW_BOUNDARY__' \
     -F "client_id=xxx" \
     -F "client_secret=xxx" \
     -F "grant_type=client_credentials"
FieldTypeRequiredDescription
client_idStringRThe API Key value generated in step 2.2.
client_secretStringRThe API Secret value generated in step 2.2.
grant_typeStringRFixed value client_credentials.
Response sample
{
    "access_token":  "CkppLkyiqEUKITGdtCZRUZBl",
    "expires_in":    21600,
    "refresh_token": "CpCAhcCRtLU4kPjs54omWW3A",
    "rt_expires_in": 43200,
    "token_type":    "bearer"
}
FieldTypeDescription
access_tokenStringBearer token to put into the Authorization header of subsequent calls. Keep it secret.
expires_inIntegerAccess-token validity in seconds.
refresh_tokenStringRefresh token used to obtain a new access token before the current one expires.
rt_expires_inIntegerRefresh-token validity in seconds.
token_typeStringFixed value bearer.

2.3.3 Fetch Access Token by Refresh Token

POST /openapi/auth/oauth2/token

Exchange a valid refresh_token for a new access token (and a fresh refresh token).

Request sample
curl -X "POST" "https://{host}/openapi/auth/oauth2/token" \
     -H 'Content-Type: multipart/form-data; charset=utf-8; boundary=__X_PAW_BOUNDARY__' \
     -F "refresh_token=CpCAhcCRtLU4kPjs54omWW3A" \
     -F "grant_type=refresh_token"
FieldTypeRequiredDescription
refresh_tokenStringRThe refresh_token obtained in the previous call.
grant_typeStringRFixed value refresh_token.
Response sample
{
    "access_token":  "CE2ZgMjcgcfOvzY0jcS7csYe",
    "expires_in":    21600,
    "refresh_token": "CdugTgyDDa05uURhK6n6GYiW",
    "rt_expires_in": 43200,
    "token_type":    "bearer"
}

2.4 Request Signature

For high-risk PSD2 endpoints (notably POST /openapi/psd2/pisp/v1/payments/{paymentProduct}), an HMAC-SHA512 signature is mandatory in the request headers. You may also apply the same scheme to every PSD2 request — DTC will validate when present.

  1. Components of the signature, in order:
    • HTTP Method — in uppercase (GET / POST / DELETE).
    • Timestamp — millisecond epoch in Singapore timezone. Requests are rejected if the timestamp drifts by more than 2 minutes.
    • URL — request URL without the scheme and host (e.g. /openapi/psd2/pisp/v1/payments/sepa-credit-transfers).
    • Querystring — in-URL parameters without the leading ?, unencoded; empty string when there are none.
    • Request Body — the exact raw body string, including invisible characters. Empty string for GET / DELETE without a body.
  2. Concatenate them with no separator. Example strings to sign:
    GET1636360576641/openapi/psd2/aisp/v1/accounts/details
    POST1636360661729/openapi/psd2/pisp/v1/payments/sepa-credit-transfers{"query":{"endToEndId":"INV-001"}}
  3. Hash the string with HMAC-SHA512 using your Sign Key, then Base64-encode.
    Sign Key   : aR2822Y6XbehWMclnB0Y2NJK
    String     : POST1636360661729/openapi/test{"a":"124"}
    Signature  : MxrYnCm9Q7JOAvOrISf8+T2kuTW1d/w0at8aaPaoiX08VWfun3XPokVlIx1TkHXdcitls09wzfUGtXQZq23xdg==
  4. Append the four DTC custom headers to your request (see §2.5).

2.5 Common Headers

HeaderR/O/CDescription
AuthorizationRBearer token: Bearer {access_token}.
Content-TypeRapplication/json for JSON body endpoints; multipart/form-data for the OAuth token endpoints.
D-REQUEST-IDRClient-generated unique request id (UUID or any opaque string), echoed in audit logs.
D-TIMESTAMPRSame millisecond timestamp used to build the signature.
D-SIGNATURECHMAC-SHA512 signature (Base64). Required on PISP payment initiation; recommended on all PSD2 endpoints.
D-SUB-ACCOUNT-IDCSub-account id under the master account, if you are calling on behalf of one.
Consent-IDCRequired on the GET single-transaction endpoint; passed as a header per Berlin Group convention.
PSU-IP-AddressCThe real PSU's IP — required on AISP endpoints. Do not use the TPP server IP.
PSU-User-AgentOForwarded to the ASPSP for fraud scoring.

3. Common Conventions

3.1 Request / Response Envelope

All PSD2 business endpoints share the standard DTC Open API envelope.

Request envelope (ApiRequest<T>)
{
    "query":  { ...endpoint-specific fields... },
    "page":   { "current": 1, "size": 50 }
}
Response envelope (ApiResponse<T>)
{
    "header": {
        "success": true,
        "errCode": null,
        "errMsg":  null
    },
    "result": { ...endpoint-specific fields... }
}
Top-level header.successboolean — true only when the call is fully successful.
Top-level header.errCodestring — a stable code such as PSD2_CONSENT_EXPIRED on failure; null on success.
Top-level header.errMsgstring — i18n-translated human message.
resultendpoint-specific object on success.

3.2 PSD2 Error Codes

CodeHTTPMeaning
PSD2_CONSENT_NOT_FOUND404Consent does not exist or has been revoked.
PSD2_CONSENT_EXPIRED409Consent has expired — PSU must grant a fresh consent via SCA.
PSD2_SCA_REQUIRED401SCA has not been completed for this consent / payment.
PSD2_ASPSP_UNAVAILABLE503Upstream ASPSP is temporarily unreachable.
PSD2_INVALID_IBAN400IBAN does not pass format checks.
PSD2_AMOUNT_LIMIT_EXCEEDED400Amount exceeds the per-transaction limit configured for the ASPSP.
PSD2_TOO_MANY_REQUESTS429Too many requests — client has exceeded the per-consent / per-client rate limit. Retry after the window resets.
PSD2_SIGNATURE_INVALID500DTC-side self-check found an inconsistent signature in egress payload.
PSD2_PAYMENT_NOT_CANCELLABLE409Payment is in a terminal/settling state and cannot be cancelled.
PSD2_UNSUPPORTED_PAYMENT_PRODUCT400Unknown {paymentProduct} in the URL path.

Base path: /openapi/psd2/v1/consent · Required scope: PSD2_AIS

These endpoints manage the AIS consent lifecycle. A PIS consent is created implicitly when initiating a payment — there is no separate "create PIS consent" endpoint.

POST /openapi/psd2/v1/consent PSD2_AIS

Create an AIS (Account Information) consent. The response includes an scaRedirectUri that the wallet client must open in a browser/WebView so the PSU can complete SCA at the ASPSP. Until the callback fires the consent is in state RECEIVED and cannot be used.

Request body
{
    "query": {
        "scope":              "AIS",
        "aspspId":            "SOMEBANK_DE",
        "psuId":              "psu-001@example.com",
        "ibans":              ["DE00000000000000000001"],
        "validUntilDays":     90,
        "recurringIndicator": true,
        "frequencyPerDay":    4,
        "tppRedirectUri":     "https://wallet.dtcpay.com/openapi/psd2/sca/callback"
    }
}
FieldTypeRequiredDescription
scopeEnumRMust be AIS on this endpoint.
aspspIdStringRASPSP identifier issued by DTC for each supported bank. The full list is available in your developer console.
psuIdStringRThe PSU id at the ASPSP — typically the email or username the PSU uses for online banking.
ibansArray<String>RIBANs scoped by this consent.
validUntilDaysIntegerOValidity in days. Berlin Group RTS caps AIS at 90. Default 90.
recurringIndicatorBooleanOtrue for ongoing access, false for one-shot. Default true.
frequencyPerDayIntegerOCap on requests/day (≥1). Berlin Group default 4.
tppRedirectUriStringRThe URL the ASPSP will redirect the PSU back to after SCA.
Response sample
{
    "header": { "success": true },
    "result": {
        "consentId":          "CSNT_A1B2C3D4",
        "scope":              "AIS",
        "aspspId":            "SOMEBANK_DE",
        "consentStatus":      "RECEIVED",
        "scaApproach":        "REDIRECT",
        "scaRedirectUri":     "https://psd2.somebank.example.com/sca?consentId=CSNT_A1B2C3D4&state=...",
        "ibans":              ["DE00000000000000000001"],
        "validUntil":         "2026-08-13T10:32:11",
        "recurringIndicator": true,
        "frequencyPerDay":    4
    }
}
GET /openapi/psd2/v1/consent/{consentId} PSD2_AIS

Return the full metadata snapshot for an existing consent. Cross-tenant access is rejected — only the client that originally created the consent can describe it.

Path parameters
NameTypeDescription
consentIdStringThe consent id returned by the creation call.
Response sample
{
    "header": { "success": true },
    "result": {
        "consentId":          "CSNT_A1B2C3D4",
        "scope":              "AIS",
        "aspspId":            "SOMEBANK_DE",
        "consentStatus":      "VALID",
        "scaApproach":        "REDIRECT",
        "scaRedirectUri":     "https://psd2.somebank.example.com/sca?consentId=...",
        "ibans":              ["DE00000000000000000001"],
        "validUntil":         "2026-08-13T10:32:11",
        "recurringIndicator": true,
        "frequencyPerDay":    4
    }
}
GET /openapi/psd2/v1/consent/{consentId}/status PSD2_AIS

Lightweight status check. Use this for polling instead of the full describe endpoint.

Response sample
{
    "header": { "success": true },
    "result": {
        "consentStatus": "VALID"
    }
}

Possible consentStatus values: RECEIVED, VALID, EXPIRED, REVOKED_BY_PSU, REJECTED, TERMINATED_BY_TPP.

DELETE /openapi/psd2/v1/consent/{consentId} PSD2_AIS

Revoke the consent. Subsequent business calls referencing it will fail with PSD2_CONSENT_NOT_FOUND.

Response sample
{
    "header": { "success": true },
    "result": {
        "consentStatus": "REVOKED_BY_PSU"
    }
}

5. AISP Endpoints

Base path: /openapi/psd2/aisp/v1 · Required scope: PSD2_AIS

All AISP endpoints require a VALID AIS consent. Refer to §4 for how to obtain one.

POST /openapi/psd2/aisp/v1/accounts/list PSD2_AIS

List the PSU accounts authorised by the consent. When withBalance=true the ASPSP may embed the latest balance object in each account; otherwise call POST /accounts/details or POST /accounts/balances to retrieve balances separately.

Request body
{
    "query": {
        "consentId":   "CSNT_K8M3X9P2",
        "withBalance": false
    }
}
FieldTypeRequiredDescription
consentIdStringRA consent in state VALID.
withBalanceBooleanOBerlin Group flag; default false.
Response sample
{
    "header": { "success": true },
    "result": {
        "accounts": [
            {
                "resourceId":      "ACC_001",
                "iban":            "DE00000000000000000001",
                "bic":             "SBNKDEFFXXX",
                "currency":        "EUR",
                "name":            "Sample User Main",
                "product":         "Current Account",
                "cashAccountType": "CACC",
                "status":          "enabled"
            },
            {
                "resourceId":      "ACC_002",
                "iban":            "DE00000000000000000002",
                "bic":             "SBNKDEFFXXX",
                "currency":        "EUR",
                "name":            "Sample User Savings",
                "product":         "Savings Account",
                "cashAccountType": "SVGS",
                "status":          "enabled"
            }
        ]
    }
}
POST /openapi/psd2/aisp/v1/accounts/details PSD2_AIS

Return a single account record. When withBalance=true the response also embeds the latest balance objects (closingBooked, expected, interimAvailable).

Request body
{
    "query": {
        "consentId":   "CSNT_K8M3X9P2",
        "accountId":   "ACC_001",
        "withBalance": true
    }
}
FieldTypeRequiredDescription
consentIdStringRA consent in state VALID.
accountIdStringRresourceId returned by /accounts/list.
withBalanceBooleanODefault true.
Response sample
{
    "header": { "success": true },
    "result": {
        "account": {
            "resourceId":      "ACC_001",
            "iban":            "DE00000000000000000001",
            "bic":             "SBNKDEFFXXX",
            "currency":        "EUR",
            "name":            "Sample User Main",
            "product":         "Current Account",
            "cashAccountType": "CACC",
            "status":          "enabled"
        },
        "balances": [
            { "balanceType": "closingBooked",    "amount": "1024.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" },
            { "balanceType": "expected",         "amount": "1010.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" },
            { "balanceType": "interimAvailable", "amount": "1024.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" }
        ]
    }
}
POST /openapi/psd2/aisp/v1/accounts/balances PSD2_AIS

Return the latest balance objects for a single account.

Request body
{
    "query": {
        "consentId": "CSNT_K8M3X9P2",
        "accountId": "ACC_001"
    }
}
Response sample
{
    "header": { "success": true },
    "result": {
        "accountId": "ACC_001",
        "balances": [
            { "balanceType": "closingBooked",    "amount": "1024.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" },
            { "balanceType": "expected",         "amount": "1010.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" },
            { "balanceType": "interimAvailable", "amount": "1024.50", "currency": "EUR", "referenceDate": "2026-05-15", "lastChangeDateTime": "2026-05-15T10:32:11" }
        ]
    }
}
Balance TypeMeaning
closingBookedMost recent booked balance at the start of the current business day.
openingBookedBooked balance at the start of the previous business day.
expectedBooked balance plus pending debits (the figure most apps display as "available").
authorisedInternal credit-limit indicator.
interimAvailableReal-time available balance.
forwardAvailableFuture-dated available balance.
POST /openapi/psd2/aisp/v1/accounts/transactions PSD2_AIS

Paginated transactions for a single account, filtered by booking status and date range.

Request body
{
    "query": {
        "consentId":     "CSNT_K8M3X9P2",
        "accountId":     "ACC_001",
        "bookingStatus": "BOOKED",
        "dateFrom":      "2026-04-01",
        "dateTo":        "2026-05-15",
        "pageIndex":     1,
        "pageSize":      50
    }
}
FieldTypeRequiredDescription
consentIdStringRA consent in state VALID.
accountIdStringRresourceId returned by /accounts/list.
bookingStatusEnumOOne of BOOKED (default), PENDING, BOTH, INFORMATION.
dateFromDateOInclusive ISO date (yyyy-MM-dd).
dateToDateOInclusive ISO date.
pageIndexIntegerO1-based; default 1.
pageSizeIntegerO1..200; default 50.
Response sample
{
    "header": { "success": true },
    "result": {
        "accountId": "ACC_001",
        "booked": [
            {
                "transactionId":  "TXN_20260514_001",
                "endToEndId":     "INV-2026-04-321",
                "bookingStatus":  "BOOKED",
                "bookingDate":    "2026-05-14",
                "valueDate":      "2026-05-14",
                "amount":         "-150.00",
                "currency":       "EUR",
                "creditorIban":   "DE00000000000000000002",
                "creditorName":   "Sample Creditor Co",
                "remittanceInfo": "Invoice 2026-04-321"
            },
            {
                "transactionId":  "TXN_20260512_002",
                "endToEndId":     "SAL-2026-05",
                "bookingStatus":  "BOOKED",
                "bookingDate":    "2026-05-12",
                "valueDate":      "2026-05-12",
                "amount":         "3200.00",
                "currency":       "EUR",
                "debtorIban":     "DE00000000000000000003",
                "remittanceInfo": "Salary May 2026"
            }
        ],
        "pending":   [],
        "total":     2,
        "pageIndex": 1,
        "pageSize":  50
    }
}
GET /openapi/psd2/aisp/v1/accounts/{accountId}/transactions/{transactionId} PSD2_AIS

Return a single transaction by id. The consent id is passed via the Consent-ID header per Berlin Group convention rather than the request body.

Request sample
curl -X GET "https://{host}/openapi/psd2/aisp/v1/accounts/ACC_001/transactions/TXN_20260514_001" \
     -H "Authorization: Bearer {access_token}" \
     -H "Consent-ID: CSNT_K8M3X9P2" \
     -H "D-REQUEST-ID: 9b7c4d2a-..."
Response sample
{
    "header": { "success": true },
    "result": {
        "transactionId":  "TXN_20260514_001",
        "endToEndId":     "INV-2026-04-321",
        "bookingStatus":  "BOOKED",
        "bookingDate":    "2026-05-14",
        "valueDate":      "2026-05-14",
        "amount":         "-150.00",
        "currency":       "EUR",
        "creditorIban":   "DE00000000000000000002",
        "creditorName":   "Sample Creditor Co",
        "remittanceInfo": "Invoice 2026-04-321"
    }
}

6. PISP Endpoints

Base path: /openapi/psd2/pisp/v1 · Required scope: PSD2_PIS

6.1 Supported Payment Products

The {paymentProduct} URL path segment selects the payment scheme. Phase 0 supports:

Product codeDescriptionRequired extra fields
sepa-credit-transfersStandard SEPA Credit Transfer (T+1).
instant-sepa-credit-transfersSEPA SCT Inst (~10 s); ASPSP must be SCT-Inst capable.
target-2-paymentsHigh-value TARGET2.creditorAgentBic
cross-border-credit-transfersCross-border (non-SEPA) credit transfer.creditorAgentBic
periodic-paymentsStanding order; recurring instruction.startDate, frequency
POST /openapi/psd2/pisp/v1/payments/{paymentProduct} PSD2_PIS

Initiate a payment of the given product. The PIS consent is created implicitly with the payment; the response includes an scaRedirectUri the wallet must open so the PSU can authorise the payment. Until the SCA callback fires, the payment stays in status RCVD.

This endpoint is idempotency-protected: supply referenceNo for client-controlled idempotency. If referenceNo is omitted, DTC derives a short-window dedup key from aspspId + debtorIban + creditorIban + amount + endToEndId so accidental duplicate clicks are still rejected.

Signature required The HMAC-SHA512 D-SIGNATURE header is mandatory on this endpoint.
Request body
{
    "query": {
        "referenceNo":     "INV-2026-04-321",
        "aspspId":         "SOMEBANK_DE",
        "psuId":           "psu-001@example.com",
        "debtorIban":      "DE00000000000000000001",
        "creditorIban":    "DE00000000000000000002",
        "creditorName":    "Sample Creditor Co",
        "currency":        "EUR",
        "amount":          "150.00",
        "remittanceInfo":  "Invoice 2026-04-321",
        "tppRedirectUri":  "https://wallet.dtcpay.com/openapi/psd2/sca/callback",
        "endToEndId":      "INV-2026-04-321"
    }
}
FieldTypeRequiredDescription
referenceNoStringOIdempotency anchor for the anti-duplicate aspect. If omitted, DTC generates one and includes it in the response context.
aspspIdStringRASPSP identifier.
psuIdStringRPSU id at the ASPSP.
debtorIbanStringRPayer IBAN — must be an account the PSU holds at the ASPSP.
creditorIbanStringRBeneficiary IBAN.
creditorNameStringRBeneficiary name (max 140 chars; appears on the ASPSP statement).
creditorAgentBicStringCRequired for target-2-payments and cross-border-credit-transfers.
currencyStringRISO 4217, 3 chars (e.g. EUR).
amountDecimal (string)RPositive decimal. Always send as a JSON string to preserve precision (e.g. "150.00").
remittanceInfoStringOFree-form, max 140 chars.
tppRedirectUriStringRWhere the ASPSP returns the PSU after SCA.
endToEndIdStringRReference visible end-to-end on the SEPA rail; serves as a secondary idempotency anchor.
startDateDateCRequired for periodic-payments.
endDateDateOOptional end date for periodic-payments.
frequencyStringCRequired for periodic-payments. Berlin Group values: Monthly, Weekly, etc.
Response sample
{
    "header": { "success": true },
    "result": {
        "paymentId":         "PAY_20260515_A1B2C3",
        "transactionStatus": "RCVD",
        "scaApproach":       "REDIRECT",
        "scaRedirectUri":    "https://psd2.somebank.example.com/sca?paymentId=PAY_20260515_A1B2C3&state=...",
        "psuMessage":        "Please complete authentication in your banking app."
    }
}
GET /openapi/psd2/pisp/v1/payments/{paymentProduct}/{paymentId} PSD2_PIS

Return the full payment record. Use this when you need debtor/creditor details or the periodic schedule; for lightweight status polling prefer /status.

Request sample
curl -X GET "https://{host}/openapi/psd2/pisp/v1/payments/sepa-credit-transfers/PAY_20260515_A1B2C3" \
     -H "Authorization: Bearer {access_token}" \
     -H "D-REQUEST-ID: 9b7c4d2a-..."
Response sample
{
    "header": { "success": true },
    "result": {
        "paymentId":         "PAY_20260515_A1B2C3",
        "paymentProduct":    "sepa-credit-transfers",
        "transactionStatus": "ACTC",
        "debtorIban":        "DE00000000000000000001",
        "creditorIban":      "DE00000000000000000002",
        "creditorName":      "Sample Creditor Co",
        "creditorAgentBic":  null,
        "currency":          "EUR",
        "amount":            "150.00",
        "remittanceInfo":    "Invoice 2026-04-321",
        "endToEndId":        "INV-2026-04-321",
        "startDate":         null,
        "endDate":           null,
        "frequency":         null,
        "createdAt":         "2026-05-15T10:32:11"
    }
}
GET /openapi/psd2/pisp/v1/payments/{paymentProduct}/{paymentId}/status PSD2_PIS

Return the latest transactionStatus only. Cheap and idempotent — safe to poll on a short interval. The status reflects the ASPSP's current progression through the payment lifecycle.

Response sample
{
    "header": { "success": true },
    "result": {
        "paymentId":                "PAY_20260515_A1B2C3",
        "transactionStatus":        "ACSC",
        "rejectReasonCode":         null,
        "rejectReasonMessage":      null,
        "lastStatusChangeDateTime": "2026-05-15T10:34:22"
    }
}
StatusMeaning
RCVDReceived by ASPSP, awaiting SCA.
PDNGPending — technical processing in progress.
ACTCAccepted Technical Validation.
ACCPAccepted Customer Profile checks.
ACSPAccepted Settlement In Process.
ACSCAccepted Settlement Completed (terminal success).
ACWCAccepted With Change (ASPSP adjusted value date etc.).
ACWPAccepted Without Posting yet.
RJCTRejected (terminal). See rejectReasonCode.
CANCCancelled (terminal).
DELETE /openapi/psd2/pisp/v1/payments/{paymentProduct}/{paymentId} PSD2_PIS

Cancel a payment. Allowed only while the payment has not entered settlement: statuses ACSP and ACSC return PSD2_PAYMENT_NOT_CANCELLABLE.

Response sample — success
{
    "header": { "success": true },
    "result": {
        "paymentId":                "PAY_20260515_A1B2C3",
        "transactionStatus":        "CANC",
        "rejectReasonCode":         null,
        "rejectReasonMessage":      null,
        "lastStatusChangeDateTime": "2026-05-15T10:36:00"
    }
}
Response sample — not cancellable
{
    "header": {
        "success": false,
        "errCode": "PSD2_PAYMENT_NOT_CANCELLABLE",
        "errMsg":  "Payment is settled or being settled and cannot be cancelled"
    }
}

7. SCA Callback

Base path: /openapi/psd2/callback

Unauthenticated The SCA callback is an unauthenticated public endpoint — the ASPSP redirects the PSU's browser to it directly. CSRF protection relies on the one-time state value embedded in the original SCA redirect URI; calls with a missing or mismatched state are rejected.
GET /openapi/psd2/callback/sca

Receives the ASPSP redirect after PSU SCA completes, validates the state against the consent or payment record stored by DTC, and flips the linked consent / payment to VALID. The wallet front-end can then resume the business flow by polling the consent / payment status endpoint, or by following the redirectTo URL returned in the JSON response.

Query parameters
NameTypeRequiredDescription
typeEnumRPSD2 sub-role being authorised: AIS, PIS, or PIIS. Used for audit logging and to route the state-machine transition.
aspspIdStringRASPSP identifier — echoes the value used when creating the consent / payment.
idStringRThe consent id (AIS / PIIS) or payment id (PIS) being authorised.
codeStringOOAuth-style authorisation code returned by the ASPSP. Forwarded to the ASPSP token-exchange step; not validated by DTC.
stateStringROne-time CSRF state set by DTC when issuing the original SCA URL. Must match exactly.
Request sample
GET /openapi/psd2/callback/sca?type=AIS&aspspId=SOMEBANK_DE&id=CSNT_A1B2C3D4&code=AUTH_CODE_xxx&state=2f4a8c9...

# Note: no Authorization header — public endpoint, ASPSP browser redirect.
Response sample — success
{
    "success":       true,
    "consentId":     "CSNT_A1B2C3D4",
    "consentStatus": "VALID",
    "redirectTo":    "https://wallet.dtcpay.com/openapi/psd2/sca/callback"
}
Response sample — state mismatch / consent not found
{
    "success": false,
    "message": "consent not found or state mismatch",
    "id":      "CSNT_A1B2C3D4"
}
Wallet UX The wallet front-end should not parse redirectTo blindly. Validate that the host matches your registered tppRedirectUri origin before following.