BudgetBakers Partner AISP API (1.1.0-draft.6)

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.

Changelog — draft.6 (Provider metadata, code-verified in bb-api)

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.

Changelog — draft.5 (bank-service decisions applied)

Resolved the deferred conflicts from Martin's bank-service review (still-open items now tracked in SPEC-REVIEW-OPEN.md):

  • B1 background_refresh_not_allowed moved 403 → 406, with the documented side effects (connection inactivated + async consent callback).
  • B2 revoke is idempotent 200 (already-Inactive is a no-op) and 400 for a Disabled connection; the bogus 403 consent_inactive is gone.
  • B3 the internal migration guard is never named to partners — the code connection_migrating is renamed operation_temporarily_unavailable with a generic message ("Operation is currently not allowed").
  • B4 refresh_cooldown/refresh_quota_exceeded documented as indistinguishable today (same guard); split needs a bank-service change.
  • B6 connect-session states: Authenticating merged into RedirectedToBank (no event separates them); AwaitingAccountSelection flagged as a target needing new pipeline mechanics (O3/WP3.5).
  • B7 the error{code,message}+X-Request-Id envelope and 401/429 are synthesized gateway/app surface, not today's flat {status,errorDesc} body.
  • B8 signatureVersion documents the v1= linkage; legacy = unsigned static header.

Changelog — draft.4 (bb-api reconciliation, code-verified)

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:

  • Required sets relaxed to reality. On Transaction, Account, Client and Connection only id is guaranteed; other fields are nullable today. SDKs must not assume non-null.
  • externalRefexternalId (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.
  • Enrichment is always emitted on the transactions endpoint; the "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).
  • O2: partner/config.countries can be non-null — bb-api does filter providers by allowedCountries.

Changelog — draft.3 (Martin's SPEC-REVIEW, bank-service-verified)

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:

  • The refresh accept response is an empty {"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

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.

Authentication

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.

Errors (v1.1)

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.

Idempotency (v1.1)

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.

Webhooks

Connection lifecycle events are delivered as HTTP callbacks — see the companion specification webhooks-v2.yaml.

Providers

Bank (provider) discovery

List banks

Returns the banks (providers) available to this partner. sandboxOnly partners see only sandbox providers. Paginated; iterate until nextCursor is null.

Authorizations:
ApiKeyAuth
query Parameters
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 nextCursor field. Treat it as opaque and iterate until the response's nextCursor is null. (It is an offset today, so it is not stable across concurrent inserts.)

country
string

Filter by ISO 3166-1 alpha-2 country code.

search
string

Case-insensitive substring match on provider name.

Responses

Response samples

Content type
application/json
{
  • "limit": 100,
  • "nextCursor": null,
  • "data": [
    ]
}

Clients

End-user (client) lifecycle

Create client (upsert by externalId)

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.

Authorizations:
ApiKeyAuth
Request Body schema: application/json
required
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 externalId on the wire today). Upsert semantics are a v1.1 target — today's POST /v1/clients always inserts and does not deduplicate on this value; a matching id is not yet returned with 200. Not unique today.

Responses

Request samples

Content type
application/json
{
  • "email": "pepa@svalovec.cz",
  • "countryCode": "CZ",
  • "externalId": "acme-user-8213"
}

Response samples

Content type
application/json
{
  • "id": "538efad5-0966-4ca4-86e2-0856d115cbb6",
  • "email": "pepa@svalovec.cz",
  • "countryCode": "CZ",
  • "externalId": "acme-user-8213"
}

Look up client by externalId

Recovers a lost clientId mapping using the partner's own user identifier.

Authorizations:
ApiKeyAuth
query Parameters
externalId
required
string

Responses

Response samples

Content type
application/json
{
  • "id": "538efad5-0966-4ca4-86e2-0856d115cbb6",
  • "email": "pepa@svalovec.cz",
  • "countryCode": "CZ",
  • "externalId": "acme-user-8213"
}

Get client

Authorizations:
ApiKeyAuth
path Parameters
clientId
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "id": "538efad5-0966-4ca4-86e2-0856d115cbb6",
  • "email": "pepa@svalovec.cz",
  • "countryCode": "CZ",
  • "externalId": "acme-user-8213"
}

Delete client

Deletes a client and purges related data per DPA/SLA. X-Client-Id must match the path parameter.

Authorizations:
ApiKeyAuth
path Parameters
clientId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "errorDesc": "Invalid request parameters",
  • "error": {
    },
  • "requestId": "req_8f3a2c1e"
}

Connections

Bank connection lifecycle and consent management

Initialize connection (direct integration)

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".

Authorizations:
ApiKeyAuth
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

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 — OSVersion without a hyphen.

X-Client-Platform
string

Platform of the end user's device (e.g. iOS, Android, Web). Analytics metadata only.

Request Body schema: application/json
required
returnUrl
required
string <uri>

