Download OpenAPI specification:
AISP API for contracted partners: create a client, connect to a bank (directly or via the hosted connect flow), retrieve accounts and transactions, refresh or reconnect (capability-gated), and revoke consent.
Reworked the Provider schema to the fields actually available on the bb-api
providers row (seitan reads it already; it just projects {id,name} — see
SPEC-REVIEW-OPEN.md §5.5). Added code, timeZone, changed bic (string) →
bicCodes (string[]). status reframed from health to visibility: the
healthy|degraded|down enum is dropped (no health signal exists anywhere in
bb-api) and replaced by the real visibility enum Active|Inactive|Hidden|Disabled.
Removed the sandbox boolean (no such column — mode is Api|Web; sandbox-ness
comes from the provider/country code convention). All additive/optional; only
id, name remain guaranteed. Also: sessionId/hosted-URL token documented as
opaque (SDKs must not parse them); ConnectSessionCreateResponse.expiresAt
reworded as the single session expiry; and AwaitingAccountSelection note fixed
per O3 (picker overlaps the fetch, no pipeline pause required).
Back-compat baggage dropped (SkipPay is offboarding and stays on the old
bank-service callback path): webhook.signatureVersion is now HMAC-only [v1]
(the unsigned legacy scheme is removed here and in webhooks-v2), and the
"kept for backwards compatibility / legacy" framing on errorDesc, refresh
returnUrl, the connection-create 200, and the API-key description was reworded
to just state backend reality.
Resolved the deferred conflicts from Martin's bank-service review
(still-open items now tracked in SPEC-REVIEW-OPEN.md):
background_refresh_not_allowed moved 403 → 406, with the
documented side effects (connection inactivated + async consent callback).Inactive is a no-op) and
400 for a Disabled connection; the bogus 403 consent_inactive is gone.connection_migrating is renamed operation_temporarily_unavailable with a
generic message ("Operation is currently not allowed").refresh_cooldown/refresh_quota_exceeded documented as
indistinguishable today (same guard); split needs a bank-service change.Authenticating merged into
RedirectedToBank (no event separates them); AwaitingAccountSelection
flagged as a target needing new pipeline mechanics (O3/WP3.5).error{code,message}+X-Request-Id envelope and 401/429 are
synthesized gateway/app surface, not today's flat {status,errorDesc} body.signatureVersion documents the v1= linkage; legacy = unsigned
static header.Reconciled the bb-api-served surface against the real current contract
(bb-api/docs/openapi/yaml/seitan.yaml, verified in code — the review is
archived in git history; open items in SPEC-REVIEW-OPEN.md). Factual
corrections, all additive/relaxing:
Transaction, Account,
Client and Connection only id is guaranteed; other fields are
nullable today. SDKs must not assume non-null.externalRef → externalId (the field name on the wire). Upsert
semantics remain a v1.1 target (today's create is a plain insert).Provider serves only {id, name} today; countryCode/logoUrl/bic/ status/sandbox are v1.1 target metadata. status (health) has no data
source and is now nullable/optional, not required.Account.type: SavingAccount (not SavingsAccount); list capped at
100 and not paginated. recordState: Cleared/Uncleared.Connection gains a nullable errorDesc; state may be null.null means disabled" case is the bank-service path only. recurrent
is derived and always present. Recurrence ids stay omitted (O10: they do
not exist in the partner payload).partner/config.countries can be non-null — bb-api does filter
providers by allowedCountries.Factual corrections only; the conflict decisions (403→406 for background
refresh, revoke idempotency, migration wording, provider health,
connect-session states) were applied later at draft.5, and any still-open
items are tracked in SPEC-REVIEW-OPEN.md. Verified behavior changes vs
today to note for integrators:
{"status":200} today; the v1.1
202 + RefreshAccepted body is an intended change, not current behavior.ownershipType parsing is case-insensitive with a silent Personal
fallback for unknown values (documented on the field).directRedirect providers return expiresAt = now on connection create.Versioning is selective and applied per path — the /v1/ segment belongs to
each individual path, not to the base URL. Individual endpoints may advance to
/v2/ independently; do not hardcode /v1/ as a shared client-side prefix.
Every request requires X-Api-Key. Client-scoped endpoints additionally require
the X-Client-Id header. Production traffic passes the Kong gateway with
per-partner rate limits; optional enterprise hardening (mTLS, IP allowlisting)
is configured at the infrastructure level, not in this API.
Every error response carries a stable machine-readable error.code (see the
ErrorCode schema) alongside the legacy errorDesc string. Clients and SDKs
MUST branch on error.code only and MUST NOT parse errorDesc. Every response
includes an X-Request-Id correlation header.
Implementation note (verified): today the backend emits a flat
{"status": <int>, "errorDesc": "<string>"} body — the error{code,message}
envelope and X-Request-Id do not exist yet and are synthesized in
v1.1 (gateway or app layer). 401 and 429 are produced by the Kong
gateway, not the backend; until a response-transformer/request-id plugin is
agreed with infra (design doc O1), their bodies are Kong's stock shape and may
not carry the envelope. Treat this section as the v1.1 target contract.
POST /v1/connections and POST /v1/connect-sessions accept an
Idempotency-Key header (24 h window, scoped per partner): a replay with the
same key returns the original response. POST /v1/clients supports upsert
semantics via externalId.
Connection lifecycle events are delivered as HTTP callbacks — see the companion
specification webhooks-v2.yaml.
Returns the banks (providers) available to this partner. sandboxOnly partners see only sandbox providers. Paginated; iterate until nextCursor is null.
| limit | integer [ 0 .. 500 ] Default: 100 Page size. Default 100 for this endpoint (raised in v1.1), max 500. |
| nextCursor | string Cursor returned by the previous page's |
| country | string Filter by ISO 3166-1 alpha-2 country code. |
| search | string Case-insensitive substring match on provider name. |
{- "limit": 100,
- "nextCursor": null,
- "data": [
- {
- "id": "ef86e35c-83f1-4b9e-bde2-cfc40b543e05",
- "name": "Komerční banka",
- "code": "kb_cz",
- "countryCode": "CZ",
- "bicCodes": [
- "KOMBCZPP"
], - "timeZone": "Europe/Prague",
- "status": "Active"
}
]
}Creates a client record for a unique end user. With externalId, the call is an upsert: if a client with the same reference exists, it is returned with HTTP 200 and no duplicate is created. Store the returned id and pass it as X-Client-Id on client-scoped endpoints.
| email required | string <email> |
| countryCode required | string ISO 3166-1 alpha-2 country code. |
| externalId | string <= 255 characters Partner's internal user identifier (field name |
{- "email": "pepa@svalovec.cz",
- "countryCode": "CZ",
- "externalId": "acme-user-8213"
}{- "id": "538efad5-0966-4ca4-86e2-0856d115cbb6",
- "email": "pepa@svalovec.cz",
- "countryCode": "CZ",
- "externalId": "acme-user-8213"
}Recovers a lost clientId mapping using the partner's own user identifier.
| externalId required | string |
{- "id": "538efad5-0966-4ca4-86e2-0856d115cbb6",
- "email": "pepa@svalovec.cz",
- "countryCode": "CZ",
- "externalId": "acme-user-8213"
}Deletes a client and purges related data per DPA/SLA. X-Client-Id must match the path parameter.
| clientId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "errorDesc": "Invalid request parameters",
- "error": {
- "code": "validation_error",
- "message": "Field 'returnUrl' is required."
}, - "requestId": "req_8f3a2c1e"
}Initializes the bank connection and returns a URL for user redirection. This is the direct integration path where the partner renders their own bank picker; for the hosted flow use POST /v1/connect-sessions. After the bank journey, the user is redirected to returnUrl with connectionId, resultCode (Ok|Error) and optional error appended. Authoritative completion signal is the webhook stream, not the redirect. Auth windows (additive): about 2 minutes to initiate bank authentication, then completion is checked ~20 minutes later (≈ 22 minutes from start). expiresAt reflects the initiate window; note that directRedirect providers return expiresAt = now, so it is not universally a "time to reach the bank".
| X-Client-Id required | string <uuid> Client ID obtained from |
| Idempotency-Key | string <= 255 characters Unique key (UUID recommended) making the request idempotent for 24 hours, scoped per partner. A replay with the same key returns the original response. |
| X-Application-Name | string Name of the partner application the end user is connecting from. Analytics metadata only — attached to the resulting connection's tracking events; omitting it has no functional effect. |
| X-Client-App-Version | string Version of the partner application. Analytics metadata only. |
| X-Client-OSVersion | string Operating-system version of the end user's device. Analytics metadata only. Note the header spelling — |
| X-Client-Platform | string Platform of the end user's device (e.g. |
| returnUrl required | string <uri> URL the user is redirected to after bank authorization, with |
| providerId required | string <uuid> Provider ID from |
| locale | string Default: "en" ISO 639-1 language code for the bank authorization UI. |
| subscribeToAllAccounts | boolean Overrides the partner-level |
| skipConsentScreen | boolean Effective only when permitted by partner configuration (compliance-gated). |
| ownershipType | string Default: "Personal" Enum: "Personal" "Shared" "Business" Parsing is case-insensitive today, and any unrecognized value silently falls back to |
{- "providerId": "ef86e35c-83f1-4b9e-bde2-cfc40b543e05",
- "locale": "en",
- "subscribeToAllAccounts": true
}{- "connectionId": "38d0322a-a01f-4ab5-811a-7014b597834a",
- "expiresAt": "2026-07-15T10:22:00Z"
}| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "id": "38d0322a-a01f-4ab5-811a-7014b597834a",
- "state": "Active"
}| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "errorDesc": "Invalid request parameters",
- "error": {
- "code": "validation_error",
- "message": "Field 'returnUrl' is required."
}, - "requestId": "req_8f3a2c1e"
}Fetches new transactions for an existing Active connection in the background. Refresh never redirects the user — if the bank requires re-authentication, the refresh fails, the connection becomes Inactive, and the recovery path is reconnect. Completion is signalled by ConnectionRefreshSuccess / ConnectionRefreshFailed webhooks (TransactionsFetching* progress events are reused; AccountsFetching* are not sent on refresh). Guards: concurrent call → refresh_in_progress; 10-minute cooldown after a successful manual refresh and per-provider daily quotas → refresh_cooldown / refresh_quota_exceeded with nextRefreshPossibleAt.
| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
| returnUrl | string <uri> Optional — refresh never redirects, so it is ignored (bank-service still binds the field; sending it is harmless). |
| locale | string |
{- "locale": "en"
}{- "status": "Accepted",
- "nextRefreshPossibleAt": "2026-07-15T10:20:00Z"
}Renews consent for an Inactive connection via a new bank authorization. Always redirects the user — behaves like create but keeps the same connectionId. Rejected for Disabled connections (connection_not_recoverable).
| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
| Idempotency-Key | string <= 255 characters Unique key (UUID recommended) making the request idempotent for 24 hours, scoped per partner. A replay with the same key returns the original response. |
| returnUrl required | string <uri> Reconnect always redirects the user to the bank; a return URL is required. |
| locale | string Default: "en" |
| subscribeToAllAccounts | boolean May yield more accounts than the previous consent. |
| skipConsentScreen | boolean Effective only when permitted by partner configuration. |
{- "locale": "en"
}{- "connectionId": "38d0322a-a01f-4ab5-811a-7014b597834a",
- "expiresAt": "2026-07-15T10:22:00Z"
}Revokes consent for the connection; the connection becomes Inactive. BudgetBakers also auto-revokes after successful data retrieval for one-shot partners (autoRevokeAfterCreate). Idempotent: revoking an already-Inactive connection succeeds with 200 and no callback (no-op). It keys off the connection status, not consent, so there is no 403 consent_inactive here.
| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "errorDesc": "Invalid connection status = 'Disabled'",
- "error": {
- "code": "connection_not_recoverable",
- "message": "Connection is Disabled and cannot be revoked."
}, - "requestId": "req_8f3a2c1e"
}Creates a session for the hosted connect flow and returns a one-time hostedUrl to open for the end user. The hosted flow renders the bank picker, consent information, bank redirect, progress and (when enabled) account selection, then redirects to returnUrl. The underlying connection is created internally once the user confirms a bank; its lifecycle (webhooks, billing) is identical to a directly created connection. The optional client-metadata headers are stored with the session and attached to that connection's tracking events, so hosted-flow analytics match direct creation.
| X-Client-Id required | string <uuid> Client ID obtained from |
| Idempotency-Key | string <= 255 characters Unique key (UUID recommended) making the request idempotent for 24 hours, scoped per partner. A replay with the same key returns the original response. |
| X-Application-Name | string Name of the partner application the end user is connecting from. Analytics metadata only — attached to the resulting connection's tracking events; omitting it has no functional effect. |
| X-Client-App-Version | string Version of the partner application. Analytics metadata only. |
| X-Client-OSVersion | string Operating-system version of the end user's device. Analytics metadata only. Note the header spelling — |
| X-Client-Platform | string Platform of the end user's device (e.g. |
| returnUrl required | string <uri> Final destination after the hosted flow completes. Validated at session creation against the app's registered return-URL patterns (https app / universal links and custom schemes supported). The redirect appends |
| locale | string Default: "en" ISO 639-1; hosted UI language. Falls back to |
| country | string ISO 3166-1 alpha-2 preselect for the bank picker. |
| providerId | string or null <uuid> When set, the bank picker is skipped and the flow starts at the consent step. |
{- "locale": "en",
- "country": "CZ"
}{- "sessionId": "cs_9c1e7ab24d",
- "expiresAt": "2026-07-15T10:35:00Z"
}For troubleshooting and server-side reconciliation. The webhook stream remains the authoritative completion signal.
| sessionId required | string |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "sessionId": "cs_9c1e7ab24d",
- "state": "Completed",
- "connectionId": "38d0322a-a01f-4ab5-811a-7014b597834a",
- "resultCode": "Ok",
- "error": null
}Accounts for the authorized connection. Not paginated; raw JSON array capped at 100 accounts per connection. (An envelope is considered for v2.)
| connectionId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
[- {
- "id": "5d3f1a2b-7c8e-4f90-a1b2-c3d4e5f60718",
- "name": "Current Account",
- "type": "CurrentAccount",
- "balance": 816,
- "currencyCode": "CZK",
- "iban": "CZ6508000000192000145399"
}, - {
- "id": "9e8d7c6b-5a49-4382-b1c0-d9e8f7a6b5c4",
- "name": "Savings Account",
- "type": "SavingAccount",
- "balance": 120450.5,
- "currencyCode": "CZK",
- "iban": null
}
]Marks the account as subscribed (included in data retrieval). (PUT /v1/accounts/{id}/subscription naming is parked for v2.)
| accountId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "errorDesc": "Invalid request parameters",
- "error": {
- "code": "validation_error",
- "message": "Field 'returnUrl' is required."
}, - "requestId": "req_8f3a2c1e"
}| accountId required | string <uuid> |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "errorDesc": "Invalid request parameters",
- "error": {
- "code": "validation_error",
- "message": "Field 'returnUrl' is required."
}, - "requestId": "req_8f3a2c1e"
}Transactions for an account, cursor-paginated. History depth depends on the bank provider (typically 90 days). Filter by variable symbol via the variableSymbol parameter: matching ignores leading zeros on both sides ("888" matches 888, 0888, 00888); trailing digits remain significant.
| accountId required | string <uuid> |
| limit | integer [ 0 .. 500 ] Default: 10 Maximum number of items in this page. |
| nextCursor | string Cursor returned by the previous page's |
Array of strings or numbers <= 20 items JSON array of up to 20 strings or numbers, e.g. |
| X-Client-Id required | string <uuid> Client ID obtained from |
{- "limit": 10,
- "nextCursor": "b2xkZXItcGFnZS10b2tlbg",
- "data": [
- {
- "id": "7c4e1a8b-2d9f-4e3a-b5c6-d7e8f9a0b1c2",
- "amount": -2326,
- "currencyCode": "CZK",
- "recordState": "Cleared",
- "recordDate": "2026-07-14T09:41:00Z",
- "note": "Payment card transaction",
- "counterParty": "ALBERT PRAHA",
- "enrichment": {
- "category": {
- "categoryId": 550,
- "categoryName": "food_and_drinks",
- "subcategoryId": 55001,
- "subcategoryName": "food_and_drinks__groceries"
}, - "merchant": {
- "id": "4b6a8c0e-1d3f-4a5b-9c7d-8e0f2a4b6c8d",
- "name": "Albert CZ",
}, - "recurrent": false
}, - "details": {
- "variableSymbol": null,
- "constantSymbol": null,
- "specificSymbol": null,
- "transactionCode": "PMNT-ICDT-ESCT",
- "creditorReference": null,
- "externalCategoryName": null,
- "mccCode": "5411",
- "others": null
}
}, - {
- "id": "3f5e7d9c-1b2a-4c6d-8e0f-a1b2c3d4e5f6",
- "amount": 42000,
- "currencyCode": "CZK",
- "recordState": "Cleared",
- "recordDate": "2026-07-01T06:02:00Z",
- "note": "Salary July",
- "counterParty": "ACME s.r.o.",
- "enrichment": {
- "category": {
- "categoryId": 900,
- "categoryName": "income",
- "subcategoryId": 90001,
- "subcategoryName": "income__salary"
}, - "merchant": null,
- "recurrent": true
}, - "details": {
- "variableSymbol": "888",
- "constantSymbol": "0308",
- "specificSymbol": null,
- "transactionCode": "PMNT-RCDT-ESCT",
- "creditorReference": null,
- "externalCategoryName": null,
- "mccCode": null,
- "others": null
}
}
]
}Self-description of the calling partner: enabled capabilities, consent duration override, and webhook signature mode. SDKs and the hosted flow configure themselves from this. Calling a disabled capability elsewhere returns 403 capability_disabled.
{- "partnerId": "2f5a1c3e-9b7d-4e21-a6c8-0d4f5e6a7b8c",
- "name": "Acme Lending",
- "mode": "sandbox",
- "capabilities": {
- "refresh": false,
- "reconnect": false,
- "enrichment": false,
- "autoRevokeAfterCreate": true,
- "nonRegulatedProviders": false
}, - "consentDuration": "PT1H",
- "countries": null,
- "webhook": {
- "signatureVersion": "v1"
}
}