Wallet
The holder's non-custodial Tempo wallet — balances, deposits, sends, conversions, bank details. Nothing here can sign.
Jump to an operation:
- GET /wallet/wallets
- POST /wallet/wallets
- GET /wallet/wallets/{id}
- GET /wallet/balances
- GET /wallet/transactions
- GET /wallet/transactions/{hash}
- GET /wallet/deposit_routes
- POST /wallet/deposit_quotes
- GET /wallet/deposit_intents
- POST /wallet/deposit_intents
- GET /wallet/deposit_intents/{id}
- GET /wallet/send_routes
- GET /wallet/send_intents
- POST /wallet/send_intents
- GET /wallet/send_intents/{id}
- POST /wallet/send_intents/{id}/source_tx
- POST /wallet/offramp_quotes
- GET /wallet/external_accounts
- POST /wallet/external_accounts
- GET /wallet/offramp_intents
- POST /wallet/offramp_intents
- GET /wallet/offramp_intents/{id}
- POST /wallet/offramp_intents/{id}/cancel
- GET /wallet/virtual_accounts
- POST /wallet/virtual_accounts
- GET /wallet/virtual_accounts/{id}
- POST /wallet/virtual_accounts/{id}/deactivate
- POST /wallet/virtual_accounts/{id}/reactivate
- GET /wallet/virtual_accounts/{id}/history
- POST /wallet/conversions
- GET /wallet/conversions/{id}
- POST /wallet/conversions/{id}/source_tx
- GET /wallet/conversion_pairs
List the holder's wallets — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
List the caller's own non-custodial Tempo wallets. Business-key callers may read but never create or sync one (RESOURCE-MODEL §2.3). Only wallets on this environment's Tempo network are listed (Wallet.network); when the environment cannot resolve its network the answer is 503 temporarily_unavailable, never a list across networks.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
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.
List the holder's wallets — available › Responses
A page of the caller's wallets.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Create or sync the holder's wallet — available
Status: Available · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable
Creates the holder's Tempo wallet on first use, or re-syncs it on reconnect. A second create while one is already active answers 409 conflict (wallet_already_exists), matching prod (R13-wallet §1 wallet-gate.html). Business-key callers cannot call this — they may only read (RESOURCE-MODEL §2.3). The wallet is always on this environment's Tempo network (Wallet.network): an already-active wallet found on another network is never returned, the answer is 409 conflict (wallet_already_exists).
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Create or sync the holder's wallet — available › Request Body
addressThe Tempo address the client derived from its passkey credential. Case-preserved verbatim.
credential_idThe WebAuthn credential id already registered via the passkey key-manager, when more than one exists for this holder.
labelCreate or sync the holder's wallet — available › Responses
The wallet, created or synced.
id^wlt_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectaddressChecksum-cased Tempo address. Never lower-cased on read or write.
chainThe chain this wallet lives on. Today always Tempo.
networkThe Tempo network this wallet lives on, from the wallet record. tempo-moderato is Tempo's test network: what it holds is test money (pathUSD), never real funds. A project serves only wallets on its own network, so every wallet one environment returns carries the same value, and it is the same value WalletBalances.network reports. Use it to pick the token a balance, send or signature is about; never infer the network from the environment's name or a client build setting. chain stays tempo.
statusRESOURCE-MODEL §2.3 names only one live state.
labelGet one wallet — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
Read one of the caller's own wallets. A wallet on another Tempo network than this environment's reads as 404 not_found; an environment that cannot resolve its network answers 503 temporarily_unavailable.
path Parameters
id^wlt_ · requiredHeaders
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.
Get one wallet — available › Responses
The wallet.
id^wlt_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectaddressChecksum-cased Tempo address. Never lower-cased on read or write.
chainThe chain this wallet lives on. Today always Tempo.
networkThe Tempo network this wallet lives on, from the wallet record. tempo-moderato is Tempo's test network: what it holds is test money (pathUSD), never real funds. A project serves only wallets on its own network, so every wallet one environment returns carries the same value, and it is the same value WalletBalances.network reports. Use it to pick the token a balance, send or signature is about; never infer the network from the environment's name or a client build setting. chain stays tempo.
statusRESOURCE-MODEL §2.3 names only one live state.
labelGet the holder's wallet balances — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
Every stablecoin held on the caller's own wallet, plus a server-computed usd_total. The server resolves the caller's own wallet address server-side; a client-supplied address is never accepted, here or on any wallet read (K4 prerequisite — RESOURCE-MODEL §0.12, §2.3 D-9). A thrown error must never be read as a $0.00 balance. Only a wallet on this environment's Tempo network is read, and network names it; an environment that cannot resolve its network answers 503 temporarily_unavailable.
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.
Get the holder's wallet balances — available › Responses
The caller's wallet balances.
chain_idThe chain this balance snapshot was read from. Today always Tempo.
networkThe Tempo network these balances were read on: the caller's wallet network (Wallet.network), or, when the caller has no wallet yet, the network this environment serves. tempo-moderato is Tempo's test network, so its balances (pathUSD) are test money. The primary token is USDC.e on tempo-mainnet and pathUSD on tempo-moderato; a client decides which balance can pay from this field, never from its own build.
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
skipped_non_usdToken symbols excluded from usd_total because their decimals are not 6 — named so a client never reads the total as complete.
List recent wallet transfers — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
A bounded recent window of on-chain transfers for the caller's own wallet — never full history, and never paginated (A1-6: this operation carried a cursor parameter that the handler silently ignored; dropped here rather than wired up, since WalletTransactionList is a fixed window, not a cursor-list — transfers[] stays the array name, not data, since packages/sdk-ts and claude/dashboard-v2 already key off it). The server resolves the caller's own wallet address; a client-supplied address is never accepted (K4 prerequisite — RESOURCE-MODEL §0.12, §2.3). degraded: true with an empty transfers[] means the chain read failed and must never be read as "no activity"; offer a retry instead.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
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.
List recent wallet transfers — available › Responses
A bounded window of the caller's wallet transfers.
chain_iddegradeddegraded_reasonGet one wallet transaction — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
Read one on-chain transaction by hash, scoped to the caller's own wallet. A transaction the wallet sent (receipt from) is out. One it did not send is read from the receipt's decoded TIP-20 Transfer logs of the accepted Swaps-dollar tokens, because a token transfer's receipt to is the token contract: a transfer to the wallet is in (checked first), one from it out, and amount, symbol and the counterparty come from that log when it is the only Transfer touching the wallet. A real hash that touches neither is 404; a hash with no receipt is status: not_found.
path Parameters
hashHeaders
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.
Get one wallet transaction — available › Responses
The transaction.
hashchain_idstatusnot_found is a 200 body naming the lookup result, not itself an HTTP 404 — RESOURCE-MODEL §2.3 names it as a state of this resource.
fromtoPresent only on a transaction the wallet did not send whose decoded TIP-20 Transfer logs of the accepted tokens hold exactly one transfer touching the wallet; absent on a transaction the wallet sent.
symbolThe accepted Swaps-dollar token of amount; present exactly when amount is.
directionblock_numbertimestampcounterparty_labelThe caller's own address_book label for the counterparty address — same address_book lookup as WalletTransactionList.transfers[].counterparty_label (§52 C4-D14 / K5c). On a transaction the wallet did not send, the counterparty is the decoded TIP-20 Transfer log's sender (in) or recipient (out), as on the list, and null when several transfers touch the wallet. On a transaction the wallet sent it is the receipt-level to, which for every TIP-20 send is the TOKEN CONTRACT, not the recipient (WalletSendModal.tsx) — so that out lookup is null for essentially every real send (Codex review of #2988, P2: fail-closed, never a wrong guess). null when unsaved or when there is no counterparty address; absent only when status: not_found.
networkThe network this transaction happened on — same convention as WalletTransactionList.transfers[].network (§52 C4-D14 / K5c). Absent when status: not_found (no transaction to place on a network).
Same as WalletTransactionList.transfers[].origin, for a mined (success) in transaction; null on every other one. Absent only when status: not_found.
List candidate deposit routes — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
The catalogue of cross-network paths onto Tempo. Auth is optional by design — this is route discovery, not an account read; a credential, if sent, is not checked for a specific account scope (RESOURCE-MODEL §2.3). K5a's /v1 implementation currently requires a credential regardless (the router has no "optional auth" lane on the authenticated route table yet) — the response is identical with or without one, so no caller-specific data is withheld either way; a true anonymous-auth lane is a named follow-up.
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.
List candidate deposit routes — available › Responses
The candidate deposit routes.
Preview a deposit's haircut — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
L2-4 (R13-wallet §2 W-G1). Nothing is written on Swaps' side and no deposit_intent row is created, unlike deposit_intents.create — but Relay itself still allocates a fresh, funds-accepting deposit address per call (the response never returns it, and Swaps records nothing that reconciles or expires it). Resolves the SAME live Relay quote deposit_intents.create itself uses for the candidate source_chain/source_asset and reports only what that quote returned — amount_out and haircut_pct are never a locally invented figure, and a Relay outage answers a typed 503, never an estimate. Idempotency-Key is required — like deposit_intents.create, a repeat POST without it would allocate another Relay address and consume another provider quote; a replay under the SAME key returns the stored price for 24h rather than a fresh one, so use a new key per quote you actually want re-priced. Use it to show a "you send X, receive ~Y" line before the holder commits to a real deposit.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Preview a deposit's haircut — dark-flag › Request Body
source_chainsource_assetA Money value that must be strictly positive — used on every request field that creates or moves value.
Preview a deposit's haircut — dark-flag › Responses
The computed preview.
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
haircut_pctDecimal string, percent (e.g. "0.15" means 0.15%), never a float — mirrors developer_fee_percent's representation elsewhere in this file.
List the holder's deposit intents — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
List the caller's deposit intents, optionally filtered to the non-terminal set.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
statusFilter to one status, e.g. the non-terminal set for an "in progress" view.
9 values (7 base + 2 recovery), confirmed exact by R13-wallet §2 W-G11 against the DB CHECK constraint — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
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.
List the holder's deposit intents — dark-flag › Responses
A page of the caller's deposit intents.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Create a cross-network deposit address — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
Mints a one-time, amount-bound deposit address for a cross-network deposit onto Tempo. Idempotency-Key is mandatory — a repeat POST without it would mint a second address and a second provider quote (RESOURCE-MODEL §2.3 D-2). The API never signs and never holds funds; this only returns where to send and what will arrive.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Create a cross-network deposit address — dark-flag › Request Body
source_chainsource_assetA Money value that must be strictly positive — used on every request field that creates or moves value.
Create a cross-network deposit address — dark-flag › Responses
The new deposit intent.
id^din_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus9 values (7 base + 2 recovery), confirmed exact by R13-wallet §2 W-G11 against the DB CHECK constraint — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
source_chainsource_assettarget_chaintarget_assetSingle-use, amount-bound — minted once per intent (RESOURCE-MODEL §2.3 D-2). Case-preserved verbatim.
expires_atOrdered transition history for this intent.
source_tx_hashtarget_tx_hashfail_reasonSet ONLY when the intent reached failed — §52 C4-D14 / K5c. Mirrors the already-recorded wallet_deposit_intents.fail_reason column (added 20260625150000, populated by the wallet-funding watcher). An OPAQUE, provider-defined (Relay) string: the raw failReason Relay attached to the request (e.g. SLIPPAGE on a tiny-deposit auto-refund) when it sent one, otherwise the Relay status word itself (refund/refunded/failure/failed) as a fallback so this is never blank. The watcher maps ALL FOUR of those Relay states to the SAME failed intent status — this field does NOT encode whether Relay auto-refunded the sender or hard-failed the request, and no vocabulary is guaranteed stable; do not pattern-match a specific string to infer "refunded" (Codex review of #2988, P2). null in every other state, including expired, cancelled and manual_recovery_required — the watcher only ever writes it while setting status: 'failed'.
Get one deposit intent — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
Read one deposit intent's status, hashes and timeline. Poll this until a terminal state (settled, expired, failed, cancelled, manual_recovery_required).
path Parameters
id^din_ · requiredHeaders
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.
Get one deposit intent — dark-flag › Responses
The deposit intent.
id^din_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus9 values (7 base + 2 recovery), confirmed exact by R13-wallet §2 W-G11 against the DB CHECK constraint — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
source_chainsource_assettarget_chaintarget_assetSingle-use, amount-bound — minted once per intent (RESOURCE-MODEL §2.3 D-2). Case-preserved verbatim.
expires_atOrdered transition history for this intent.
source_tx_hashtarget_tx_hashfail_reasonSet ONLY when the intent reached failed — §52 C4-D14 / K5c. Mirrors the already-recorded wallet_deposit_intents.fail_reason column (added 20260625150000, populated by the wallet-funding watcher). An OPAQUE, provider-defined (Relay) string: the raw failReason Relay attached to the request (e.g. SLIPPAGE on a tiny-deposit auto-refund) when it sent one, otherwise the Relay status word itself (refund/refunded/failure/failed) as a fallback so this is never blank. The watcher maps ALL FOUR of those Relay states to the SAME failed intent status — this field does NOT encode whether Relay auto-refunded the sender or hard-failed the request, and no vocabulary is guaranteed stable; do not pattern-match a specific string to infer "refunded" (Codex review of #2988, P2). null in every other state, including expired, cancelled and manual_recovery_required — the watcher only ever writes it while setting status: 'failed'.
List candidate send routes — available
Status: Available · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailable
The catalogue of paths off the holder's Tempo wallet. intent_required: false marks a direct same-chain Tempo send, which never touches /v1 at all — the holder's passkey signs a raw chain call directly (RESOURCE-MODEL §0.10, §2.3). Auth is optional.
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.
List candidate send routes — available › Responses
The candidate send routes.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
List the holder's cross-network send intents — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
List the caller's cross-network send intents, optionally filtered to the non-terminal set. A direct same-chain Tempo send never creates one of these (RESOURCE-MODEL §0.10).
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
statusFilter to one status, e.g. the non-terminal set for an "in progress" view.
8 values, confirmed exact by R13-wallet §2 W-G11 — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
awaiting_signature: a payout-funding send payouts.fund handed to one session, not yet recorded.
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.
List the holder's cross-network send intents — dark-flag › Responses
A page of the caller's send intents.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Prepare a cross-network send — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable · Money boundary: per-transaction human confirmation required · Non-custodial: response carries unsigned steps — Swaps never signsThis 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.
Money boundary (RESOURCE-MODEL §0.10, §0.11) — this crosses the boundary and per-transaction human confirmation is required before its unsigned steps are signed. The API never signs: the response carries send_instructions.steps[], an ordered list of unsigned calls the holder's own passkey must sign — nothing leaves the wallet until then. Never chain this from a quote straight into signing without a human confirming first; only a direct same-chain Tempo send bypasses this endpoint entirely.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Prepare a cross-network send — dark-flag › Request Body
A Money value that must be strictly positive — used on every request field that creates or moves value.
destination_chaindestination_assetdestination_addressPrepare a cross-network send — dark-flag › Responses
The new send intent, with unsigned steps to sign.
id^sin_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus8 values, confirmed exact by R13-wallet §2 W-G11 — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
awaiting_signature: a payout-funding send payouts.fund handed to one session, not yet recorded.
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
destination_chaindestination_assetdestination_addressUnsigned. The API cannot sign; the holder's passkey signs every step.
expires_atThe quoted "recipient gets" amount. Null until the routing provider has quoted it — a client renders "exact amount shown next" while it is null (R13-wallet §2 W-G2).
source_tx_hashThe source leg's transaction hash, verbatim. Recorded by source_tx.set, or by the server for a payout-funding send whose client record never landed; a later source_tx.set with the same hash is a no-op and a different hash is refused.
Get one send intent — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
Read one send intent's status, hashes and timeline. Poll this until a terminal state (settled, expired, failed, cancelled).
path Parameters
id^sin_ · requiredHeaders
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.
Get one send intent — dark-flag › Responses
The send intent.
id^sin_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus8 values, confirmed exact by R13-wallet §2 W-G11 — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
awaiting_signature: a payout-funding send payouts.fund handed to one session, not yet recorded.
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
destination_chaindestination_assetdestination_addressUnsigned. The API cannot sign; the holder's passkey signs every step.
expires_atThe quoted "recipient gets" amount. Null until the routing provider has quoted it — a client renders "exact amount shown next" while it is null (R13-wallet §2 W-G2).
source_tx_hashThe source leg's transaction hash, verbatim. Recorded by source_tx.set, or by the server for a payout-funding send whose client record never landed; a later source_tx.set with the same hash is a no-op and a different hash is refused.
Record a send intent's source transaction — available
Status: Available · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable
Records the transaction hash the holder's wallet just broadcast for this intent's source leg, moving it to source_submitted only once the chain itself confirms an exact-token, exact-amount transfer from the holder's own wallet to the deposit step's own recipient — never a client assertion. Call once, immediately after signing. Re-sending the same hash is a safe no-op; a different hash for the same intent is refused — never retry with a fresh hash to force it through. A send prepared to fund a payout that is cancelled or closed without funds is never recorded against that payout: 409 send_intent_payout_closed once the hash is recorded on the send (support reconciles it from there). If that write could not be made or verified, the call answers 503 source_tx_record_failed: the hash is not recorded yet — retry the same call with the same Idempotency-Key after Retry-After. A send payouts.fund handed to one session is recorded only from that session, also after the expiry sweep marked it expired (it moves on to source_submitted; 409 source_tx_already_recorded if a replacement send is already live for the payout); any other session gets 409 wallet_funding_claimed and nothing is recorded.
path Parameters
id^sin_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Record a send intent's source transaction — available › Request Body
source_tx_hashRecord a send intent's source transaction — available › Responses
The send intent, updated to source_submitted.
id^sin_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus8 values, confirmed exact by R13-wallet §2 W-G11 — RESOURCE-MODEL §2.3's v1 draft omitted cancelled.
awaiting_signature: a payout-funding send payouts.fund handed to one session, not yet recorded.
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
destination_chaindestination_assetdestination_addressUnsigned. The API cannot sign; the holder's passkey signs every step.
expires_atThe quoted "recipient gets" amount. Null until the routing provider has quoted it — a client renders "exact amount shown next" while it is null (R13-wallet §2 W-G2).
source_tx_hashThe source leg's transaction hash, verbatim. Recorded by source_tx.set, or by the server for a payout-funding send whose client record never landed; a later source_tx.set with the same hash is a no-op and a different hash is refused.
Quote a bank withdrawal — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
Compute-only — nothing is persisted, no address is minted. Prices what the holder's wallet would need to send for a given bank-destination amount. Indicative only: expected_source is a point-in-time estimate, quantised to 2 decimals (decimals: 2, not USDC's native 6 — the quote engine rounds to cents), with no expires_at in this response — the actual rate is re-priced live at offramp_intents.create, which is the only figure that is ever binding.
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.
Quote a bank withdrawal — dark-flag › Request Body
destination_currencydestination_payment_railA Money value that must be strictly positive — used on every request field that creates or moves value.
Quote a bank withdrawal — dark-flag › Responses
The computed quote.
destination_currencydestination_payment_railA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
The estimated amount and asset the holder's wallet will send. Indicative, and quantised to 2 decimals — decimals is 2 here even though USDC itself is a 6-decimal asset, because the quote engine rounds this figure to cents before it is published; a finer scale would state precision that was never quoted. The binding figure is re-priced at offramp_intents.create.
List the holder's external bank accounts — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
List the holder's own bank-account destinations, masked. Own means added here with attest_own_account, or saved under the holder's provider customer and not recorded by Swaps as anyone else's (an account Swaps recorded as a Pay an invoice or payroll recipient appears only once it is added here in the verified legal name); a holder name that differs from the verified legal name never hides one. Filtered to the four rails this wallet's off-ramp actually supports (ach/wire/sepa/spei) — a Bridge customer's OTHER external accounts (e.g. one added through a different Swaps surface on a rail this wallet doesn't offer) are never returned here. KNOWN LIMITATION: Bridge's own account record cannot distinguish wire from ach on read (both share the identical account+routing shape) — a wire account you added still lists back with rail: "ach".
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
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.
List the holder's external bank accounts — dark-flag › Responses
A page of the holder's external accounts.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Add a bank account the holder owns — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable · Money boundary: per-transaction human confirmation requiredThis 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.
Money boundary, REST-only — never an MCP tool argument, because most MCP clients log tool arguments verbatim and raw bank details must cross the boundary exactly once (RESOURCE-MODEL §2.3). attest_own_account must be true; an agent may never add a destination on a user's behalf. Bank details already saved as a Pay an invoice or payroll recipient are refused (owner_name_mismatch) unless the provider's holder name on that account is the verified legal name (ignoring case, spacing, punctuation, accents and word order); then this attestation makes it the holder's own. Bank details the provider already holds are reused only when exactly one saved account of the same currency carries the same account tail (and the same BIC, routing number or sort code when both sides have one); otherwise the answer is 409 conflict and nothing is saved (503 when the saved accounts can't be read right now; retry). The 201 publishes the rail on the provider's own record, not the rail you requested; the one exception is that a wire account whose provider record reads back as ach (both share the identical account+routing shape) still publishes the wire you requested — see GET /wallet/external_accounts's description for the same read-side ambiguity.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Add a bank account the holder owns — dark-flag › Request Body
Decision Table
| Variant | Matching Criteria |
|---|---|
| type = object · rail="ach" · requires: attest_own_account, currency, account_number +2 more | |
| type = object · rail="wire" · requires: attest_own_account, currency, account_number +2 more | |
| type = object · rail="sepa" · requires: attest_own_account, currency, iban +1 more | |
| type = object · rail="spei" · requires: attest_own_account, currency, clabe |
attest_own_accountMust be true. Re-verified against the provider again at spend time.
currencyrailaccount_numberrouting_numberaccount_typebank_nameAdd a bank account the holder owns — dark-flag › Responses
The new external account, masked.
id^ba_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectcurrencyrailbank_namelast4The last 4 characters of the account/IBAN/CLABE identifier — bare characters, never a masking marker such as ••; a client formats its own display string. Matches payroll's identically-named destination_snapshot.last4.
List the holder's bank withdrawals — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
List the caller's off-ramp intents, optionally filtered to the non-terminal set. Returns at most the 100 most recent off-ramp intents; has_more: false means the end of that window, not necessarily the end of the holder's history. Paging past 100 needs the upstream keyset tracked as https://github.com/swapsapp/swaps/issues/3460.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
statusFilter to one status, e.g. the non-terminal set for an "in progress" view.
11 values, American spelling canceled, exact match confirmed by R13-wallet §3 against RESOURCE-MODEL §2.3.
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.
List the holder's bank withdrawals — dark-flag › Responses
A page of the caller's off-ramp intents.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Withdraw from the wallet to a bank account — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable · Money boundary: per-transaction human confirmation requiredThis 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.
Money boundary (RESOURCE-MODEL §0.10, §0.11) — per-transaction human confirmation is required. The transit deposit into deposit_instructions is a direct same-chain Tempo call the holder's own passkey signs; it is never a /v1 write. Refuses over the per-rail minimum or the $3,000 V1 ceiling, and refuses outright if the amount cannot be priced. external_account_id must be one of the holder's own accounts (GET /wallet/external_accounts); any other id answers 404 before any provider call.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Withdraw from the wallet to a bank account — dark-flag › Request Body
external_account_id^ba_ · requireddestination_currencydestination_payment_railA Money value that must be strictly positive — used on every request field that creates or moves value.
Withdraw from the wallet to a bank account — dark-flag › Responses
The new off-ramp intent.
id^ofr_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus11 values, American spelling canceled, exact match confirmed by R13-wallet §3 against RESOURCE-MODEL §2.3.
external_account_id^ba_ · requireddestination_currencydestination_payment_railA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
developer_fee_percentThe transit deposit's address, amount and on-chain memo (R13-wallet §2 W-G12) — chain is always tempo and the memo, when the rail needs one, is carried on memo. This is a direct same-chain Tempo call the holder's own passkey signs; it is never a /v1 write.
The provider-observed amount actually settled to the bank account, in destination_currency. Set only once the intent reaches payment_processed — this is Bridge's flexible-amount off-ramp, so rate movement between quote and deposit (or a partial/rounded deposit) can make this genuinely differ from amount, the originally requested figure. Absent (never a fabricated copy of amount) before settlement, and on an intent whose provider payload never carried a settlement receipt.
Get one bank withdrawal — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
Read one off-ramp intent's status and deposit instructions. Poll until a terminal state (payment_processed, refunded, refund_failed, canceled, failed).
path Parameters
id^ofr_ · requiredHeaders
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.
Get one bank withdrawal — dark-flag › Responses
The off-ramp intent.
id^ofr_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus11 values, American spelling canceled, exact match confirmed by R13-wallet §3 against RESOURCE-MODEL §2.3.
external_account_id^ba_ · requireddestination_currencydestination_payment_railA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
developer_fee_percentThe transit deposit's address, amount and on-chain memo (R13-wallet §2 W-G12) — chain is always tempo and the memo, when the rail needs one, is carried on memo. This is a direct same-chain Tempo call the holder's own passkey signs; it is never a /v1 write.
The provider-observed amount actually settled to the bank account, in destination_currency. Set only once the intent reaches payment_processed — this is Bridge's flexible-amount off-ramp, so rate movement between quote and deposit (or a partial/rounded deposit) can make this genuinely differ from amount, the originally requested figure. Absent (never a fabricated copy of amount) before settlement, and on an intent whose provider payload never carried a settlement receipt.
Cancel a bank withdrawal — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
Cancels an off-ramp intent only from awaiting_funds (RESOURCE-MODEL §2.3) — once the transit deposit is signed and broadcast, this refuses rather than clawing money back. The exact eligibility window (whether quoted also qualifies) is an open question the design surfaces before the transit deposit is signed (R13-wallet §2 W-G14, проверить).
path Parameters
id^ofr_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Cancel a bank withdrawal — dark-flag › Responses
The cancelled off-ramp intent.
id^ofr_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatus11 values, American spelling canceled, exact match confirmed by R13-wallet §3 against RESOURCE-MODEL §2.3.
external_account_id^ba_ · requireddestination_currencydestination_payment_railA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
developer_fee_percentThe transit deposit's address, amount and on-chain memo (R13-wallet §2 W-G12) — chain is always tempo and the memo, when the rail needs one, is carried on memo. This is a direct same-chain Tempo call the holder's own passkey signs; it is never a /v1 write.
The provider-observed amount actually settled to the bank account, in destination_currency. Set only once the intent reaches payment_processed — this is Bridge's flexible-amount off-ramp, so rate movement between quote and deposit (or a partial/rounded deposit) can make this genuinely differ from amount, the originally requested figure. Absent (never a fabricated copy of amount) before settlement, and on an intent whose provider payload never carried a settlement receipt.
List the holder's virtual accounts — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
List the holder's bank-deposit accounts, per currency. Without purpose this includes Payment links collection accounts and legacy_wallet accounts (a wallet-purpose row settling outside this holder's Tempo wallet) that settle outside the wallet; a wallet screen asks for purpose=wallet_funding.
query Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
purposeOnly accounts with this purpose. An unknown value is a 400 invalid_request.
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.
List the holder's virtual accounts — dark-flag › Responses
A page of the holder's virtual accounts.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Create a virtual account — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
An explicit confirm — a virtual account is a billable provider object and is never auto-created (RESOURCE-MODEL §2.3 invariants). 409 conflict when a Payment links collection account already receives this currency into this wallet and no activated wallet-funding account does (the provider would hand the collection account back instead of issuing a wallet-funding one): refused before any provider call, and again if the provider hands one back anyway. When an activated wallet-funding account is already on that currency and wallet, the create returns it (409 conflict only if the provider hands back the collection account instead). GET /v1/capabilities?product=wallet_bank publishes that corridor as not_enabled with blocked_reason: collection_account_uses_currency.
Refused before any provider write unless the wallet_bank_virtual_account corridor for currency reads available on GET /v1/capabilities?product=wallet_bank (W-R23): 409 capability_unavailable with details.reason: rail_not_enabled while receive by bank is not launched for this account; 409 network_not_supported when the holder's wallet is not on Tempo mainnet (the provider settles USDC on Tempo mainnet, which a test-network wallet never sees) — except on a Swaps staging project whose provider runs in its sandbox, where a wallet on that project's own test network is accepted and nothing settles for real (a staging-only stub credits test tokens); otherwise 409 virtual_account_holder_not_verified, virtual_account_requirements_outstanding or capability_unavailable, each with details.currency, details.corridor_status and, when the corridor has one, details.blocked_reason. Each currency needs its own endorsement (USD base, EUR sepa, MXN spei). 503 temporarily_unavailable when the launch flag or the accounts already on the wallet cannot be read.
Provider sandbox only (a Swaps staging project): 503 temporarily_unavailable with Retry-After when the account was issued but could not be recorded as a sandbox account (one retry inside the request) — without that record its test deposits would not be credited. The account exists (listed with provider_environment: null); retry after Retry-After with the same Idempotency-Key: the retry reaches the same account and records it. Never answered for a live account.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Create a virtual account — dark-flag › Request Body
currencyusd, eur or mxn (case-insensitive) — the matching wallet_bank_virtual_account corridor.
Create a virtual account — dark-flag › Responses
The new virtual account.
id^va_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatuspurposeWhich product owns this account. wallet_funding: the wallet's own "receive by bank" account. collection: a pay-in account for Payment links and Buy orders, one per customer, source currency and settlement destination — it belongs to Payment links, not to the wallet screen. legacy_wallet (W-R25): a wallet-purpose account whose stored destination is not this holder's Tempo wallet — an account of the wallet's previous settlement destination (e.g. Solana, Ethereum) from before the Tempo cutover, shown honestly rather than folded into collection. Read-only through /v1: the wallet API manages only accounts that settle to this holder's Tempo wallet; while status is activated, deposits still settle to settles_to. virtual_accounts.deactivate / .reactivate refuse it with 409 conflict (legacy_wallet_read_only). Filter with GET /v1/wallet/virtual_accounts?purpose=wallet_funding.
Where deposits to this account are credited, read from the account's stored settlement destination. null when that destination is unknown — never guessed.
Provider-issued instructions for funding this VA from an external source. Masked on read where the canon requires it.
developer_fee_percentZero on receive-by-bank today — the holder-identity distinction is the load-bearing fact, not price.
provider_environmentThe provider environment this account was issued in, read only from Swaps' stored record of the account — never from the caller's livemode or from the server's configuration. sandbox: a provider test account — bank details are test data and no real bank transfer arrives. production: a live provider account. null: not recorded — never evidence of a live account. Every live account today answers null, and so, in the provider sandbox, does any account not created through POST /v1/wallet/virtual_accounts (for example a Payment links collection account, or one created by the dashboard's own action or found by a provider sync) and any account created before 2026-09-29. Only sandbox marks a test account: show a test-mode marker on the bank details for sandbox and for nothing else.
Redacted event history for this VA.
Get one virtual account — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
Read one of the holder's virtual accounts, including the "in your name" rail guard fields.
path Parameters
id^va_ · requiredHeaders
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.
Get one virtual account — dark-flag › Responses
The virtual account.
id^va_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatuspurposeWhich product owns this account. wallet_funding: the wallet's own "receive by bank" account. collection: a pay-in account for Payment links and Buy orders, one per customer, source currency and settlement destination — it belongs to Payment links, not to the wallet screen. legacy_wallet (W-R25): a wallet-purpose account whose stored destination is not this holder's Tempo wallet — an account of the wallet's previous settlement destination (e.g. Solana, Ethereum) from before the Tempo cutover, shown honestly rather than folded into collection. Read-only through /v1: the wallet API manages only accounts that settle to this holder's Tempo wallet; while status is activated, deposits still settle to settles_to. virtual_accounts.deactivate / .reactivate refuse it with 409 conflict (legacy_wallet_read_only). Filter with GET /v1/wallet/virtual_accounts?purpose=wallet_funding.
Where deposits to this account are credited, read from the account's stored settlement destination. null when that destination is unknown — never guessed.
Provider-issued instructions for funding this VA from an external source. Masked on read where the canon requires it.
developer_fee_percentZero on receive-by-bank today — the holder-identity distinction is the load-bearing fact, not price.
provider_environmentThe provider environment this account was issued in, read only from Swaps' stored record of the account — never from the caller's livemode or from the server's configuration. sandbox: a provider test account — bank details are test data and no real bank transfer arrives. production: a live provider account. null: not recorded — never evidence of a live account. Every live account today answers null, and so, in the provider sandbox, does any account not created through POST /v1/wallet/virtual_accounts (for example a Payment links collection account, or one created by the dashboard's own action or found by a provider sync) and any account created before 2026-09-29. Only sandbox marks a test account: show a test-mode marker on the bank details for sandbox and for nothing else.
Redacted event history for this VA.
Deactivate a virtual account — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
Deactivates a virtual account. Reversible via reactivate. 409 conflict for a collection account: Payment links owns it. 409 conflict (legacy_wallet_read_only) for a legacy_wallet account.
path Parameters
id^va_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Deactivate a virtual account — dark-flag › Responses
The deactivated virtual account.
id^va_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatuspurposeWhich product owns this account. wallet_funding: the wallet's own "receive by bank" account. collection: a pay-in account for Payment links and Buy orders, one per customer, source currency and settlement destination — it belongs to Payment links, not to the wallet screen. legacy_wallet (W-R25): a wallet-purpose account whose stored destination is not this holder's Tempo wallet — an account of the wallet's previous settlement destination (e.g. Solana, Ethereum) from before the Tempo cutover, shown honestly rather than folded into collection. Read-only through /v1: the wallet API manages only accounts that settle to this holder's Tempo wallet; while status is activated, deposits still settle to settles_to. virtual_accounts.deactivate / .reactivate refuse it with 409 conflict (legacy_wallet_read_only). Filter with GET /v1/wallet/virtual_accounts?purpose=wallet_funding.
Where deposits to this account are credited, read from the account's stored settlement destination. null when that destination is unknown — never guessed.
Provider-issued instructions for funding this VA from an external source. Masked on read where the canon requires it.
developer_fee_percentZero on receive-by-bank today — the holder-identity distinction is the load-bearing fact, not price.
provider_environmentThe provider environment this account was issued in, read only from Swaps' stored record of the account — never from the caller's livemode or from the server's configuration. sandbox: a provider test account — bank details are test data and no real bank transfer arrives. production: a live provider account. null: not recorded — never evidence of a live account. Every live account today answers null, and so, in the provider sandbox, does any account not created through POST /v1/wallet/virtual_accounts (for example a Payment links collection account, or one created by the dashboard's own action or found by a provider sync) and any account created before 2026-09-29. Only sandbox marks a test account: show a test-mode marker on the bank details for sandbox and for nothing else.
Redacted event history for this VA.
Reactivate a virtual account — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailableThis 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.
Reactivates a previously deactivated virtual account. 409 conflict for a collection account: Payment links owns it. 409 conflict (legacy_wallet_read_only) for a legacy_wallet account.
path Parameters
id^va_ · requiredHeaders
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Reactivate a virtual account — dark-flag › Responses
The reactivated virtual account.
id^va_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectstatuspurposeWhich product owns this account. wallet_funding: the wallet's own "receive by bank" account. collection: a pay-in account for Payment links and Buy orders, one per customer, source currency and settlement destination — it belongs to Payment links, not to the wallet screen. legacy_wallet (W-R25): a wallet-purpose account whose stored destination is not this holder's Tempo wallet — an account of the wallet's previous settlement destination (e.g. Solana, Ethereum) from before the Tempo cutover, shown honestly rather than folded into collection. Read-only through /v1: the wallet API manages only accounts that settle to this holder's Tempo wallet; while status is activated, deposits still settle to settles_to. virtual_accounts.deactivate / .reactivate refuse it with 409 conflict (legacy_wallet_read_only). Filter with GET /v1/wallet/virtual_accounts?purpose=wallet_funding.
Where deposits to this account are credited, read from the account's stored settlement destination. null when that destination is unknown — never guessed.
Provider-issued instructions for funding this VA from an external source. Masked on read where the canon requires it.
developer_fee_percentZero on receive-by-bank today — the holder-identity distinction is the load-bearing fact, not price.
provider_environmentThe provider environment this account was issued in, read only from Swaps' stored record of the account — never from the caller's livemode or from the server's configuration. sandbox: a provider test account — bank details are test data and no real bank transfer arrives. production: a live provider account. null: not recorded — never evidence of a live account. Every live account today answers null, and so, in the provider sandbox, does any account not created through POST /v1/wallet/virtual_accounts (for example a Payment links collection account, or one created by the dashboard's own action or found by a provider sync) and any account created before 2026-09-29. Only sandbox marks a test account: show a test-mode marker on the bank details for sandbox and for nothing else.
Redacted event history for this VA.
List a virtual account's deposit history — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailableThis 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.
"Money in" rows for one virtual account — a pull read over virtual_account.deposit_received (RESOURCE-MODEL §3); the real push producer is the Bridge webhook. One row per deposit: Bridge's lifecycle steps of one deposit collapse into one row whose amount is the settled step's (payment_processed) when present, else the received step's (funds_received), never a sum, and whose occurred_at is the first recorded of those steps, so a deposit that settles keeps its place and an issued next_cursor stays valid. A deposit not yet reported received is not listed. Newest first.
The amount is that one step's own value and currency, as the provider reports them. The settled step is net of the provider's fees, so a row's value can change on the same row, at the same occurred_at and cursor position, when its deposit settles: Bridge documents usd on both steps, so a deposit received as 120.00 USD reads 116.55 USD once settled. The currency is the step's own; today the provider reports the source currency on both steps. Never label the account by a row's currency, and never sum rows across a settlement.
path Parameters
id^va_ · requiredquery Parameters
limitDefault 25. Out of range is never rejected: a value outside 1..100, a fraction, or a non-number is not a 400 — 0, a negative number, a non-integer or anything non-numeric falls back to the default (25); anything above 100 is capped to 100. A documented clamp, not a silent one; minimum/maximum are deliberately absent from this schema so a generated client (the MCP tool included) does not reject a value the API itself accepts.
cursorOpaque cursor from a previous response's next_cursor.
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.
List a virtual account's deposit history — dark-flag › Responses
A page of deposit-received rows for this virtual account, one per deposit.
has_morenext_cursorA1-2: required (still nullable) — every producer already emits null rather than omitting the field when there is no next page, so this only removes a third, unused wire state (absent) a strict client had to handle for nothing.
Build an unsigned same-chain conversion — available
Status: Available · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable · Money boundary: per-transaction human confirmation required · Non-custodial: response carries unsigned steps — Swaps never signs
Money boundary (RESOURCE-MODEL §0.10, §0.11) — per-transaction human confirmation is required. The API never signs: steps[] is an ordered list of unsigned calls (an approve before a swap, when the DEX needs one) the holder's own passkey signs; nothing moves until then. swap-execution builds a transaction, it never sends one. Testnet has one token, so a conversion is honestly unavailable there.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Build an unsigned same-chain conversion — available › Request Body
from_tokento_tokenA Money value that must be strictly positive — used on every request field that creates or moves value.
chain_idslippage_bpsBuild an unsigned same-chain conversion — available › Responses
The built conversion, with unsigned steps to sign.
id^cnv_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectkindAlways dex — on-chain, returns steps[] for the holder's passkey to sign. The proposed cex (no-wallet exchange leg) branch was withdrawn by G on 2026-09-08 (Exolix removed); this field is kept, pinned to dex, so a future rail can extend the enum without a breaking projection change.
provider_idchain_idfrom_tokento_tokenA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
The quoted receive amount, before the slippage bound (R13-wallet §2 W-G9).
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
rateDecimal string, never a float (R13-wallet §2 W-G9). Example "0.9998".
slippage_bpstracking_idA1-6: additive alias of id (see this schema's own description) — kept for one version for claude/dashboard-v2's existing caller. New callers should prefer id.
Ordered, unsigned — a two-step ceremony (approve then swap) is two entries, not one call{} — structurally explicit rather than inferred from calling create twice (R13-wallet §2 W-G9).
tracking_statusThe tracked leg's status (RESOURCE-MODEL §2.3). A kind: dex conversion — the only kind this API builds (D-25: G cut the proposed kind: cex branch, 2026-09-08, Exolix removed) — reaches only pending (built, unsigned) and submitted (its POST .../source_tx recorded and on-chain-verified the swap's own broadcast transaction); settled/failed are not yet advanced automatically past that point.
Track a conversion — available
Status: Available · Callers: agent (business key), dashboard session · Scope:
wallet.read· Test mode: unavailable
Read a conversion's tracked on-chain leg by its id. Never claim the swap executed before the chain says so.
path Parameters
id^(cnv_)?[0-9a-fA-F-]… · requiredThe conversion's id (cnv_-prefixed — A1-6, RESOURCE-MODEL §0.2). A bare, un-prefixed tracking_id is also accepted for one version (claude/dashboard-v2's WalletConvertScreen/useWalletConversions already call this route with the raw tracking_id, not yet the prefixed id) — see Conversion.tracking_id.
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.
Track a conversion — available › Responses
The tracked conversion.
id^cnv_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectkindAlways dex — on-chain, returns steps[] for the holder's passkey to sign. The proposed cex (no-wallet exchange leg) branch was withdrawn by G on 2026-09-08 (Exolix removed); this field is kept, pinned to dex, so a future rail can extend the enum without a breaking projection change.
provider_idchain_idfrom_tokento_tokenA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
The quoted receive amount, before the slippage bound (R13-wallet §2 W-G9).
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
rateDecimal string, never a float (R13-wallet §2 W-G9). Example "0.9998".
slippage_bpstracking_idA1-6: additive alias of id (see this schema's own description) — kept for one version for claude/dashboard-v2's existing caller. New callers should prefer id.
Ordered, unsigned — a two-step ceremony (approve then swap) is two entries, not one call{} — structurally explicit rather than inferred from calling create twice (R13-wallet §2 W-G9).
tracking_statusThe tracked leg's status (RESOURCE-MODEL §2.3). A kind: dex conversion — the only kind this API builds (D-25: G cut the proposed kind: cex branch, 2026-09-08, Exolix removed) — reaches only pending (built, unsigned) and submitted (its POST .../source_tx recorded and on-chain-verified the swap's own broadcast transaction); settled/failed are not yet advanced automatically past that point.
Record a conversion's source transaction — available
Status: Available · Callers: agent (business key), dashboard session · Scope:
wallet.write· Test mode: unavailable
Records the transaction hash the holder's wallet just broadcast for this conversion's swap step, moving it to submitted only once the chain itself confirms an exact-token, exact-amount transfer from the holder's own wallet to the swap step's own call target (the StablecoinDEX) — never a client assertion. Call once, immediately after signing. Re-sending the same hash is a safe no-op; a different hash for the same conversion is refused — never retry with a fresh hash to force it through.
path Parameters
id^(cnv_)?[0-9a-fA-F-]… · requiredThe conversion's id (cnv_-prefixed — A1-6). A bare, un-prefixed tracking_id is also accepted for one version — see wallet.conversions.get's own parameter description.
Headers
Idempotency-Key^[A-Za-z0-9_-]{16,12… · requiredCaller-generated, 16-128 characters of [A-Za-z0-9_-] (a UUID or a base64url token both qualify). The value is part of a primary key, so the shape is enforced, not advisory: outside it the request is 400 invalid_request/idempotency_key_invalid. A replay within 24 hours returns the stored response; a different body under the same key is idempotency_error; a replay of a request that failed after it may have taken effect is 409 idempotency_failed — reconcile, then retry under a NEW key. After 24 hours the key is forgotten and the request is new (D-108).
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.
Record a conversion's source transaction — available › Request Body
source_tx_hashRecord a conversion's source transaction — available › Responses
The conversion, updated to submitted.
id^cnv_ · requiredPrefixed, opaque. The prefix is part of the contract; the suffix is never parsed.
objectkindAlways dex — on-chain, returns steps[] for the holder's passkey to sign. The proposed cex (no-wallet exchange leg) branch was withdrawn by G on 2026-09-08 (Exolix removed); this field is kept, pinned to dex, so a future rail can extend the enum without a breaking projection change.
provider_idchain_idfrom_tokento_tokenA money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
The quoted receive amount, before the slippage bound (R13-wallet §2 W-G9).
A money value. amount is a minor-unit integer as a string; decimals states the scale so a client never re-derives it (USDC is 6). Floats never appear on a money path.
rateDecimal string, never a float (R13-wallet §2 W-G9). Example "0.9998".
slippage_bpstracking_idA1-6: additive alias of id (see this schema's own description) — kept for one version for claude/dashboard-v2's existing caller. New callers should prefer id.
Ordered, unsigned — a two-step ceremony (approve then swap) is two entries, not one call{} — structurally explicit rather than inferred from calling create twice (R13-wallet §2 W-G9).
tracking_statusThe tracked leg's status (RESOURCE-MODEL §2.3). A kind: dex conversion — the only kind this API builds (D-25: G cut the proposed kind: cex branch, 2026-09-08, Exolix removed) — reaches only pending (built, unsigned) and submitted (its POST .../source_tx recorded and on-chain-verified the swap's own broadcast transaction); settled/failed are not yet advanced automatically past that point.
List convertible token pairs — dark-flag
Status: Dark-flag — live behind a cohort or flag · Callers: agent (business key), dashboard session, business key · Scope:
wallet.read· Test mode: unavailableThis 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.
L2-4 (R13-wallet §2 W-G10) — the mainnet Swaps-dollar token set's convertible pairs, each with a live liquidity flag from the same on-chain DEX probe conversions.create itself calls. Discovery data, not account-scoped; security marks auth optional (the router still requires a credential today — see deposit_routes.list's own identical, already-documented gap — the output never varies by caller either way).
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.
List convertible token pairs — dark-flag › Responses
The convertible token pairs.