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'simplementslist,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):
- 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. - Bridge wallet — a Bridge-custodied stablecoin wallet, keyed by
bridge_wallet_id(never the raw on-chain address). - Virtual account — the fiat-in primitive: a permanent, customer-specific deposit address.
- Transfer (with external accounts / liquidation addresses as source/destination
references) — the movement primitive. One
POST /transfersobject backs payouts, conversion orders, and custody withdrawals; itssource/destinationpair references any combination of a bank rail, a chain address, orbridge_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) vsheld(Conduit/Sovera — balance retained). Apass_throughaccount carries a nominateddestinationand 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 offrom_address/external_account_id/bridge_wallet_id). The adapter throwsProviderValidationException('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
| Tag | Meaning |
|---|---|
| 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 §n | Designed but not built — the Bridge domain-alignment design §n (its PRs A/B/C). |
| PROGRAMME Bn/Sn | A tracked coverage-programme item — D118 §Follow-ups / router tracker §7. |
| NOT APPLICABLE | No Stables product surface consumes this Bridge mechanic. |
| OPEN QUESTION | Blocked 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 flow | What Bridge does | Our journey / module | Status | Notes → digest |
|---|---|---|---|---|
| Individual onboarding (KYC Links / Customers API) | Hosts a Persona-backed identity + document capture behind a generated link; or a direct Customers-API create | onboarding — SubmitOnboardingApplicationCommand → OnboardsCustomers/VerifiesIdentity; BridgeRailsProvider implements both | SERVED (D118) via KYC Links | Adapter uses POST /kyc_links, not the Customers API; documents are never forwarded (Bridge collects them). Customers §2 |
| KYB + UBO / associated persons | Business onboarding with beneficial-owner graph | onboarding — VerifiesBusinesses + FetchesVerification; BridgeRailsProvider implements both | SERVED (D118) via KYC Links | Full Customers-API KYB payload (D117's pinned enums) deferred — D118 item 2. Customers §4 |
| Endorsements per rail | Per-rail approvals gating which rails a customer may use | onboarding — endorsements[] on kyc_links.create; requirement machine codes → application status | SERVED (D118) (submit-time) | "Add an endorsement to an existing customer" endpoint is an OPEN QUESTION. Customers §6 |
| Terms-of-Service acceptance ordering | ToS acceptance gates activation; approved KYC + pending ToS stays pending | onboarding — application status = map(kyc_status, tos_status) via BridgeStatusMap | SERVED (D118) | An approved KYC with pending ToS stays Processing. Customers §3 |
| Customer status lifecycle (non-linear) | Status transitions (can move backward: active → under_review) | onboarding — ApplicationStatus via BridgeStatusMap; customer.* webhook → mirror | SERVED (D118) (mapping) + PLANNED §3 (handlers) | Webhook domain handlers (BridgeCustomerUpdatedHandler) not built yet. Customers §5 |
| High-risk / prohibited activities | Prohibits/high-risk-flags certain business activities | onboarding — SubmitActivityDeclarationCommand + onboarding_activity_declarations (D117) + provider-support matrix | SERVED (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 requirements | Per-country ID/PoA/formation-doc requirements | onboarding (hosted flow collects docs) | SERVED (D118) via hosted flow | Documents collected by Bridge's flow, not forwarded by us. Customers §8 |
| Restricted-customer caps / supported & prohibited countries | Aggregate caps on restricted US customers; country allow/deny lists | features jurisdictions + onboarding gating | OPEN QUESTION (cap period) / informational | Lifetime-vs-30-day cap ambiguity is an open question. Customers §10–11 |
Accounts, virtual accounts & fees
| Bridge flow | What Bridge does | Our journey / module | Status | Notes → digest |
|---|---|---|---|---|
| VA create + transfer-only auto-convert | Fiat → stablecoin on arrival, forwarded to the VA destination; no fiat balance | accounts — virtual_accounts mirror (today mirror-only: UpsertVirtualAccountMirrorCommand; no client create command exists yet) | PLANNED §2 | CreateVirtualAccountCommand + SettlementMode/SettlementDestination are new (not built — verified). VA §1–2 |
| VA destination nomination / mutation | Destination settable at create and mutable via PUT ("future transactions use the updated destination") | accounts — UpdateVirtualAccountDestinationCommand (step-up MFA + approval-quorum consensus) | PLANNED §2 | Re-nomination is money-sensitive: fresh step-up and quorum (engine's 3rd consumer). VA §3 |
| Multiple VAs per currency; deactivate / reactivate | Several VAs per currency; activate/deactivate | accounts — UpsertVirtualAccountMirrorCommand on activation/deactivation events | PLANNED §3 | Handler BridgeVirtualAccountActivityHandler routes lifecycle events to the mirror. VA §4 |
VA deposit events + deposit_id reconciliation keys | virtual_account.activity events, one deposit grouped by deposit_id, per-rail keys | funding — UpsertTransactionMirrorCommand (exists); Bridge activity handler | SERVED (D118) (mirror cmd) + PLANNED §3 (handler) | One transactions row per deposit_id; new fee/hash columns planned. VA §6 |
| Developer fees & minimums | Configurable developer fee %, per-rail minimums | accounts/payments — developerFeePercent on the VA spec; fee columns on transactions | PLANNED §2 / §3 | No fee-config surface today; fee fields promoted from receipt.*. Transfers §13 |
| Rail-specific limits / thresholds | Per-rail min/max, third-party P2P ACH caps | rails.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 flow | What Bridge does | Our journey / module | Status | Notes → digest |
|---|---|---|---|---|
| Transfer object + 13 states (payout / order legs) | One POST /transfers object backs payouts and conversion orders; 13-state lifecycle | payments — InitiatePayoutCommand (ManagesPayouts, FiatRails), CreateOrderCommand/CancelOrderCommand (ManagesOrders, Conversion); TransferMapper/TransferPayloadMapper | SERVED (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 transaction | payments/funding — ManagesTransactions::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 Payee | Bank accounts as payout destinations; VoP name-check | payments — AddWhitelistRecipientCommand → ManagesWhitelistRecipients; ExternalAccountMapper | SERVED (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 accounts | Onboard a bank account via Plaid | payments — extension of whitelist-recipient registration | PROGRAMME B6 | Not in the D118 adapter. Transfers §11 |
| Fiat / crypto returns + return policies | fiat_deposit_return, crypto returns, return_instructions/return-policy requirement | payments/compliance — no returns lifecycle today | PROGRAMME 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 lifecycle | payments — no recall surface today | PROGRAMME B8 | Response schema/deadline semantics are an open question. Transfers §10 |
| Static / recurring template transfers | Reusable transfer templates for recurring movement | — no recurring-transfer product surface | NOT APPLICABLE | No Stables recurring-payout journey; revisit if one is built. Transfers §5 |
| Payment-tracking identifiers & receipts | receipt.*, tracking ids, payment_received_rail | payments/funding — transaction mirror rows | PLANNED §3 | Persist payment_received_rail (cleared rail), not just the requested rail. Transfers §12 |
| Cutoffs, settlement windows, holidays | Per-rail processing cutoffs and bank holidays | — settlement reflected asynchronously via transfer status | NOT 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 flow | What Bridge does | Our journey / module | Status | Notes → digest |
|---|---|---|---|---|
| Bridge custodial wallets (create/get/list) | Single-key, platform-controlled stablecoin wallets per customer/chain | accounts — CreateWalletCommand → ManagesWallets; WalletMapper; BalanceProjector | SERVED (D118) | Wallets require Bridge L&C approval + carry geo exclusions (go-live precondition). VA §11 |
| Custody transfers (withdrawals) | A transfer sourced from bridge_wallet | accounts/custody-controls — ManagesCustodyTransactions::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::rotateWallet | STUBBED (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) | payments — RegisterAddressCommand → ManagesRegisteredAddresses (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 D118 | OPEN QUESTION / not modelled | Flagged for the custody forward; not in adapter scope. VA §13 |
bridge_wallet.activity events + payment_route join | Custody-side view of a VA deposit landing; payment_route.deposit_id join key | accounts — BridgeWalletActivityHandler → RefreshWalletBalancesCommand | PLANNED §3 | Stamps 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 today | PROGRAMME B1 | New ManagesLiquidationAddresses capability + mirror + client API. VA §8–9 |
| Master balance / FX rates | (Bridge exchangeRates — live FX quotes) | treasury — GetMasterBalanceQuery; ExchangeRateProvider (table-less; ConfiguredExchangeRateProvider today) | PROGRAMME B3 | Bridge-backed ExchangeRateProvider replaces the config provider. Guides §5 |
Compliance, travel rule & platform mechanics
| Bridge flow | What Bridge does | Our journey / module | Status | Notes → digest |
|---|---|---|---|---|
| Travel rule | /travel_rule surface for on-chain legs (EEA: no threshold; US: > $3,000) | funding — SubmitSenderInformationCommand (ManagesTransactions::submitSenderInformation) | STUBBED (D118) → PROGRAMME B2 | Bridge has no inbound-deposit RFI; a new SubmitsTravelRule capability closes the gap. Guides §9 |
| Webhooks — signature / retry / replay | Asymmetric-signed (X-Webhook-Signature: t=…,v0=…, RSA + SHA-256 over "<t>.<rawBody>"), retried on any non-2xx | webhooks — BridgeSignatureValidator + BridgeTenantResolver (registered, D118) | SERVED (D118) (ingress) + PLANNED §3 (handlers) | Signed/deduped/stored/acked with zero domain handlers wired today. Webhooks §2–4 |
| Signature hashing convention | Bridge's own samples disagree (single vs double SHA-256) | BridgeSignatureValidator | OPEN QUESTION | Unresolvable 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/precision | bridge-sdk + Support\BridgeIdempotencyKey::normalize(); DTOs keep money/timestamps as strings | SERVED (D118) | Bridge is the first provider needing key normalisation (SHA-256 when > 64). Webhooks §6 |
| Sandbox | Schema-only: no money movement, no payment webhooks, no KYC links | operational precondition — production canary before the flip | PLANNED §6 (operational) | Money-path + signature scheme can only be validated in production. Guides §11 |
Cards & issuance
| Bridge flow | What Bridge does | Our journey / module | Status | Notes → 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 B5 | Largest 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 issuer | NOT APPLICABLE | Webhooks §8 |
| Rewards | Reward-programme summaries | treasury/reporting | PROGRAMME B10 | Lowest-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").
- 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)
- Onboarding shape gaps. Correct field for a business's beneficial owners at create
(
ultimate_beneficial_owners[]vsassociated_persons[]); whether a standalone "add an endorsement to an existing customer" endpoint exists; whether ToS/KYC links expire; the full enum values forbusiness_type,account_purpose,source_of_funds,employment_status,expected_monthly_payments,estimated_annual_revenue_usd,high_risk_activities; thecards-endorsement-specific requirements; PII retention periods and rate limits. (Customers §12) - 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)
- 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)
- 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 exactlykyc_link.customer_idpopulates; whether any API-visible flag marks a Bridge Wallet "prefunded" or it's purely out-of-band configuration; whetherpayment_route.typeonbridge_wallet.activityevents has a stable, complete value list. (VA §Open questions, Webhooks §11) - Transfer / returns / rail shapes. Canonical
destination_tx_hashvsdestination_trx_hash; theawaiting_funds/awaiting_source_depositstate name; the fixed-output transfer request shape; thereturn_instructionsobject 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; theACH pull/debit not supportedconditions. (Transfers §Open questions) - Per-currency guide ambiguities. BRL Pix minimum (10 vs 20 BRL); COP account-type field name
(
co_bank_transfervs 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) - 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 freezereasonenum); deposit-address provisioning for current-gen cards; whether the legacyfees{}/payout mechanics still apply under Stripe Issuing (and thecard_withdrawal/posted_card_account_transactionpayload shapes); wallet-to-card cardinality; whether Bridge/Stripe provides a fraud signal to consume instead of building new. (Cards §Open questions) - Issuance (if ever relevant). Whether
/issuance/reserves/liquidity_allocationtargets one stablecoin per account or supports multi-coin; whether/v1/customersin 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_throughvsheld) + a nominated destination, so aheld-mode provider (Conduit today, Sovera later) still fits the same shape. Apass_throughaccount 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) →
BridgeSignatureValidatoris built fail-closed. PHP'sopenssl_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:syncis no-prune → the four-line go-live grant flip is not self-sufficient: run alone, every priorconduit/sumsub/utilagrant survives as a permitted key and any unserved group keeps a staleconduitPRIMARY. 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.