Providers & coverage
Coverage is a resource, not a claim on a marketing page. GET /v1/capabilities?product=… answers "can this account do X" — read it instead of hard-coding an assumption about a corridor, a country or a payment method. A second endpoint, GET /v1/eligibility, is specified for "could an account in this country generally do X", but it is closed in production (see Eligibility below).
Capabilities — what this account can do right now
GET /v1/capabilities?product=payment_links|payouts|payroll|buy_sell|wallet_bank returns, per product, the corridors or rails this specific authenticated account is actually eligible for — not a static list of everything Swaps theoretically supports.
Every corridor row states its own status honestly rather than a blanket "supported":
| Field | Meaning |
|---|---|
lifecycle | public (advertised), preview (soft-launched), or blocked |
public_claim_status | live, unverified, or planned — the honest gap between what's claimed and what's been probed |
executable | whether this account can execute today — a capability read is availability, not a green light to skip re-checking at the money boundary |
blocked_reason | present whenever executable is false, so an agent has something to act on instead of a bare no |
advisory | a fact to tell the person about the corridor's bank accounts, whatever its status — never a block, and absent when there is nothing to say |
blocked_reason is an open enum: a client that meets a value it does not know treats the corridor as not executable and shows the reason as text. The values below name an environment or account fact rather than a missing provider. On the wallet_bank_virtual_account corridor every reason applies in one order, the order POST /v1/wallet/virtual_accounts refuses by: owner_only (the caller is not the account's owner), rail_not_enabled (receive by bank is not launched for the account), verification_closed, payin_fiat_refused, endorsement_not_requestable, route_unavailable (the account's Swaps Wallet is not on Tempo mainnet), collection_account_uses_currency, then a verification step with no reason, then payin_fiat_pending (an individual account whose provider fiat pay-in activation could not be read), then wallet_not_found:
blocked_reason | Where | What it says |
|---|---|---|
relay_requires_tempo_mainnet | product=payment_links, the crypto_relay corridor | The deployment settles on a Tempo test network (or the network cannot be resolved), and the payer page admits Relay only on Tempo mainnet. The corridor reads not_enabled with source_chains: [] whatever the Relay switch says. max_amount is still published while the per-invoice cap is set. |
payin_fiat_pending | product=payment_links, its bank corridors | Only for an individual account whose provider customer carries no endorsement record and whose fiat pay-in activation is not active: the corridor reads gathering_no_path, a wait on the provider with no date. When the customer carries endorsements, each bank corridor follows its own endorsement instead (an approved sepa reads available), the same rule the payer's payment follows. |
collection_account_uses_currency | product=wallet_bank, the wallet_bank_virtual_account corridor | A Payment links collection account already receives this currency into the same wallet, so a new wallet bank account would only hand that one back. The corridor reads not_enabled with action: none. POST /v1/wallet/virtual_accounts for that currency answers 409 conflict. |
wallet_not_found | product=wallet_bank, the wallet_bank_virtual_account corridor | Receive by bank is open to the account, but it has no Swaps Wallet yet, so a bank deposit would have nowhere to settle. The corridor reads not_enabled with action: none: create the wallet first (POST /v1/wallet/wallets). It is the last reason: a person the provider has closed, or who still has to verify, reads that first. POST /v1/wallet/virtual_accounts answers 409 wallet_not_found for the same case. |
owner_only | product=wallet_bank, the wallet_bank_virtual_account corridor | The caller is a member signed in as themselves who is not the account's owner, and only the owner opens receive by bank. Every currency reads not_enabled with action: none, first in the order. POST /v1/wallet/virtual_accounts refuses them before anything else: a member with 403 role_denied (the operation's role gate), an admin who is not the owner with 403 scope_denied. |
verification_closed | product=wallet_bank, the wallet_bank_virtual_account corridor | Verifying again cannot open it: the provider has given its final answer on the account holder — a final rejection (rejection_outcome: terminal on the customer), or a paused or offboarded customer — or verifying is the holder's next step and it has used every verification attempt (verification_attempts_exhausted: true on the customer), so POST /v1/customers/{id}/verification_links mints no further link and support is the next step; that case carries detail_code: verification_attempts_exhausted. Every currency reads not_enabled with action: none. POST /v1/wallet/virtual_accounts answers 409 capability_unavailable with the same details.blocked_reason and details.detail_code. |
payin_fiat_refused | product=wallet_bank, the wallet_bank_virtual_account corridor | The provider reports the account holder's fiat pay-ins as inactive or rejected (an individual account): its answer, not a wait. Every currency reads not_enabled with action: none. POST /v1/wallet/virtual_accounts answers 409 capability_unavailable with the same details.blocked_reason, and refuses the same way when the provider's own record, read again at the create, reports it. A pending activation does not block the wallet's bank account. |
endorsement_not_requestable | product=wallet_bank, the wallet_bank_virtual_account corridor | This currency needs its own provider endorsement (USD base, EUR sepa, MXN spei) that the account holder's provider customer does not carry, and the API cannot request one today — whatever the verification state, so verifying does not open it. That currency reads not_enabled with action: none. POST /v1/wallet/virtual_accounts answers 409 capability_unavailable with the same details.blocked_reason. |
advisory is a fact to tell the person, never a block. Today it appears only on the wallet_bank_virtual_account corridor, as payin_fiat_pending: the account holder is an individual whose provider fiat pay-in activation is still pending, so the provider may review a deposit into any of the holder's wallet bank accounts and return it. It is a fact about the holder, not about whether a new account can be created (status says that), so every currency's corridor of a launched account carries it, whatever its status — a closed one too, because accounts already issued, including those of a previous wallet, keep receiving. The owner_only corridor never carries it: the accounts that caller holds are their own, not the owner's. Say so before the person shares the bank details of any account in that currency, including one created earlier. It is an open enum: a client tolerates a value it does not know.
detail_code names which of its reason's sentences detail carries, where a client must tell them apart. Today only verification_attempts_exhausted, on verification_closed: offer support, never a retry. It is an open enum.
For product=payment_links the answer also carries fee, the Swaps fee the payer is charged on top of the invoice (you receive the full invoice), read from the same constant the charge uses: kind: "percentage", bps: 100, applies_to: "payer", rails: ["crypto_tempo", "crypto_relay"], rounding (floor) and rounding_decimals (6). A rail that is not in rails carries no fee claim from this object. The fee is a price, so it is published even while those corridors read not_enabled. See Payment links for what the payer is asked for.
verification_checked_at says when the account's verification facts behind the corridors were checked. With verification=snapshot they come from Swaps' stored check while it is no more than 24 hours old (no provider call); an older one is read live, and a live read that cannot reach the provider answers 503 temporarily_unavailable rather than corridors from the older status. The default, live, reads the provider on every request. Use snapshot only to display corridors — see Customers.
There is also an unauthenticated public projection of capabilities (product-wide fee and corridor claims with no entitlement or per-account detail) for anyone building a comparison surface before a user signs up.
Availability is not permission
A capability answering executable: true is not authorization to move money. The server re-checks eligibility at create time and again at the money boundary (funding, activation, execution) — routing output is never chained straight into execution. See Agents & MCP → what a tool can never do.
Eligibility — what a country and account type could generally expect
GET /v1/eligibility is dark-flag and closed in production: it answers 503 temporarily_unavailable, and it stays closed until the public claims it makes are signed off. Do not build on it; for an authenticated account, read GET /v1/capabilities?product=… instead. What follows is the contract it will carry once it opens.
GET /v1/eligibility (public: country + customer type) answers a coarser, pre-signup question — "if someone in this country opened this kind of account, what would likely work" — using the same eligibility engine the dashboard's own onboarding reads, with its own four honest states:
likely_availableconditionalunavailableunknown
likely_available and conditional are never collapsed into one "available" bucket — an agent told a flat "available" for something the engine only called likely would tell an end user something the engine itself won't promise.
Providers behind a corridor
A capability row can name the provider handling a corridor (display_label, logo_url where relevant) — the point of the resource is honesty about whether a corridor works, not brand attribution. Swaps fans a request out across multiple connected providers per product (banking rails, on/off-ramp providers, DEX and CEX liquidity) and returns the best executable route; which provider actually serviced a given order is visible on that order's own record, not guessed from the corridor list.
Keeping this honest
Coverage claims drift when a provider's own availability changes faster than a document does. The KPI page tracks K12 — provider truth: capability rows whose claimed status disagrees with the last live probe, target zero stale claims older than 24 hours. If a corridor this page or the /v1/capabilities response describes turns out wrong, that is a defect against K12, not a documentation nuance.
Next: Products for what each product actually does with a resolved capability · Agents & MCP for how a tool reads this same resource.