# Payout OpenBanking PSD2 API

PSD2 API for third-party providers (TPPs). Read the accounts and transactions a user granted to
you, confirm funds and initiate payments from Payout accounts. The API follows the
[SBA standard](https://sbaonline.docs.apiary.io).

The API accepts and returns JSON only. Send request bodies with `Content-Type: application/json`.

**Amounts** in responses are decimal strings, such as `"3055.8500"`. In requests,
`instructedAmount.value` is a JSON number and the balance check `amount.amount` an integer or a
decimal string.

**Timestamps** in responses are RFC 3339 in UTC with microseconds. `statusDatetime` ends in `Z`,
the others in `+00:00`.

Every response has these headers:

- `response-id` – a unique UUID of the response
- `correlation-id` – your `Correlation-ID` request header, or a new UUID when you did not send one
- `process-id` – your `Process-ID` request header, or a new UUID when you did not send one

## Environments

- Sandbox, for testing only: `https://sandbox.payout.one`
- Production: `https://app.payout.one`

## Authentication

Every endpoint except [Enrol](#enrol) needs an OAuth 2.0 access token (JWT) issued by
[PayoutID](https://developers.payout.tech/api/payout-id.html) to your TPP client. Send it in the `Authorization` header:

```http
Authorization: Bearer <access_token>
```

Get the tokens from PayoutID in the same environment as the API:

| Environment | Authorization URL                        | Token URL                            |
|-------------|------------------------------------------|--------------------------------------|
| Sandbox     | https://id-sa.payout.one/oauth/authorize | https://id-sa.payout.one/oauth/token |
| Production  | https://id.payout.one/oauth/authorize    | https://id.payout.one/oauth/token    |

Their parameters are described at [Authorize user](https://developers.payout.tech/api/payout-id.html#authorization_redirect_for_user)
and [Get access token](https://developers.payout.tech/api/payout-id.html#endpoint_to_retrieve_authorization_token).

The token must be issued to a TPP client registered with Payout and carry the scope the endpoint
requires:

| Scope | Grant | Endpoints |
| ----- | ----- | --------- |
| `AISP` | `authorization_code`, on behalf of the user | [List accounts](#list_accounts), [Retrieve account details and balances](#account_info), [List transactions](#list_transactions) |
| `PIISP` | `authorization_code`, on behalf of the user | [Check balance](#balance_check) |
| `PISP` | `client_credentials` | [Create payment order](#standard_sba_payment), [Retrieve payment order status](#order_status) |
| `PISPSUBMIT` | `authorization_code` for one payment order, see [Payment flow](#payment-flow) | [Submit payment order](#submit_payment) |

Request `AISP`, `PIISP` and `PISPSUBMIT` as the only scope of an authorization request: PayoutID
refuses to combine them with other scopes.

Account information endpoints return data only for the accounts the user granted to your TPP.

### Payment flow

1. Get a `PISP` token with the `client_credentials` grant at the token URL.
2. Create the payment order with [Create payment order](#standard_sba_payment). Keep its `orderId`.
3. Send the user to the authorization URL with `scope=PISPSUBMIT` and the `orderId` as `resource`,
   in sandbox:

   ```http
   GET https://id-sa.payout.one/oauth/authorize?response_type=code&client_id=<client_id>&redirect_uri=<redirect_uri>&scope=PISPSUBMIT&resource=<orderId>
   ```

   Exchange the `code` PayoutID returns to `redirect_uri` for a `PISPSUBMIT` token at the token URL.
4. Submit the payment order with [Submit payment order](#submit_payment) and the `PISPSUBMIT` token.


## Errors

Authentication failures return `401`:

```json
{
  "status": 401,
  "reason": "Unauthorized"
}
```

You get it when the token:

- is missing, invalid or expired
- lacks the required scope or user
- was issued to an unknown client

Other errors use this shape:

```json
{
  "errors": {
    "message": "Resource not found"
  }
}
```

| Status | Message | When |
| ------ | ------- | ---- |
| 400 | `Bad request` | The body is not valid JSON, a required attribute is missing, or the payment order ID is not a UUID |
| 404 | `Resource not found` | The account or payment order does not exist, or the account was not granted to your TPP |
| 406 | `Request is not acceptable` | The `Accept` header does not allow JSON |
| 500 | `Internal server error` | Unexpected error |

[Enrol](#enrol) validation errors and refused [payment order submissions](#submit_payment) also
return `400`, with the bodies described at those endpoints.


## Enrol

`POST https://sandbox.payout.one/api/psd2/v1/enrol`

Registers your TPP as a new client and returns its client credentials. This endpoint does not
need an access token.

Payout is notified of the enrolment. The client stays inactive until Payout activates it.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Request body

- `licenseNumber` — string (required) · PSD2 license number of your TPP. Each license number can be enrolled only once. · e.g. `12345`
- `clientName` — string (required) · Client name of your TPP · e.g. `Example TPP, s.r.o.`
- `logoUri` — string · URL of a publicly accessible logo of your TPP · e.g. `https://tpp.example.com/logo.png`
- `scopes` — string[] (required) · Scopes your TPP will request. See [Authentication](#authentication) for the grant and endpoints of each. · e.g. `["AISP"]`
  - `AISP` — Read granted accounts, their balances and transactions
  - `PISP` — Create payment orders and read their status
  - `PISPSUBMIT` — Submit a payment order
  - `PIISP` — Check whether an account has enough balance
  - `profile` — Default OAuth scope, not required by any endpoint of this API
- `contacts` — string<email>[] (required) · E-mail addresses to contact your TPP. At least one is required. · e.g. `["test@example.com"]`
- `redirectUris` — string<uri>[] (required) · Redirect URIs your TPP will use. Each must be an absolute HTTPS URL without a fragment. · e.g. `["https://tpp.example.com/oauth/callback"]`
- `certificate` — string (required) · Base64-encoded PSD2 certificate of your TPP · e.g. `MIIDdzCCAl+gAwIBAgIURXhhbXBsZVBTRDJDZXJ0aWZpY2F0ZQ==`

### Response 201

- `licenseNumber` — string · PSD2 license number, as sent · e.g. `12345`
- `clientId` — string · Client ID generated for your TPP, 64 hexadecimal characters · e.g. `a1b2c3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8f90`
- `clientSecret` — string · Client secret generated for your TPP. It is returned only in this response and cannot be retrieved again, so store it securely. · e.g. `0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0`
- `cleintName` — string · Client name of your TPP, as sent in `clientName`. The key is spelled `cleintName`. · e.g. `Example TPP, s.r.o.`
- `logoUri` — string | null · Logo URL, as sent. `null` when you sent none · e.g. `https://tpp.example.com/logo.png`
- `scopes` — string[] · Scopes, as sent · e.g. `["AISP"]`
- `contacts` — string[] · Contact e-mail addresses, as sent · e.g. `["test@example.com"]`
- `redirectsUris` — string[] · Redirect URIs, as sent in `redirectUris`. The key is spelled `redirectsUris`. · e.g. `["https://tpp.example.com/oauth/callback"]`

### Responses

- `201` — Client enrolled. It stays inactive until Payout activates it.
- `400` — Validation failed (for example, the license number is already enrolled), or a required attribute is missing.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/enrol' \
  -H "Content-Type: application/json" \
  -d '{
       "licenseNumber": "12345",
       "clientName": "Example TPP, s.r.o.",
       "logoUri": "https://tpp.example.com/logo.png",
       "certificate": "MIIDdzCCAl+gAwIBAgIURXhhbXBsZVBTRDJDZXJ0aWZpY2F0ZQ==",
       "scopes": [
         "AISP"
       ],
       "contacts": [
         "test@example.com"
       ],
       "redirectUris": [
         "https://tpp.example.com/oauth/callback"
       ]
     }'
```


## List accounts

`GET https://sandbox.payout.one/api/psd2/v1/accounts`

Lists the user's accounts that the user granted to your TPP.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Response 200

- `creationDateTime` — string<date-time> · When the list was created, in RFC 3339 format · e.g. `2026-10-06T08:15:30.123456+00:00`
- `accounts` — object[]
  - `identification` — object
    - `identifier` — string · Account identifier. Send it as `identifier` to the other endpoints. · e.g. `Q7v_K2mNp4Xs`
  - `name` — string · Account name · e.g. `Example Shop, s.r.o.`
  - `productName` — string · Product name, always `Payout Account` · e.g. `Payout Account`
  - `type` — string · ISO 20022 cash account type code, always `CACC` · e.g. `CACC`
  - `baseCurrency` — string · ISO 4217 base currency of the account, always `EUR` · e.g. `EUR`
  - `servicer` — object · Institution that services the account
    - `financialInstitutionIdentification` — string · Name of the institution · e.g. `Payout, s.r.o.`
  - `consent` — string[] · Scopes of the access token used for the request · e.g. `["AISP"]`

### Responses

- `200` — Success
- `401` — Missing or invalid bearer token, or the token lacks the required scope.

### Example

```bash
curl -X GET 'https://sandbox.payout.one/api/psd2/v1/accounts' \
  -H "Authorization: Bearer $TOKEN"
```


## Retrieve account details and balances

`POST https://sandbox.payout.one/api/psd2/v1/accounts/information`

Retrieves the details of one granted account and its balance in each currency.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Request body

- `identifier` — string (required) · Account identifier (`identification.identifier` from [List accounts](#list_accounts)) · e.g. `Q7v_K2mNp4Xs`

### Response 200

- `account` — object
  - `name` — string · Account name · e.g. `Example Shop, s.r.o.`
  - `productName` — string · Product name, always `Payout Account` · e.g. `Payout Account`
  - `baseCurrency` — string · ISO 4217 base currency of the account, always `EUR` · e.g. `EUR`
  - `type` — string · ISO 20022 cash account type code, always `CACC` · e.g. `CACC`
- `balances` — object[] · One balance per currency
  - `name` — string · Account name · e.g. `Example Shop, s.r.o.`
  - `typeCodeOrProprietary` — string · Balance type, always `ITAV` · e.g. `ITAV`
  - `amount` — object · Amount of money with its currency
    - `value` — string · Decimal amount, serialized as a string · e.g. `3055.8500`
    - `currency` — string · ISO 4217 currency code · e.g. `EUR`
  - `creditDebitIndicator` — string · Whether incoming or outgoing funds prevail · e.g. `CRDT`
    - `CRDT` — Incoming funds exceed outgoing funds
    - `DBIT` — Outgoing funds equal or exceed incoming funds
  - `dateTime` — string<date-time> · When the balance was read, in RFC 3339 format · e.g. `2026-10-06T08:15:30.123456+00:00`

### Responses

- `200` — Success
- `400` — The body is not valid JSON or has no `identifier`.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.
- `404` — None of the accounts the user granted to your TPP has this `identifier`.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/accounts/information' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
       "identifier": "Q7v_K2mNp4Xs"
     }'
```


## List transactions

`POST https://sandbox.payout.one/api/psd2/v1/accounts/transactions`

Lists the transactions of the accounts the user granted to your TPP, newest first. All filters
are optional; without a body you get the first page across all granted accounts.

> [!WARNING]
> In this API, `status` `BOOKED` means **pending** and `INFO` means **executed**, the reverse
> of their usual ISO 20022 meaning. This applies to the `status` filter and to the `status` of
> each transaction.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Request body

- `identifier` — string · Return only transactions of this account (`identification.identifier` from [List accounts](#list_accounts)) · e.g. `Q7v_K2mNp4Xs`
- `dateFrom` — string<date> · Return transactions created on or after this date, `YYYY-MM-DD`. Other formats are ignored. · e.g. `2026-09-01`
- `dateTo` — string<date> · Return transactions created before this date, `YYYY-MM-DD`. Other formats are ignored. · e.g. `2026-09-30`
- `status` — string · Return only transactions with this `status`. Note that `BOOKED` means pending and `INFO` means executed, the reverse of their usual ISO 20022 meaning. · e.g. `BOOKED`
  - `BOOKED` — Only pending transactions
  - `INFO` — Only executed transactions
- `pageSize` — integer · Number of transactions per page · e.g. `20`
- `page` — integer · Page number, starting at 0 · e.g. `4`

### Response 200

- `pageCount` — integer · Total number of pages for the given filters · e.g. `3`
- `transactions` — object[]
  - `amount` — object · Amount of money with its currency
    - `value` — string · Decimal amount, serialized as a string · e.g. `3055.8500`
    - `currency` — string · ISO 4217 currency code · e.g. `EUR`
  - `creditDebitIndicator` — string · Direction of the transaction · e.g. `CRDT`
    - `CRDT` — Credit, money into the account
    - `DBIT` — Debit, money out of the account
  - `reversalIndicator` — boolean · Whether the transaction reverses an earlier transaction · e.g. `false`
  - `status` — string · Execution state of the transaction. Note that `INFO` means executed and `BOOKED` means pending, the reverse of their usual ISO 20022 meaning. · e.g. `INFO`
    - `INFO` — Executed
    - `BOOKED` — Pending, not executed yet
  - `bookingDate` — string<date> · Date the transaction was created · e.g. `2026-09-15`
  - `valueDate` — string<date> · Same as `bookingDate` · e.g. `2026-09-15`
  - `bankTransactionCode` — string · Transaction type code · e.g. `PM`
    - `PM` — Any other transaction
    - `GHC` — Account management, support, monthly minimum or receipts fee
  - `transactionDetails` — object
    - `references` — object · References that identify the transaction
      - `accountServicerReference` — string · Payout's transaction ID · e.g. `184512`
      - `endToEndIdentification` — string | null · Transaction reference in the form `/VS{variable symbol}/SS/KS`, or null when the transaction has none · e.g. `/VS20260915/SS/KS`
    - `relatedParties` — object · The account is the debtor of a debit and the creditor of a credit. When the transaction has no counterparty, the other party is `Payout, s.r.o.` with identification `PAYOUT`.
      - `debtor` — object
        - `name` — string · Name of the party · e.g. `Example Customer`
      - `debtorAccount` — object
        - `identification` — string · Account `identifier` for the account, IBAN for the counterparty · e.g. `CZ6508000000192000145399`
      - `creditor` — object
        - `name` — string · Name of the party · e.g. `Example Shop, s.r.o.`
      - `creditorAccount` — object
        - `identification` — string · Account `identifier` for the account, IBAN for the counterparty · e.g. `Q7v_K2mNp4Xs`
    - `relatedDates` — object
      - `acceptanceDateTime` — string<date> · Same as `bookingDate` · e.g. `2026-09-15`

### Responses

- `200` — Success
- `400` — The body is not valid JSON.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/accounts/transactions' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
       "page": 2,
       "identifier": "Q7v_K2mNp4Xs"
     }'
```


## Create payment order

`POST https://sandbox.payout.one/api/psd2/v1/payments/standard/sba`

Creates a payment order, a standard SBA payment from a Payout account to the creditor's IBAN.
The payment order starts in status `PDNG` and is executed only after you
[submit it](#submit_payment).

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Request body

- `instructionIdentification` — string (required) · Your identification of the instruction · e.g. `aff52ratg5ageh53`
- `debtor` — object (required)
  - `identifier` — string (required) · Account to pay from (`identification.identifier` from [List accounts](#list_accounts)) · e.g. `Q7v_K2mNp4Xs`
- `creditor` — object (required)
  - `name` — string (required) · Full name or company name of the creditor · e.g. `Example Supplier, s.r.o.`
  - `iban` — string (required) · IBAN of the creditor · e.g. `SK3112000000198742637541`
  - `email` — string (required) · E-mail address of the creditor · e.g. `billing@example.com`
- `instructedAmount` — object (required)
  - `value` — number (required) · Amount with two decimals · e.g. `12.5`
  - `currency` — string (required) · ISO 4217 currency code · e.g. `EUR`
- `endToEndIdentification` — string · Your transaction reference. In the form `/VS{variable symbol}/SS{specific symbol}/KS{constant symbol}`, the variable symbol becomes the payment reference and must be numeric with at most 10 digits. This value, or `instructionIdentification` when it is empty, must not repeat across payments from the same account. Otherwise the submission is refused. · e.g. `/VS20261006/SS/KS`
- `remittanceInformation` — string · Description that appears on the creditor's statement, with only letters without accents, digits, spaces and `/-?:().,'+`. Creating the payment order accepts up to 255 characters, but its submission is refused when the text is longer than 140 characters or has other characters. · e.g. `Invoice 2026-104`

### Response 201

- `orderId` — string<uuid> · Payment order ID · e.g. `3b0f6c2e-8d41-4a7b-9c55-1e2f3a4b5c6d`
- `status` — string · Status of the payment order · e.g. `PDNG`
  - `PDNG` — Created, not submitted yet
  - `ACSC` — Submitted, payment created
  - `RJCT` — Submission refused (returned only by [Submit payment order](#submit_payment))
- `statusDatetime` — string<date-time> · When the status was read · e.g. `2026-10-06T08:15:30.123456Z`

### Responses

- `201` — Payment order created
- `400` — The body is not valid JSON or a required attribute is missing.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.
- `404` — No account has the identifier given in `debtor.identifier`.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/payments/standard/sba' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
       "instructionIdentification": "aff52ratg5ageh53",
       "debtor": {
         "identifier": "Q7v_K2mNp4Xs"
       },
       "creditor": {
         "name": "Example Supplier, s.r.o.",
         "iban": "SK3112000000198742637541",
         "email": "billing@example.com"
       },
       "instructedAmount": {
         "value": 12.5,
         "currency": "EUR"
       },
       "endToEndIdentification": "/VS20261006/SS/KS",
       "remittanceInformation": "Invoice 2026-104"
     }'
```


## Submit payment order

`POST https://sandbox.payout.one/api/psd2/v1/payments/submission`

Submits a created payment order for execution. On success the payment order moves to status
`ACSC`.

Call it with a `PISPSUBMIT` token obtained for the payment order (see
[Payment flow](#payment-flow)). The token identifies the payment order, so the request has no
body.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Response 201

- `orderId` — string<uuid> · Payment order ID · e.g. `3b0f6c2e-8d41-4a7b-9c55-1e2f3a4b5c6d`
- `status` — string · Status of the payment order · e.g. `PDNG`
  - `PDNG` — Created, not submitted yet
  - `ACSC` — Submitted, payment created
  - `RJCT` — Submission refused (returned only by [Submit payment order](#submit_payment))
- `statusDatetime` — string<date-time> · When the status was read · e.g. `2026-10-06T08:15:30.123456Z`

### Responses

- `200` — The payment order was already submitted. It is returned unchanged.
- `201` — Payment order submitted, status `ACSC`.
- `400` — The payment order was refused, for example because the available balance is too low or a field of the payment order breaks a rule given in its description. The body has status `RJCT`; its `orderId` is a newly generated UUID, not the ID of the submitted payment order.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/payments/submission' \
  -H "Authorization: Bearer $TOKEN"
```


## Retrieve payment order status

`GET https://sandbox.payout.one/api/psd2/v1/payments/{order_id}/status`

Retrieves the current status of a payment order.

### Parameters

- `order_id` (path, required) — Payment order ID, returned as `orderId` by [Create payment order](#standard_sba_payment)
- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Response 200

- `orderId` — string<uuid> · Payment order ID · e.g. `3b0f6c2e-8d41-4a7b-9c55-1e2f3a4b5c6d`
- `status` — string · Status of the payment order · e.g. `PDNG`
  - `PDNG` — Created, not submitted yet
  - `ACSC` — Submitted, payment created
  - `RJCT` — Submission refused (returned only by [Submit payment order](#submit_payment))
- `statusDatetime` — string<date-time> · When the status was read · e.g. `2026-10-06T08:15:30.123456Z`

### Responses

- `200` — Success
- `400` — `order_id` is not a UUID.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.
- `404` — No payment order has this ID.

### Example

```bash
curl -X GET 'https://sandbox.payout.one/api/psd2/v1/payments/3b0f6c2e-8d41-4a7b-9c55-1e2f3a4b5c6d/status' \
  -H "Authorization: Bearer $TOKEN"
```


## Check balance

`POST https://sandbox.payout.one/api/psd2/v1/accounts/balanceCheck`

Checks whether an account has enough available balance for an amount.

### Parameters

- `Correlation-ID` (header) — Your ID to match a request to its response. Echoed back in the `correlation-id` response header.
- `Process-ID` (header) — Your ID to group several requests into one process. Echoed back in the `process-id` response header.

### Request body

- `instructionIdentification` — string · Your technical identification of the request. Accepted but not evaluated. · e.g. `piisp-20261006-0001`
- `creationDateTime` — string<date-time> · When the request was created, in RFC 3339 format. Accepted but not evaluated. · e.g. `2026-10-06T08:15:30.123456+00:00`
- `identifier` — string (required) · Account identifier (`identification.identifier` from [List accounts](#list_accounts)) · e.g. `Q7v_K2mNp4Xs`
- `amount` — object (required)
  - `amount` — integer | string (required) · Amount to check, as an integer or a decimal string such as "60.50". Fractional JSON numbers are not accepted. · e.g. `6000`
One of:

**Option 1**

- integer

**Option 2**

- string
  - `currency` — string (required) · ISO 4217 currency code · e.g. `EUR`

### Response 200

- `response` — string · Result of the check · e.g. `APPR`
  - `APPR` — The available balance in `currency` is greater than `amount`
  - `DECL` — The available balance is not greater than `amount`, or the user's account has no balance in `currency`
- `dateTime` — string<date-time> · When the check was made, in RFC 3339 format · e.g. `2026-10-06T08:15:30.123456+00:00`

### Responses

- `200` — Success
- `400` — The body is not valid JSON or a required attribute is missing.
- `401` — Missing or invalid bearer token, or the token lacks the required scope.

### Example

```bash
curl -X POST 'https://sandbox.payout.one/api/psd2/v1/accounts/balanceCheck' \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
       "instructionIdentification": "piisp-20261006-0001",
       "identifier": "Q7v_K2mNp4Xs",
       "amount": {
         "amount": 6000,
         "currency": "EUR"
       }
     }'
```

