Swaps API
Resource-shaped REST for Swaps — payment links, payouts, payroll, wallet, quotes and orders, capabilities, screening, crypto processing.
Single source of truth for the public /v1 surface. Generated artefacts — the reference docs,
the TypeScript and Python SDKs, the MCP tool list and llms.txt — are derived from this file
and never edited by hand (API-CANON §12, "rule of six").
Three tenses, never a fourth (x-swaps-status): available — a live backend exists and /v1 is
a thin router in front of it. dark-flag — live, but gated behind a cohort or feature flag;
while the flag is closed, calling it answers 503 temporarily_unavailable (an availability
lever, never an authorization one), never capability_unavailable. proposed — does not
exist yet, documented for the contract it will carry, never presented as callable and never
hidden.
Money is an object (Money), never a bare number. Every object carries livemode. Every
mutating request carries Idempotency-Key. Every response echoes Swaps-Version.
Terms acceptance (inactive until a legal release is explicitly enabled). Selected new live
operations may return 409 capability_unavailable, code terms_acceptance_required, with
details.revision and details.action. The person must explicitly accept the displayed
revision in Swaps; only an owner can accept for an account. Personal wallet intents require
that user's own receipt. An API key or agent cannot manufacture acceptance. Acceptance does not
authorize a payment or replace provider agreements. An unavailable acceptance registry returns
503 temporarily_unavailable, code legal_acceptance_unavailable; respect Retry-After.
Existing completed idempotent responses remain replayable. Reads and recovery are not gated.
Response tolerance (A1-SDK-TOLERANT, fixer round 1 finding #3), x-swaps-response-tolerant.
additionalProperties: false/unevaluatedProperties: false on a schema reachable from a
RESPONSE — a resource, a list envelope, or a nested object such as Money, at any depth, even
when the SAME schema is also reachable from a request — is NOT a promise that an unrecognized
key fails deserialization on that side: the generated @swaps/sdk strips it instead
(packages/contracts-api/generated/schemas.ts, scripts/openapi/gen-zod.mjs §3b), matching
this document's own forward-compatibility promise above. Any other client generated from this
document should do the same: on a response object, treat the keyword as advisory only, never as
license to fail deserialization on an unknown key. A REQUEST body keeps the keyword's literal
meaning — an unrecognized key there is still a 400 (a schema reachable from BOTH sides gets a
request-only strict variant server-side; see the SDK generator). This item does not remove the
keyword from the ~200 response schemas that carry it (spread across every resource file, mostly
outside this item's file ownership) — this paragraph is the explicit publication of the true
behavior instead, so a consumer reading only this document, not the SDK source, still gets it
right.
Forward compatibility (A1-2). A RESPONSE enum tagged x-swaps-open-enum: true may gain
a new member within /v1 without a version cut: treat an unrecognized member as an opaque
string and never fail deserialization on it — the generated SDK types these as a union of the
known literals widened with string so a new member still type-checks. A REQUEST enum is never
open: an unrecognized value there stays 400 invalid_request, because it names a choice the
caller is making, not a fact the server is reporting. The one deliberate pairing this produces:
Event.type (a response) is open, so a new event type is delivered rather than rejected, while
webhook_endpoints.create/.update's event_types (the SAME catalogue, submitted as a
request) still 400s an unrecognized name — an endpoint asking to receive a type this deployment
does not know cannot be honoured, open catalogue or not. This is a narrowing of API-CANON §3's
additive-fields rule to enum members specifically; it does not relax anything else there.
Test mode (A1-3, reconciled against merged K11-1/K11-2 code — fixer round 2). /v1 cannot be
exercised in test mode by anyone today: api_v1.test_mode (the master switch K11-2 added) is OFF,
and no livemode=false account can exist in production yet either (K11-4, the piece that lets one
be created, has not landed) — no sk_test_ caller exists to reach any of the behaviour below.
While the switch is off, the router refuses EVERY /v1 operation the same way, before any
operation-specific code runs: caller.livemode === false forks to dispatchTestMode
(api-v1/router.ts, api-v1/test_mode.ts), which answers 503 temporarily_unavailable
(sideEffectFree, Retry-After: 60) for every disposition, including one the router would run
locally once the switch is on. This is a platform-wide gate, not a claim about any one operation —
it is why every available/dark-flag operation below is annotated x-swaps-test-mode: unavailable. Four operations additionally enforce the same refusal a second time in their own
handler code (quotes.create, quotes.get, orders.create, customers.create) — an independent
guard, not the only one. On every other operation, unavailable records only that K11 has not yet
individually verified and evidenced a full/sandbox/fixtures/dev-cron claim for it, one
operation at a time, each with its own test (K11_EVIDENCE_LIST,
packages/contracts-api/__tests__/contract/test-mode-truth.test.ts). The router matches that
value exactly: every unavailable operation is routed refused and answers 503 temporarily_unavailable before any handler runs, whether the switch is on or off — with the switch
on, that refusal is permanent and carries no Retry-After. Restoring one operation flips the
router disposition, this contract value and K11_EVIDENCE_LIST together, in one change (API-CANON
§12, "what is unavailable is documented with its reason, not hidden").
The handful of proposed operations keep an aspirational
full/sandbox/fixtures value instead — they are not callable at all yet, so there is no live
behaviour for unavailable to correct.
- GET /payment_links
- POST /payment_links
- GET /payment_links/{id}
- PATCH /payment_links/{id}
- POST /payment_links/{id}/activate
- POST /payment_links/{id}/cancel
- POST /payment_links/{id}/send_invoice
- GET /payment_links/{id}/reminder_schedule
- PATCH /payment_links/{id}/reminder_schedule
- POST /payment_links/{id}/reminder_schedule/disable
- GET /payment_links/{id}/events
- GET /payment_links/{id}/receipt
- GET /clients
- POST /clients
- GET /clients/{id}
- PATCH /clients/{id}
- POST /clients/{id}/archive
- GET /products
- POST /products
- GET /products/{id}
- PATCH /products/{id}
- POST /products/{id}/archive
- GET /payment_sessions/{token}
- GET /payment_sessions/by_code/{short_code}
- POST /payment_sessions/{token}/view
- POST /payment_sessions/{token}/payments
- GET /payment_sessions/{token}/payments/{payment_id}
- POST /payment_sessions/{token}/receipt_email
- POST /payment_sessions/{token}/mark_sent
- POST /payment_sessions/{token}/pay_with_wallet
- GET /payouts
- POST /payouts
- GET /payouts/eligibility
- GET /payouts/{id}
- POST /payouts/{id}/beneficiary
- GET /payouts/{id}/funding_instructions
- POST /payouts/{id}/funding_instructions
- GET /payouts/{id}/wallet_funding_quote
- GET /payouts/{id}/receipt
- GET /payouts/{id}/attempts
- GET /payouts/{id}/events
- POST /payouts/{id}/cancel
- POST /payouts/{id}/mark_sent
- POST /payouts/{id}/replace
- GET /payroll_runs
- POST /payroll_runs
- GET /payroll_runs/{id}
- POST /payroll_runs/{id}/approve
- POST /payroll_runs/{id}/cancel
- POST /payroll_runs/{id}/execute
- GET /payroll_runs/{id}/readiness
- GET /payroll_runs/{id}/funding_instructions
- POST /payroll_runs/{id}/funding_instructions
- GET /payroll_runs/{id}/items
- GET /payroll_runs/{id}/items/{item_id}
- POST /payroll_runs/{id}/items/{item_id}/reissue_link
- GET /payroll_runs/{id}/attempts
- GET /payroll_runs/{id}/events
- GET /payroll_runs/{id}/export
- GET /payroll_recipients
- GET /payroll_recipients/{id}
- POST /payroll_recipients/{id}/adopt_pending_destination
- GET /payroll_templates
- POST /payroll_templates
- GET /payroll_templates/{id}
- GET /payroll_recipient_sessions/{token}
- POST /payroll_recipient_sessions/{token}/destination
- 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
- POST /quotes
- GET /quotes/{id}
- GET /orders
- POST /orders
- GET /orders/{id}
- POST /orders/{id}/cancel
- GET /orders/{id}/events
- GET /payment_links/{id}/payments
- GET /payments
- GET /payments/{id}
- GET /payments/{id}/events
- GET /subscriptions
- POST /subscriptions
- GET /subscriptions/{id}
- POST /subscriptions/{id}/pause
- POST /subscriptions/{id}/resume
- POST /subscriptions/{id}/cancel
- GET /subscriptions/{id}/invoices
- GET /subscriptions/{id}/invoices/{invoice_id}
- GET /capabilities
- GET /eligibility
- GET /customers
- POST /customers
- GET /customers/{id}
- POST /customers/{id}/verification_links
- GET /customers/{id}/associated_persons
- PATCH /customers/{id}/compliance_profile
- GET /customers/{id}/events
- GET /screenings
- POST /screenings
- GET /screenings/{id}
- GET /screenings/{id}/reports
- POST /screenings/{id}/reports
- GET /screenings/{id}/reports/{report_id}
- GET /screenings/{id}/reports/{report_id}/evidence_pdf
- POST /traces
- GET /transactions/{chain}/{hash}
- GET /account
- PATCH /account
- GET /accounts
- POST /accounts
- GET /account/readiness
- GET /account/setup_guide
- POST /account/sessions/revoke_all
- GET /activity
- GET /activity/summary
- GET /events
- GET /events/{id}
- GET /events/stream
- GET /webhook_endpoints
- POST /webhook_endpoints
- GET /webhook_endpoints/{id}
- PATCH /webhook_endpoints/{id}
- DELETE /webhook_endpoints/{id}
- POST /webhook_endpoints/{id}/rotate_secret
- POST /webhook_endpoints/{id}/send_test_event
- GET /webhook_deliveries
- GET /webhook_deliveries/{id}
- POST /webhook_deliveries/{id}/replay
- POST /webhook_waitlist
- POST /card_waitlist
- GET /address_book
- POST /address_book
- GET /address_book/{id}
- PATCH /address_book/{id}
- DELETE /address_book/{id}
- POST /address_book/{id}/recheck
- POST /address_book/{id}/beneficiary
- GET /credits
- GET /credit_events
- POST /credit_checkouts
- GET /api_keys
- POST /api_keys
- GET /api_keys/{id}
- POST /api_keys/{id}/roll
- POST /api_keys/{id}/revoke
- GET /api_keys/{id}/usage
- GET /request_logs
sk_live_… or sk_test_…, hashed to SHA-256 against public.api_keys and checked for
status, expiry, IP allowlist, plan and scopes on every request. Acts as the account that owns the key
and reaches only that account's resources. Scopes are <resource>.<read|write>.bearer caller class and derives scopes from account_members.role
(SEC-A/P0-2). It is not bound to the dashboard client today — per-client binding is K12 and is
not enforced yet, so a caller who holds their own session token (e.g. copied out of the
dashboard) can present it directly. Not a general alternative to businessKey for the rest of
this document.payment_link.* events
payment.* events
subscription.* events
payout.* events
payroll_run.* events
payroll_item.* events
payroll_template.created
deposit_intent.* events
send_intent.* events
offramp_intent.* events
virtual_account.* events
wallet.created
conversion.* events
customer.* events
capability.* events
order.* events
screening.* events
account.access_state_changed
address_book.entry_rechecked
credit.* events
test.ping
webhook_endpoint.disabled