URL the user is redirected to after bank authorization, with connectionId, resultCode (Ok|Error) and optional error query parameters appended. Must match a return-URL pattern registered for one of the partner's apps in the portal (https, app link, or custom scheme).

providerId
required
string <uuid>

Provider ID from GET /v1/providers. For hosted bank selection use POST /v1/connect-sessions instead.

locale
string
Default: "en"

ISO 639-1 language code for the bank authorization UI.

subscribeToAllAccounts
boolean

Overrides the partner-level subscribeToAllAccountsDefault for this connection.

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 Personal (no 400). Send an exact enum value; do not rely on the fallback.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{}

Get connection status

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "id": "38d0322a-a01f-4ab5-811a-7014b597834a",
  • "state": "Active"
}

Delete connection

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "errorDesc": "Invalid request parameters",
  • "error": {
    },
  • "requestId": "req_8f3a2c1e"
}

Refresh connection (capability-gated)

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.

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Request Body schema: application/json
optional
returnUrl
string <uri>

Optional — refresh never redirects, so it is ignored (bank-service still binds the field; sending it is harmless).

locale
string

Responses

Request samples

Content type
application/json
{
  • "locale": "en"
}

Response samples

Content type
application/json
{
  • "status": "Accepted",
  • "nextRefreshPossibleAt": "2026-07-15T10:20:00Z"
}

Reconnect connection (capability-gated)

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).

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

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.

Request Body schema: application/json
required
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.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{}

Revoke consent

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.

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "errorDesc": "Invalid connection status = 'Disabled'",
  • "error": {
    },
  • "requestId": "req_8f3a2c1e"
}

Connect sessions

Hosted connect flow orchestration (recommended integration path)

Create a hosted connect session

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.

Authorizations:
ApiKeyAuth
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

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 — OSVersion without a hyphen.

X-Client-Platform
string

Platform of the end user's device (e.g. iOS, Android, Web). Analytics metadata only.

Request Body schema: application/json
required
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 connectionId, resultCode (Ok|Error|Cancelled), sessionId, and error (failures only).

locale
string
Default: "en"

ISO 639-1; hosted UI language. Falls back to en.

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.

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{}

Get connect session status

For troubleshooting and server-side reconciliation. The webhook stream remains the authoritative completion signal.

Authorizations:
ApiKeyAuth
path Parameters
sessionId
required
string
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "sessionId": "cs_9c1e7ab24d",
  • "state": "Completed",
  • "connectionId": "38d0322a-a01f-4ab5-811a-7014b597834a",
  • "resultCode": "Ok",
  • "error": null
}

Accounts

Account retrieval and subscription for an authorized connection

List accounts

Accounts for the authorized connection. Not paginated; raw JSON array capped at 100 accounts per connection. (An envelope is considered for v2.)

Authorizations:
ApiKeyAuth
path Parameters
connectionId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
[
  • {
    },
  • {
    }
]

Subscribe to an account

Marks the account as subscribed (included in data retrieval). (PUT /v1/accounts/{id}/subscription naming is parked for v2.)

Authorizations:
ApiKeyAuth
path Parameters
accountId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "errorDesc": "Invalid request parameters",
  • "error": {
    },
  • "requestId": "req_8f3a2c1e"
}

Unsubscribe from an account

Authorizations:
ApiKeyAuth
path Parameters
accountId
required
string <uuid>
header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
{
  • "errorDesc": "Invalid request parameters",
  • "error": {
    },
  • "requestId": "req_8f3a2c1e"
}

Transactions

Transaction retrieval for an account

List transactions

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.

Authorizations:
ApiKeyAuth
path Parameters
accountId
required
string <uuid>
query Parameters
limit
integer [ 0 .. 500 ]
Default: 10

Maximum number of items in this page.

nextCursor
string

Cursor returned by the previous page's nextCursor field. Treat it as opaque and iterate until the response's nextCursor is null. (It is an offset today, so it is not stable across concurrent inserts.)

Array of strings or numbers <= 20 items

JSON array of up to 20 strings or numbers, e.g. ?variableSymbol=["123","456"]. Numbers are treated as their string form.

header Parameters
X-Client-Id
required
string <uuid>

Client ID obtained from POST /v1/clients. Required on all client-scoped endpoints.

Responses

Response samples

Content type
application/json
Example
{
  • "limit": 10,
  • "nextCursor": "b2xkZXItcGFnZS10b2tlbg",
  • "data": [
    ]
}

Partner

Partner self-description and capability discovery

Capability discovery

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.

Authorizations:
ApiKeyAuth

Responses

Response samples

Content type
application/json
{
  • "partnerId": "2f5a1c3e-9b7d-4e21-a6c8-0d4f5e6a7b8c",
  • "name": "Acme Lending",
  • "mode": "sandbox",
  • "capabilities": {
    },
  • "consentDuration": "PT1H",
  • "countries": null,
  • "webhook": {
    }
}