Capabilities
What this account can execute right now, rail by rail, and public eligibility by country.
Jump to an operation:
What this account can execute right now, rail by rail — available
Status: Available · Callers: business key, agent (business key), dashboard session · Scope:
capabilities.read· Test mode: unavailable
One resource, five product projections (payment_links, payouts, payroll, buy_sell, wallet_bank — D-23, D-34), always caller-specific: every projection resolves the CALLING account's own verification facts (Bridge status, or the existing payouts eligibility computation) and reports what that account can execute. buy_sell also takes country, method and provider to narrow the coverage grid. Its defaults and coverage cells come from one fresh route snapshot; this endpoint does not quote, reserve a price, or promise a first quote. Call it before quoting or offering anything — it reports availability only and authorizes nothing; the server re-checks at create and at the money boundary (RESOURCE-MODEL §0.10). BL-60 (C4-D37): for product=payroll, funding_currencies[] additionally publishes, per fiat currency, whether Bridge can issue a FUNDING account for THIS account today — COP stays a selectable run currency (its bank_bridge payout corridor is live) even though it currently answers funding_account_available: false. Fix round 1 (#1/#3): a Bridge funding rail existing is not sufficient — an unverified account reads funding_account_available: false, reason: 'verification_required' for a currency whose own bank_bridge corridor (corridors[] above, same currency) has not itself resolved to available yet; corridors[] and funding_currencies[] answer different questions (paying a recipient in that currency vs. funding a run in it) and must never be read as the same fact. PAYROLL-CRYPTO-1: each funding_currencies[] entry also carries crypto_funding — whether a run in that currency can be funded with USDC sent to the employer's Bridge payroll wallet (and, when it can, which asset on which chain), gated by the same switch the funding service reads and never available while the currency itself cannot be funded. K6 correction: this operation requires a credential — an anonymous, account-free public claims projection (no entitlement, no route id, no flag, no preview row) is a DIFFERENT operation (a public corridor/rail grid with no caller identity behind it at all) and is not this one; the router has no optional-auth route class to serve it from yet. If a public projection is wanted, it should be specified and built as its own path, not folded into this operation's security. BL-40: for payment_links/payroll/wallet_bank, an account that already has a Bridge customer but whose live status read failed answers 503 temporarily_unavailable for the whole projection — never a fabricated not_started on every corridor. NB-5: a buy_sell answer with degraded: true means the route snapshot could not be obtained (unreachable or no fresh lease) — report that the check failed and offer a retry, never report that no route exists. A4-FIX-6 — Test mode: unavailable. Every projection resolves the calling account's own verification facts (Bridge status, payouts eligibility, the route snapshot), and a test-mode account has no such facts to answer from, so the router refuses a test-mode caller (503 temporarily_unavailable, side-effect free, no Retry-After) and the handler refuses it a second time before any read. It is never forwarded to the dev project. CP-T2: for product=payment_links, the crypto_tempo corridor carries crypto_only — whether this account can activate a crypto-only link (settled to its own Swaps Wallet) without any Bridge customer, and the first gate that blocks it. A read failure behind it answers 503 temporarily_unavailable for the whole projection. #3932: the crypto_relay corridor reads available only while the crypto_tempo corridor does — Relay delivers on the Tempo leg and the payer page never offers it otherwise; its max_amount (the per-invoice cap) is published even while it reads not_enabled.
query Parameters
productcountryproduct=buy_sell only — narrows the coverage grid to one country. This filter never resolves residency or personal KYC.
methodproduct=buy_sell only — narrows the coverage grid to one payment method.
providerproduct=buy_sell only — narrows the coverage grid to one provider.
Headers
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Swaps-AccountA1-2. Selects which of the caller's account memberships this request acts on, by Account.id (acct_…), when the credential can reach more than one; the caller's default account otherwise (D-109, supabase/functions/api-v1/auth.ts). Declared on every operation a dashboard session can call (#3561: the runtime caller class, not the formal security list, which names only businessKey on operations both credential classes reach). A value the session is not a member of answers 404 not_found. A business key already resolves to exactly one account (its own) and ignores this header.
What this account can execute right now, rail by rail — available › Responses
The capability snapshot for this product — public or authenticated projection depending on the credential presented.
versionShape varies by product: payouts publishes {kind, bps, applies_to}; payment_links publishes {kind, bps, applies_to, rails, rounding, rounding_decimals}; payroll publishes {bps, shape, enabled} (employer-funded-on-top, never a percentage deducted from the recipient — §2.5). All keys are optional here to cover every shape.
payment_links — the Swaps fee on the splitter rails named in rails (crypto_tempo, crypto_relay), the same constant the server charges: applies_to: payer — added on top of the invoice and paid by the payer; the merchant receives the full invoice. For an invoice of amount, the payer's deposit instructions charge fee = floor(amount × 10^rounding_decimals × bps / 10000) / 10^rounding_decimals (a USD invoice with at most two decimals is never rounded); Payment.fee and Payment.amount_expected on these rails publish the same figures at that scale. It applies to crypto subscriptions too: every subscription invoice is a crypto-only payment link paid on these rails. A rail not listed in rails carries no fee claim from this object (no rate, no bearer); the fee on a Bridge-routed rail (bank rails, crypto_bridge) is deducted from what the merchant receives and is published separately as deducted_fee. Provider fees (Relay relay/gas, Bridge network/processing) are not this fee and are quoted per payment. Present whatever the rails' current availability (corridors[]).
product=payment_links only — the Swaps fee on the Bridge-routed rails named in rails (bank rails and crypto_bridge): applies_to: merchant — the payer pays exactly the invoice and the provider deducts this percentage from what arrives, so the merchant receives what arrived minus it (founder ruling «Bank: 1% is deducted from what you receive»). This is the rate configured now; the figure a payment was charged, to the cent and rounded up, is that payment's own Payment.deducted_fee, read from the provider receipt (null there when the payer paid in another currency than the invoice — crypto_bridge's USDC source included — or no consistent receipt was recorded). Omitted when no fee is configured.
product=payouts only — the «Pay with» sources for this caller: external_wallet (USDC from any wallet, always available) and swaps_wallet («Wallet balance», coming_soon while its kill switch is off; unavailable to a business key and in test mode).
product=buy_sell only — the resolved first-load defaults (exchange_bootstrap, D-23, BS-G4).
product=buy_sell only — the country × method × provider availability grid (D-23, BS-G5).
product=wallet_bank only — the currency → endorsement → status map (§2.3 v2 amendments).
product=payroll only (BL-60, C4-D37) — for each payroll fiat currency (the SAME set supabase/functions/payroll/lib.ts's FIAT_CURRENCIES accepts as a run currency), whether Bridge can issue a FUNDING account for it today, with a reason when it cannot. Additive: the run-currency picker keeps every fiat currency (the founder rejected removing COP — a LIVE bank_bridge/co_bank_transfer payout corridor, see corridors above — over dropping it just because it has no Bridge funding rail yet) and now reads this array to warn honestly up front instead of the gap surfacing only after a run is created. funding_account_available is the bank-transfer method; crypto_funding (PAYROLL-CRYPTO-1) is the USDC method for the same currency, and its absence means the USDC method is not available.
product=payment_links only (§52.33, PL-TEMPO-BRIDGE-1-T) — for each link fiat currency (the same currencies the bank corridors above cover), whether a link in that currency can settle its Tempo-wallet destination and through which rail. An entry without tempo_wallet.available: true reads as not available for that currency today.
product=wallet_bank only — the V1 ceiling ($3,000, §2.3 v2 amendments).
degradedproduct=buy_sell only — true when this read is not backed by a fresh route snapshot: the upstream hop (best-quote) was unreachable or itself had no fresh route-truth lease (NB-5). providers[] is still a live, independent read; defaults/coverage are OMITTED — never an empty grid or a fabricated "nothing is available" claim, and never a permanent 503 for a condition that cannot change.
degraded_reasonPresent only when degraded is true. Reserved: unavailable_in_test_mode is never emitted since A4-FIX-6; a test-mode caller gets 503.
Which products this country and customer type can likely use — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: public token · Test mode: unavailable
This operation is dark-flag — live behind a cohort or flag. While its resource switch (
api_v1.<resource>) is off, calling it answers503 temporarily_unavailable; with the switch on, a closed product or launch flag, or a cohort the account is outside, answers409 capability_unavailable. Check its product page before relying on it.
The compliance-navigator engine's own four statuses, verbatim — never collapse likely_available and conditional into one available (RESOURCE-MODEL §2.4). PUBLIC and UNAUTHENTICATED (L3-2): resolved off the router's public-token table with no caller identity at all (RESOURCE-MODEL §0.9), the same class payment_sessions.get uses. The public projection takes only country + customer_type. This PR drops monthly_volume_band/purpose/regulated_activities from this operation ENTIRELY, not just to "authenticated callers only": D-16 already forbids them on any anonymous path, this operation has no other path, and capabilities.get's own description already names the structural reason an authenticated variant can't be folded into this one's security — "the router has no optional-auth route class to serve it from yet... it should be specified and built as its own path." A richer, authenticated eligibility projection accepting those three fields is a real future operation once that route class exists; it is not this one. payroll is one of the five published products (the compliance-navigator engine's exchange/wallet/payment_links/ payouts/payroll — NOT /v1/capabilities's unrelated payment_links|payouts|payroll|buy_sell|wallet_bank product enum, a different question for a different, authenticated caller). limits is populated only for payouts, and only for a country whose currency AND whose payout rail this catalog actually covers (a real per-currency corridor minimum, published as Money — fix round 1, P1: a currency match alone is not a country match, e.g. Zimbabwe uses USD but has no US-domestic ACH/wire rail; fix round 2, P2 corrected the EUR corridor's country scope again — Monaco/San Marino/Vatican City are EUR-currency SEPA members the EEA-only gate wrongly excluded — every other product omits limits rather than publish an approximate or fabricated figure, and limits itself never carries a max: no producer publishes one today (fix round 2, P3). limits.confidence is exact only for a hard-rule match (EEA membership, or country === 'US'); Monaco/San Marino/Vatican City's match is this operation's OWN widening onto a rail with no country dimension in its payout SSOT, so it reads approximate there, never the same exact an EEA country gets for the identical eur_sepa corridor. requires_bridge_verification (fix round 1, P1 as requires_verification; renamed fix round 2, P2 — the bare name over-claimed for exchange, whose false means only "no Bridge customer needed," not "no KYC anywhere") marks the three products gated on a Bridge customer/endorsement the caller may not have yet (payment_links/payouts/payroll) — those three also lead reasons[] with bridge_customer_missing and, for an EEA country, add eea_verification_required to both reasons[] and blockers[]; exchange also publishes eea_verification_required in reasons[] for an EEA country as of fix round 2, P3 (matching the canon engine's own code for that branch), even though it needs no Bridge customer and so never gets a blockers[] entry for it; never conflate a Bridge-gated likely_available with wallet's true, unconditional one. notes[] carries the fact reasons[]' shared code cannot: exchange always publishes dex_no_kyc + fiat_provider_kyc, and adds eea_assets for an EEA country — the MiCA asset restriction its eea_verification_required reason code alone would otherwise conflate with the other three products' real Bridge-verification gate. disclaimers[] (fix round 1, P2) mirrors the engine's own hedging (not_advice always; approximate/eea/sanctioned/unknown_country per branch, spelling corrected fix round 2, P3) — this is the one operation whose x-swaps-status: dark-flag exists because public capability claims need the founder's legal sign-off, so the claims never ship without the hedges that go with them everywhere else this engine speaks. x-swaps-test-mode: unavailable (A1-3, 2026-09-22): every available/dark-flag operation in this document was corrected to unavailable because K11's cross-project test-mode dispatch does not exist yet (apps/docs/pages/test-mode.mdx) — this operation is a strong first K11 candidate to restore, since it takes no token/key of any kind (security: []) and so has no sk_test_/live distinction to make, unlike its two public-token siblings (payment_sessions.get/payroll_recipient_sessions.get, fix round 1, P3 note) whose resolved session DOES belong to a livemode-bearing resource, but the claim stays unavailable here too until K11 names it in its evidence list with its own test, exactly like every other operation below. CORS: the 200 response carries a wildcard Access-Control-Allow-Origin: * for a header-free cross-origin GET (fix round 1, P2) — this is narrower than "any browser page can call this" (corrected fix round 2, P2): a caller that sends a custom request header (e.g. Swaps-Version) triggers a CORS preflight this router does not yet answer with a public wildcard, and every refusal this operation can return (400s, and the 503 while its flag row is unseeded) still carries an origin-specific ACAO. Fixing both needs a router-level change outside this operation's own file; tracked as a follow-up, not attempted here.
query Parameters
country^[A-Za-z]{2}$ · requiredISO 3166-1 alpha-2 country code, matched against this exact pattern server-side — no leading/trailing whitespace is trimmed (fix round 1, P3: a previous handler bug trimmed before validating, accepting an input this published pattern refuses).
customer_typeHeaders
Swaps-VersionPins behaviour within /v1 to a dated version; defaults to the key's version; echoed on every response.
Which products this country and customer type can likely use — dark-flag › Responses
The engine's answer for this country and customer type.
countryThe normalized (upper-cased) country the caller passed.
customer_typedisclaimersMachine-readable disclaimer codes (fix round 1, P2) — mirrors resolveEligibility's own disclaimerKeys (src/lib/eligibility/engine.ts), stripped of the complianceNav.disclaimer. i18n prefix. not_advice is present on every response; approximate when any product's confidence is approximate; eea for an EEA country; sanctioned/unknown_country replace the other three for those two hard-block branches (fix round 2, P3 corrected the spelling — round 1 shipped the reversed country_unknown, which is actually the SEPARATE reasons[] code this same branch publishes; disclaimers[] and reasons[] are deliberately different vocabularies and this was their one accidental collision). Never omitted — an empty array would itself be a false "no caveats" claim on the operation this dark-flag legal sign-off exists to gate.