Get started
Prepare a business key and inspect account capabilities before creating a draft payment link.
Switched on resource by resource
Each /v1 resource is switched on separately in production. A resource that is not switched on
answers 503 temporarily_unavailable — read it as "not available yet", not as an outage, and read
GET /v1/capabilities?product=<product> for what your account can do. The examples below are not an onboarding
guarantee. The public MCP documentation lists
the published integration surfaces.
1. Obtain a business key
The account holder manages keys in Developers → API keys. Automatic issuance on signup is not promised. Supply an existing key through your client environment. This guide needs a live key: a sk_test_ key runs only the operations Test mode lists — API-key, request-log and account reads on your test account, and webhook endpoints, webhook deliveries and the per-object event lists in the development project — and the payment-link calls below answer it 503 temporarily_unavailable. Once enabled, read GET /v1/capabilities?product=payouts first, with a live key — capabilities is unavailable in test mode (see Test mode) — product is required, one of payment_links, payouts, payroll, buy_sell, wallet_bank — and proceed only when the account is authorized.
Code
Keys are shown once. Rotate a lost key from Developers → API keys — support cannot read it back to you.
2. Create a payment link
A link starts as a draft: nothing is charged and no rail goes live until you activate it. Every mutating request carries an Idempotency-Key so a retried call never double-creates the link — generate a fresh key per new link; reusing the same key within 24 hours replays the stored response instead of creating a second one — and amount is always a Money object — a minor-unit integer string plus decimals, never a float. An optional Swaps-Version header pins the call to a dated contract version; omit it and the key's own default applies, and every response echoes the version it was served under (see Conventions).
Name where the money lands when you draft the link, or activate refuses it with 400 settlement_destination_required. settlement_destination points to a bank account or crypto address you already saved in the address book. /v1/address_book takes a dashboard session or an agent key, never a business key, so save the destination once under Addresses in the dashboard. No dashboard screen shows its adr_… id today: read it from GET /v1/address_book with an agent key or a dashboard session (the id field, adr_ plus a UUID), then reuse it from your key. Without one, use the crypto-only path below. The other option is a crypto-only link paid out to your Swaps Wallet: send accepted_rail_kinds: ["crypto"] instead, on a USD link only, where the crypto-only capability is enabled for your account. Don't send both. Payment links are unavailable in test mode, so the samples use a live key; a sk_test_ key gets 503 temporarily_unavailable here.
There is no published Swaps client library today — packages/sdk-ts (@swaps/sdk) is this repo's own internal, unpublished package, not something npm installable. Until one ships, cURL and plain fetch — a Node 18+ and browser built-in, no library to install — are what actually runs:
201 Created — payment_link · draft
Code
3. Open the link
A draft has no url. Activating is the account holder's own compliance attestation — over the dashboard, or a signed POST …/activate — never something an agent does on their behalf (see Payment links). REST only: activate has no MCP tool.
attestation_acceptedmust betrue; anything else is refused.url,payable_railsandsettlement_kindare set at that moment, never before.statusmovesdraft → activeand the payer can open the link.
Code
A refused activation leaves the link a draft:
| Answer | Meaning |
|---|---|
400 settlement_destination_required | The draft names neither a settlement_destination nor accepted_rail_kinds: ["crypto"]. PATCH a destination onto it and activate again. |
409 allowed_rails_invalid_for_currency | The stored allowed_rails leaves no rail a payer could use. error.details.offerable_rails lists the ones that would work. |
422 crypto_only_currency_not_usd | A crypto-only link is not in USD. The Swaps Wallet rail pays out USD stablecoins with no FX step. |
An account not yet eligible gets 403 permission_error before the destination is checked; a crypto-only draft can also get 409 wallet_not_found or 409 capability_unavailable, and a second activate gets 409 conflict. The guide's Minimal flow table gives each one's fix.
The payer opens url (https://swaps.app/pay/plk_… on production). Its last path segment is the public session token that GET /v1/payment_sessions/{token}, POST …/view and POST …/payments take, with no key. The Payment links guide walks through the payer side and the other refusals.
4. Go live
Live access requires enablement, an authorized live key, account verification and the relevant capabilities. Changing the key prefix does not grant access. Confirm the supported operation and rail before making a live call.
Do not treat a documented test-mode contract as proof of a working end-to-end sandbox. See Test mode for implementation limits.
Ask your agent instead
After enablement, an agent with an authorized key can inspect capabilities and prepare permitted drafts. The account holder creates the key and confirms any activation themselves.
Using my existing business key, call GET /v1/capabilities?product=payment_links over REST — the MCP tool equivalent is list_capabilities (see the Agents & MCP page). If authorization or availability fails, stop and report the error. Otherwise summarize permitted actions; do not create or activate a payment link.
Next: the Payment links guide · the full error type table.