Skip to content

The hosted connect flow ​

The hosted flow is how your users connect their bank: you create a connect session on your backend, send the user to the returned hostedUrl, and we take it from there — bank picker, consent, the bank's own OAuth, progress, and the redirect back to you. Your API key never touches the user's device, and the bank sees a real browser (banks block embedded WebViews).

Connect flow sequence

  1. Your backend creates a session: POST /v2/connect-sessions with the returnUrl this user should land on.
  2. You open hostedUrl: as a web redirect, or from your app in the system browser (see Mobile apps).
  3. The user picks a bank (skipped if you set providerId — or if you set connectionId, which reconnects an existing connection). They see the consent info and are redirected to the bank's OAuth.
  4. Back from the bank, a progress screen tracks the data download; if account selection is enabled for you, the user chooses which accounts to share before anything is downloaded.
  5. The user is redirected to your returnUrl with the result, and your backend receives the authoritative events as webhooks.

Connect sessions ​

sh
curl -X POST https://aisp-partner.bbapi.io/v2/connect-sessions \
  -H "X-Api-Key: {{BB_API_KEY}}" \
  -H "X-Client-Id: <clientId>" \
  -H "Content-Type: application/json" \
  -d '{ "returnUrl": "https://app.example.com/bb-callback" }'
json
{
  "sessionId": "cs_9c1e…",
  "hostedUrl": "https://aisp-connect.bbapi.io/s/<one-time-token>",
  "expiresAt": "2026-07-15T10:35:00Z"
}
  • The hostedUrl token is one-time — the first load consumes it. Hand it to the user immediately and never reuse it.
  • expiresAt is the single expiry to honor. An expired session ends in the Expired state with a friendly screen; just create a new session.
  • POST /v2/connect-sessions accepts an Idempotency-Key header — a retry with the same key replays the original response instead of minting a second session.
  • Optional fields: locale (UI language), country (preselects the bank picker's country), providerId (skips the picker entirely), connectionId (reconnect — see below; the bank is implied, so send it instead of providerId, not alongside).

Track the session from your backend:

sh
curl https://aisp-partner.bbapi.io/v2/connect-sessions/<sessionId> \
  -H "X-Api-Key: {{BB_API_KEY}}" \
  -H "X-Client-Id: <clientId>"
Session stateMeaning
AwaitingBankSelectionuser is in the bank picker
RedirectedToBankhanded off to the bank's OAuth
Authenticatinguser is authenticating at the bank
Fetchingaccounts and transactions are downloading
AwaitingAccountSelectionuser is choosing which accounts to share; the download is paused until they confirm
Completedterminal — connectionId is set
Failedterminal — see the failure webhook for the reason
Cancelledterminal — user cancelled
Expiredterminal — session outlived expiresAt

The return redirect ​

The final redirect to your returnUrl appends connectionId, resultCode (Ok | Error | Cancelled), sessionId, and error on failure.

Two rules keep integrations robust:

  • returnUrl is allowlisted. The portal (Apps → return URLs) holds your registered patterns — web URLs, iOS universal links, Android app links, custom schemes. Each session passes the concrete URL for this user; it must match a registered pattern or the create fails with validation_error.
  • The redirect is a UX signal, not the result. Users close tabs and switch apps mid-flow; the redirect may never arrive. The authoritative outcome is the session state and the webhooks (ConnectionCreateSuccess / ConnectionCreateFailed).

Account selection ​

If account selection is enabled for your integration, the flow stops after we have read the list of accounts covered by the bank consent and asks the user which of them to share. Nothing is downloaded until they confirm — an account the user leaves unchecked never has its transactions fetched, and the selection can never exceed what the bank authorized.

This makes the selection a consent, not a display filter:

  • Selected accounts become Active and receive transaction downloads.
  • Unselected accounts end Disabled. You still see them in GET /v2/connections/{id}/accounts — with their name, balance and subscriptionStatus — they simply carry no transactions.
  • Widening the selection later is not possible; the user has to go through a reconnect (below), which shows the picker again with their previous choices pre-checked.
  • On a reconnect, an account the bank no longer shares is shown to the user as ending and cannot be kept — consenting to it could not be honoured, since the bank would refuse to serve it. Expect such an account to come back Disabled with the transactions it already had; it is not deleted.
  • There is no per-account subscribe endpoint. The one way to withdraw is coarse and per connection: PATCH /v1/connections/{id}/revoke.

The account status you read back tells you where each account stands:

subscriptionStatusMeaningTransactions downloaded
Activethe user shared this accountyes
Inactiveshared before, consent has expiredno — reconnect to resume
Disablednot shared (unselected in the picker)never
Unsubscribednot part of this flow — legacy per-account subscriptionn/a

If the user abandons the picker, the attempt fails after an hour: you get ConnectionCreateFailed and the consent is revoked at the bank.

Connection lifecycle ​

A successful flow produces a connection — the durable link to one bank consent. Reading it (GET /v2/connections/{id}, .../accounts) is a /v2 call like everything else; the lifecycle actions below (reconnect, refresh, revoke, delete, direct create) are the one part of the API still served under /v1, until they get their /v2 aliases. It moves through four states:

StateMeaningRecovery
Pendingtransient during creation — never a resting state—
Activeauthorized, consent valid—
Inactiveconsent expired or revoked, or refreshes kept failingreconnect → Active
Disabledthe flow never completed (consent never obtained)none — create a new connection
  • Consent expiry and revocation arrive as ConnectionConsentExpired / ConnectionConsentRevoked webhooks and flip the connection to Inactive.
  • Reconnect repeats the bank authorization for an Inactive connection and keeps the same connectionId — your account and transaction history stay attached. Two ways in:
    • hosted: POST /v2/connect-sessions with connectionId — the user gets the same screens as a create, minus the bank picker, and the account picker at the end with their previous choices pre-checked. Nothing happens to the connection until the user opens hostedUrl.
    • direct: POST /v1/connections/{id}/reconnect, if you host the redirect yourself.
  • Reconnecting a Disabled connection is rejected; start a new connect session instead.

Refresh never redirects ​

POST /v1/connections/{id}/refresh fetches new transactions in the background and returns 202 Accepted — there is nothing to show the user. If the bank demands re-authentication, the refresh fails, the connection goes Inactive, and reconnect is the recovery path. Refreshes are also guarded by a cooldown and per-day quotas — handle refresh_cooldown, refresh_in_progress, and refresh_quota_exceeded per the error reference.