Skip to main content

Bridge flow alignment

What this covers / who it's for. The map from every Bridge flow/mechanic in the digest pages onto Stables' own modules, journeys, and commands: what Bridge does, where it lands in our code today, and whether that landing is served, planned, a tracked programme item, not applicable, or an open question. For anyone reasoning about "how does this Bridge thing work for us?" — before writing adapter, mirror, or client code. Bridge behavior is as fetched 2026-09-03; the module/command/route names are verified against code at the same date (BridgeRailsProvider's implements list, route:list, the module commands).

How money moves through Bridge for a Stables tenant

Bridge composes four primitives, and every use case is a rearrangement of them (Guides §2):

  1. Customer (+ endorsements) — the onboarded party. Created via a hosted KYC Link (POST /kyc_links, the path our adapter takes) or the direct Customers API; per-rail endorsements (e.g. sepa, pix, faster_payments) gate which rails the customer may use and are themselves gated on KYC + ToS acceptance.
  2. Bridge wallet — a Bridge-custodied stablecoin wallet, keyed by bridge_wallet_id (never the raw on-chain address).
  3. Virtual account — the fiat-in primitive: a permanent, customer-specific deposit address.
  4. Transfer (with external accounts / liquidation addresses as source/destination references) — the movement primitive. One POST /transfers object backs payouts, conversion orders, and custody withdrawals; its source/destination pair references any combination of a bank rail, a chain address, or bridge_wallet.

Two Bridge realities shape how this maps onto our neutral account model (alignment design §1–2):

  • A Bridge virtual account is transfer-only. Fiat that lands on it is auto-converted to a stablecoin and forwarded to the VA's nominated destination — an external crypto address or a Bridge custodial wallet — with no fiat balance ever held. Because a future Sovera account does hold fiat, the neutral model keeps this as a per-account settlement mode: pass_through (Bridge — forward on arrival, no balance) vs held (Conduit/Sovera — balance retained). A pass_through account carries a nominated destination and never writes a balance projection; the treasury master-balance rollup excludes it by construction.
  • Every Bridge transfer needs an explicit source. Bridge has no "spend from my balance" default, so a payout/order/custody move must name where funds come from (payment_rail + currency + one of from_address/external_account_id/bridge_wallet_id). The adapter throws ProviderValidationException('Bridge requires an explicit transfer source') rather than guessing; aligning our makers to supply it is the alignment design's PR B (§4).

Legend — the status column

TagMeaning
SERVED (D118)The BridgeRailsProvider adapter maps this today (registered, not granted — no live traffic until the go-live flip).
STUBBED (D118)The adapter advertises the interface but the method throws ProviderOperationNotSupportedException (422) — Bridge structurally cannot serve it.
PLANNED §nDesigned but not built — the Bridge domain-alignment design §n (its PRs A/B/C).
PROGRAMME Bn/SnA tracked coverage-programme item — D118 §Follow-ups / router tracker §7.
NOT APPLICABLENo Stables product surface consumes this Bridge mechanic.
OPEN QUESTIONBlocked on an answer from Bridge (see the consolidated list below).

Everything below is registered, not granted at the seam level (ADR D118): Bridge carries zero jurisdiction_provider_grants rows, so no jurisdiction routes to it until the human-approved go-live flip. "SERVED" means "the adapter maps it", not "it runs in production".

Onboarding & compliance

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
Individual onboarding (KYC Links / Customers API)Hosts a Persona-backed identity + document capture behind a generated link; or a direct Customers-API createonboardingSubmitOnboardingApplicationCommandOnboardsCustomers/VerifiesIdentity; BridgeRailsProvider implements bothSERVED (D118) via KYC LinksAdapter uses POST /kyc_links, not the Customers API; documents are never forwarded (Bridge collects them). Customers §2
KYB + UBO / associated personsBusiness onboarding with beneficial-owner graphonboardingVerifiesBusinesses + FetchesVerification; BridgeRailsProvider implements bothSERVED (D118) via KYC LinksFull Customers-API KYB payload (D117's pinned enums) deferred — D118 item 2. Customers §4
Endorsements per railPer-rail approvals gating which rails a customer may useonboardingendorsements[] on kyc_links.create; requirement machine codes → application statusSERVED (D118) (submit-time)"Add an endorsement to an existing customer" endpoint is an OPEN QUESTION. Customers §6
Terms-of-Service acceptance orderingToS acceptance gates activation; approved KYC + pending ToS stays pendingonboarding — application status = map(kyc_status, tos_status) via BridgeStatusMapSERVED (D118)An approved KYC with pending ToS stays Processing. Customers §3
Customer status lifecycle (non-linear)Status transitions (can move backward: activeunder_review)onboardingApplicationStatus via BridgeStatusMap; customer.* webhook → mirrorSERVED (D118) (mapping) + PLANNED §3 (handlers)Webhook domain handlers (BridgeCustomerUpdatedHandler) not built yet. Customers §5
High-risk / prohibited activitiesProhibits/high-risk-flags certain business activitiesonboardingSubmitActivityDeclarationCommand + onboarding_activity_declarations (D117) + provider-support matrixSERVED (D118) (our declaration) + OPEN QUESTION (business-model permission)Whether Bridge permits Stables' own tenant model is the first open question below. Customers §9
Region-specific ID / document requirementsPer-country ID/PoA/formation-doc requirementsonboarding (hosted flow collects docs)SERVED (D118) via hosted flowDocuments collected by Bridge's flow, not forwarded by us. Customers §8
Restricted-customer caps / supported & prohibited countriesAggregate caps on restricted US customers; country allow/deny listsfeatures jurisdictions + onboarding gatingOPEN QUESTION (cap period) / informationalLifetime-vs-30-day cap ambiguity is an open question. Customers §10–11

Accounts, virtual accounts & fees

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
VA create + transfer-only auto-convertFiat → stablecoin on arrival, forwarded to the VA destination; no fiat balanceaccountsvirtual_accounts mirror (today mirror-only: UpsertVirtualAccountMirrorCommand; no client create command exists yet)PLANNED §2CreateVirtualAccountCommand + SettlementMode/SettlementDestination are new (not built — verified). VA §1–2
VA destination nomination / mutationDestination settable at create and mutable via PUT ("future transactions use the updated destination")accountsUpdateVirtualAccountDestinationCommand (step-up MFA + approval-quorum consensus)PLANNED §2Re-nomination is money-sensitive: fresh step-up and quorum (engine's 3rd consumer). VA §3
Multiple VAs per currency; deactivate / reactivateSeveral VAs per currency; activate/deactivateaccountsUpsertVirtualAccountMirrorCommand on activation/deactivation eventsPLANNED §3Handler BridgeVirtualAccountActivityHandler routes lifecycle events to the mirror. VA §4
VA deposit events + deposit_id reconciliation keysvirtual_account.activity events, one deposit grouped by deposit_id, per-rail keysfundingUpsertTransactionMirrorCommand (exists); Bridge activity handlerSERVED (D118) (mirror cmd) + PLANNED §3 (handler)One transactions row per deposit_id; new fee/hash columns planned. VA §6
Developer fees & minimumsConfigurable developer fee %, per-rail minimumsaccounts/paymentsdeveloperFeePercent on the VA spec; fee columns on transactionsPLANNED §2 / §3No fee-config surface today; fee fields promoted from receipt.*. Transfers §13
Rail-specific limits / thresholdsPer-rail min/max, third-party P2P ACH capsrails.bridge.virtual_account_routes config (allowed rail pairs)PLANNED §2 + OPEN QUESTION (exact caps)Route validity is config-driven, fail-loud; exact numeric caps need Bridge. Transfers §14

Money movement — transfers, payouts, orders

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
Transfer object + 13 states (payout / order legs)One POST /transfers object backs payouts and conversion orders; 13-state lifecyclepaymentsInitiatePayoutCommand (ManagesPayouts, FiatRails), CreateOrderCommand/CancelOrderCommand (ManagesOrders, Conversion); TransferMapper/TransferPayloadMapperSERVED (D118) (adapter) + PLANNED §4 (domain supplies source)BridgeStatusMap reuses the neutral order/payout/transaction vocab. Transfers §1–2
Transaction reads (fiat)Read a transfer as a transactionpayments/fundingManagesTransactions::getTransaction/listTransactions (implemented after the D118 item-1b rename)SERVED (D118)List honours only a raw Bridge state filter (no clean neutral-status inverse). Transfers §2
External accounts + Verification of PayeeBank accounts as payout destinations; VoP name-checkpaymentsAddWhitelistRecipientCommandManagesWhitelistRecipients; ExternalAccountMapperSERVED (D118) (masked identifiers)Bridge never echoes a full account number/IBAN — mapper stores only last_4; consumers must not assume full identifiers. Transfers §11
Plaid-linked external accountsOnboard a bank account via Plaidpayments — extension of whitelist-recipient registrationPROGRAMME B6Not in the D118 adapter. Transfers §11
Fiat / crypto returns + return policiesfiat_deposit_return, crypto returns, return_instructions/return-policy requirementpayments/compliance — no returns lifecycle todayPROGRAMME B9 (return policies)Crypto return policy must be configured before any crypto-in flow. Transfers §8–10
Funds requests (recalls)Recall/return-of-funds request lifecyclepayments — no recall surface todayPROGRAMME B8Response schema/deadline semantics are an open question. Transfers §10
Static / recurring template transfersReusable transfer templates for recurring movement— no recurring-transfer product surfaceNOT APPLICABLENo Stables recurring-payout journey; revisit if one is built. Transfers §5
Payment-tracking identifiers & receiptsreceipt.*, tracking ids, payment_received_railpayments/funding — transaction mirror rowsPLANNED §3Persist payment_received_rail (cleared rail), not just the requested rail. Transfers §12
Cutoffs, settlement windows, holidaysPer-rail processing cutoffs and bank holidays— settlement reflected asynchronously via transfer statusNOT APPLICABLE (informational)We reflect Bridge's settlement status, we don't enforce cutoffs; exact windows are an open question. Transfers §15

Custody, wallets & liquidation

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
Bridge custodial wallets (create/get/list)Single-key, platform-controlled stablecoin wallets per customer/chainaccountsCreateWalletCommandManagesWallets; WalletMapper; BalanceProjectorSERVED (D118)Wallets require Bridge L&C approval + carry geo exclusions (go-live precondition). VA §11
Custody transfers (withdrawals)A transfer sourced from bridge_walletaccounts/custody-controlsManagesCustodyTransactions::initiate/get/list (renamed pair, D118 1b)SERVED (D118) + PLANNED §4 (custody resolver arm)In-app approval-quorum (D97) is the sole N-eyes gate — Bridge has no vendor-side co-sign. VA §13
Wallet rotation(no rotate/replace-key operation)ManagesWallets::rotateWalletSTUBBED (D118)Bridge manages keys; 422 terminal. VA §11
Signers / signing quorum(no signer/quorum concept — wallets are single-key)ManagesWalletSigners + ManagesSigningQuorum (advertised)STUBBED (D118)Bridge answers FALSE to ManagesSignersAndQuorumNatively. VA §12–13
Registered / whitelisted crypto addresses(no address book — crypto destinations named inline as to_address)paymentsRegisterAddressCommandManagesRegisteredAddresses (Custody group)STUBBED (D118)Entire surface stubbed; destinations are inline per transfer. Transfers §3
initiation_required + SCA initiation{}Some wallet transfers require a strong-customer-auth initiation step— not modelled as a capability in D118OPEN QUESTION / not modelledFlagged for the custody forward; not in adapter scope. VA §13
bridge_wallet.activity events + payment_route joinCustody-side view of a VA deposit landing; payment_route.deposit_id join keyaccountsBridgeWalletActivityHandlerRefreshWalletBalancesCommandPLANNED §3Stamps destination_tx_hash on the deposit row via the join key. VA §14
Liquidation addresses + drains (off-ramp)Permanent crypto deposit address that auto-forwards (offramp); 12-state drain lifecycle— no off-ramp intake surface todayPROGRAMME B1New ManagesLiquidationAddresses capability + mirror + client API. VA §8–9
Master balance / FX rates(Bridge exchangeRates — live FX quotes)treasuryGetMasterBalanceQuery; ExchangeRateProvider (table-less; ConfiguredExchangeRateProvider today)PROGRAMME B3Bridge-backed ExchangeRateProvider replaces the config provider. Guides §5

Compliance, travel rule & platform mechanics

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
Travel rule/travel_rule surface for on-chain legs (EEA: no threshold; US: > $3,000)fundingSubmitSenderInformationCommand (ManagesTransactions::submitSenderInformation)STUBBED (D118)PROGRAMME B2Bridge has no inbound-deposit RFI; a new SubmitsTravelRule capability closes the gap. Guides §9
Webhooks — signature / retry / replayAsymmetric-signed (X-Webhook-Signature: t=…,v0=…, RSA + SHA-256 over "<t>.<rawBody>"), retried on any non-2xxwebhooksBridgeSignatureValidator + BridgeTenantResolver (registered, D118)SERVED (D118) (ingress) + PLANNED §3 (handlers)Signed/deduped/stored/acked with zero domain handlers wired today. Webhooks §2–4
Signature hashing conventionBridge's own samples disagree (single vs double SHA-256)BridgeSignatureValidatorOPEN QUESTIONUnresolvable from docs; needs a real production delivery (sandbox sends none). Webhooks §3
API mechanics (idempotency / pagination / precision)Idempotency-Key ≤64 chars; cursor pagination; string money/precisionbridge-sdk + Support\BridgeIdempotencyKey::normalize(); DTOs keep money/timestamps as stringsSERVED (D118)Bridge is the first provider needing key normalisation (SHA-256 when > 64). Webhooks §6
SandboxSchema-only: no money movement, no payment webhooks, no KYC linksoperational precondition — production canary before the flipPLANNED §6 (operational)Money-path + signature scheme can only be validated in production. Guides §11

Cards & issuance

Bridge flowWhat Bridge doesOur journey / moduleStatusNotes → digest
Cards (Stripe Issuing)Card issuing on Stripe Issuing: eligibility, funding, real-time auth, freezes, PINs, statements, webhooks ↔ Stripe— no cards module (CapabilityGroup::Cards exists, no interface bound)PROGRAMME B5Largest programme item: new IssuesCards capability + a new cards module, its own design. Cards
Stablecoin issuance (mint/burn/reserves)Mint/burn, reserves, liquidity allocation— Stables is not a stablecoin issuerNOT APPLICABLEWebhooks §8
RewardsReward-programme summariestreasury/reportingPROGRAMME B10Lowest-priority programme item. Webhooks §8

Open questions for Bridge

This list is CANONICAL — consolidated and deduped from the six digest pages' own open-questions sections, which point back here rather than duplicate it. The business-model question is first — it gates whether any of the above is licensable at all. When Bridge answers a question, close it here first, then remove or annotate it on the source page(s) (see the README's "How to refresh").

  1. Does Bridge permit Stables' own business model? Bridge's ToS prohibits "Investment or credit services / Digital asset exchange services / Money services / Money transmission services (provided by Users to third parties via Bridge)". Confirm directly with Bridge that offering Bridge-backed money movement to Stables' tenants/customers is permitted — this is an inference from the docs that needs a real answer. (Customers §9, alignment §8)
  2. Onboarding shape gaps. Correct field for a business's beneficial owners at create (ultimate_beneficial_owners[] vs associated_persons[]); whether a standalone "add an endorsement to an existing customer" endpoint exists; whether ToS/KYC links expire; the full enum values for business_type, account_purpose, source_of_funds, employment_status, expected_monthly_payments, estimated_annual_revenue_usd, high_risk_activities; the cards-endorsement-specific requirements; PII retention periods and rate limits. (Customers §12)
  3. Restricted-customer cap period. Is the restricted-US aggregate cap $100k lifetime or per 30-day — the individuals page and the add-requirements/DAO pages disagree. (Customers §12)
  4. Webhook signature hashing. Single (Ruby sample) vs double (Go/Python/Node/Java samples) SHA-256 — unresolvable from docs; needs a production delivery. Plus the real delivery timeout / retry-backoff schedule, and what triggers signing-key rotation. (Webhooks §11)
  5. VA / destination edge cases. Effect of a destination change on an already-in-flight deposit; whether a hard cap on VAs per customer per currency exists; whether a bounced/refunded VA deposit can be replayed by Bridge; how a VA/liquidation address transitions to deactivated (undocumented endpoint or support-only); when exactly kyc_link.customer_id populates; whether any API-visible flag marks a Bridge Wallet "prefunded" or it's purely out-of-band configuration; whether payment_route.type on bridge_wallet.activity events has a stable, complete value list. (VA §Open questions, Webhooks §11)
  6. Transfer / returns / rail shapes. Canonical destination_tx_hash vs destination_trx_hash; the awaiting_funds/awaiting_source_deposit state name; the fixed-output transfer request shape; the return_instructions object shape and settability; how external accounts are deactivated; whether fiat returns support non-USD + fees; whether SPEI/PIX/FPS/COP support the fiat-deposit-return flow; the funds-request (recall) schema/deadline; the ACH pull/debit not supported conditions. (Transfers §Open questions)
  7. Per-currency guide ambiguities. BRL Pix minimum (10 vs 20 BRL); COP account-type field name (co_bank_transfer vs the page's typo); the GBP VA sample's "currency":"brl" (bug or copy error); exact rail cutoff windows; whether a batch-payout endpoint exists or it's one transfer per payee; whether remittances support an FX/local-payout leg; the fixed-vs-flexible SEPA memo field name. (Guides §13)
  8. Cards (for the B5 design). Whether a current-generation PIN flow exists or only the legacy iframe (plus the complete card_account/Stripe-card status enum and freeze reason enum); deposit-address provisioning for current-gen cards; whether the legacy fees{}/payout mechanics still apply under Stripe Issuing (and the card_withdrawal/ posted_card_account_transaction payload shapes); wallet-to-card cardinality; whether Bridge/Stripe provides a fraud signal to consume instead of building new. (Cards §Open questions)
  9. Issuance (if ever relevant). Whether /issuance/reserves/liquidity_allocation targets one stablecoin per account or supports multi-coin; whether /v1/customers in the idempotency guide is a live second API version or a stale typo. (Webhooks §11)

Bridge realities that changed our design

Where a Bridge fact forced a design choice that a naive "swap the vendor" would have missed:

  • Virtual accounts are transfer-only → we did not model a fiat balance on a Bridge VA. Instead the neutral account model carries a per-account settlement mode (pass_through vs held) + a nominated destination, so a held-mode provider (Conduit today, Sovera later) still fits the same shape. A pass_through account never writes a balance projection and is excluded from the master-balance rollup (alignment §2).
  • The sandbox is schema-only (no money movement, no payment webhooks, no KYC links) → money-path behavior and the webhook signature scheme cannot be validated pre-production. The go-live plan requires a production canary tenant, not a sandbox sign-off (alignment §6).
  • The webhook signature-hash convention is ambiguous in Bridge's own samples (single vs double SHA-256) → BridgeSignatureValidator is built fail-closed. PHP's openssl_verify() on the raw payload hashes once internally, matching the Ruby sample only — 1 of Bridge's 5 sample conventions — which RAISES rather than lowers the go-live verification risk: the convention is an explicit go-live verification item against a real delivery, not an assumption baked into the validator (Webhooks §3).
  • features:sync is no-prune → the four-line go-live grant flip is not self-sufficient: run alone, every prior conduit/sumsub/utila grant survives as a permitted key and any unserved group keeps a stale conduit PRIMARY. The grant PR must add a reconcile-delete (or an explicit revoke step) — a constraint already recorded in D118 §Go-live and alignment §5.
  • Cards run on Stripe Issuing → B5 is not "wire up Bridge cards" but a new capability + module whose fraud, fee, and PIN surfaces are partly Stripe's; the digest flags several Stripe-vs-Bridge ambiguities the B5 design must resolve first (Cards).

Back to the Bridge cluster index · ← Engineering wiki.