Linking Cards to Bank Accounts
Introduction
Linking a card to a connected bank account is what turns on earlier settlement for that card: when a card authorization matches a posted bank transaction on the account, the transaction flips PENDING→SETTLED on the bank's evidence — typically within hours of the swipe rather than days.
Linking cards is optional and independent of the bank connection itself. Bank-only events (fees, transfers, payroll, FX markup) fire on a connected bank account with no card link at all — see Quick Start: Bank Linking. This page is about the second step: attaching cards for earlier settlement.
You may not need to call anythingCard links are detected automatically. Read on for when to link manually — pre-linking before any spend, correcting an assignment, or importing history.
Automatic detection
The first time a card's transactions produce a high-confidence match against a bank account's feed, Astrada links that card to the account — no API call required. This is the common path: connect the bank, let spend flow, and cards attach themselves to the right account as evidence appears.
Two things to know about auto-detected links:
- They do not backfill history. Only transactions from the moment of linking forward benefit from earlier settlement. To import earlier history, link the card manually with
backfill(below). - The current set of linked cards is always readable — see Reading the current links.
Manual linking
PATCH /bank-accounts/{id} (scope banking:write) sets the account's card set explicitly. It's the manual override for the automatic detector. Use it to:
- Pre-link a card before any spend exists, so the very first matching transaction settles early.
- Correct an assignment, or move a card between accounts.
- Import history for newly linked cards, via
backfill.
PATCH /bank-accounts/a1b2c3d4-e5f6-7890-abcd-ef1234567890 HTTP/1.1
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
{
"cardIds": [
"5dec2c49-0aa5-4683-a317-427eb5d115f3",
"8abc1d23-4e56-7f89-0abc-def123456789"
],
"backfill": { "startDate": "2026-01-27", "endDate": "2026-04-27" }
}curl -X PATCH https://api.astrada.co/bank-accounts/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"cardIds": [
"5dec2c49-0aa5-4683-a317-427eb5d115f3",
"8abc1d23-4e56-7f89-0abc-def123456789"
],
"backfill": { "startDate": "2026-01-27", "endDate": "2026-04-27" }
}'{
"cardIds": [
"5dec2c49-0aa5-4683-a317-427eb5d115f3",
"8abc1d23-4e56-7f89-0abc-def123456789"
],
"backfill": { "startDate": "2026-01-27", "endDate": "2026-04-27" }
}| Field | Type | Required | Description |
|---|---|---|---|
cardIds | UUID[] | Yes | The cards to link. Full replacement — send the complete desired set, not a delta. |
backfill | object | No | Opt-in historical import. Runs only for cards being added in this request. |
backfill.startDate | ISO date | No | Defaults to 90 days ago. |
backfill.endDate | ISO date | No | Defaults to today. |
Response (200 OK):
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"cardIds": [
"5dec2c49-0aa5-4683-a317-427eb5d115f3",
"8abc1d23-4e56-7f89-0abc-def123456789"
],
"state": "active",
"updatedAt": "2026-04-27T15:00:00Z",
"backfill": { "transactionsProcessed": 50, "matchesFound": 10 }
}The backfill block appears only when a backfill was requested.
cardIdsis a full replacementThe endpoint replaces the account's explicit card set with exactly what you send — it is not a delta. To add a card, send the existing
cardIdsplus the new one; to remove one, send the set without it. SendingcardIds: []clears explicit links (automatic detection may re-link a card on its next high-confidence match).
Backfill history
When you add cards, pass backfill: { startDate, endDate } to import historical bank transactions for those cards (default: the last 90 days). Earlier settlement then runs against any historical card authorizations still in PENDING, and bank-only events fire retroactively for historical transactions that remain unmatched.
Backfill runs only for the cards added in that request — not cards already linked to the account.
Multi-card linking & constraints
A bank account can carry many cards (1:N); a card links to at most one bank account at a time. Each rule below returns a specific error — handle them in your linking UI:
| Rule | On violation |
|---|---|
| A card links to only one bank account at a time. | 409 Conflict — the card is already linked to another bank account. |
Every card and the bank account must share the same subaccountId. | Enforced upstream by card issuance; banking trusts the cardIds you pass. |
To move a card from account A to account B: first PATCH A with a cardIds set that omits the card (freeing it), then PATCH B to add it. Linking a card to B while it's still linked to A returns 409 CARD_ALREADY_LINKED.
Reading the current links
GET /bank-accounts/{id}(scopebanking:read) — the account, including itscardIds.GET /bank-accounts?cardId={cardId}— the account a given card is linked to (empty if none).GET /bank-accounts?subaccountId={subaccountId}— every account for a subaccount, each with itscardIds.
Next steps
- Quick Start: Bank Linking — the end-to-end on-ramp.
- Bank Linking Webhooks & Events — what earlier settlement looks like on the wire.
- Webhook Event Types — the canonical event catalog.