API quickstart

The send21 API has two building blocks. A payment request asks to get paid: the payer opens a link, picks a currency and pays from any wallet. A payment draft prepares a payment to someone else, which the sender signs in their own wallet. Base URL: https://send21.io/api/v1.

1. Create an API key

Sign in and open API keys. Pick only the scopes you need. Keys look like s21_... and are shown once. No key can move funds, because send21 never holds any.

ScopeAllows
drafts:readRead drafts, payment requests, templates, status and rates
drafts:writeCreate, cancel and re-quote drafts and payment requests
addressbook:readRead saved addresses
addressbook:writeChange saved addresses
webhooks:manageRegister and delete webhook endpoints

2. Ask to get paid: a payment request

Price it in any of 165 fiat currencies and list the currencies you accept, each with your own receiving address. The response contains payPath; share https://send21.io plus that path as a link or QR code. The rate locks when the payer picks a currency.

curl -X POST https://send21.io/api/v1/payment-requests \
  -H "Authorization: Bearer s21_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{
    "fiatCurrency": "EUR",
    "fiatAmount": 49.00,
    "orderId": "ORDER-1042",
    "memo": "Invoice 1042",
    "options": [
      { "currency": "Eurc", "network": "Base", "address": "0xYourAddress" },
      { "currency": "Usdc", "network": "Solana", "address": "YourSolanaAddress" },
      { "currency": "Btc", "network": "Mainnet", "address": "[email protected]", "method": "Lightning" }
    ]
  }'

3. Prepare a payment: a draft

A draft pays a receiver. With singleOutput: true it is one QR payment from any wallet, and the send21 fee is billed to your account. Without it, the fee is a separate output and the template suits PSBT and batching wallets. Fetch wallet-ready instructions from GET /drafts/{id}/tx-template.

curl -X POST https://send21.io/api/v1/drafts \
  -H "Authorization: Bearer s21_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: payout-77" \
  -d '{
    "receiverAddress": "0xReceiverAddress",
    "network": "Arbitrum",
    "currency": "Usdc",
    "paymentMethod": "MultiOutput",
    "singleOutput": true,
    "fiatCurrency": "SEK",
    "fiatAmount": 1500
  }'

4. Track the payment

  • Poll GET /drafts/{id}/status for AwaitingPayment, Seen, Confirmed, Expired or Cancelled.
  • Better: register a webhook and react to draft.confirmed.
  • A transfer within 10% of the billed amount is flagged as draft.amount_mismatch and can be accepted with POST /drafts/{id}/accept-received.

Good to know

  • Send an Idempotency-Key header on create calls. A retry with the same key returns the original object.
  • Batch creation: POST /drafts/batch takes up to 25 drafts.
  • Rate limit: 300 requests per minute per key. A 429 means back off; prefer webhooks over tight polling.
  • A 402 on a create call means accrued fees must be settled first; the body names POST /fees/accrued/settle.
  • Full reference: /swagger and /openapi.json.

Related

This page is also available as Markdown: /docs/quickstart.md. Questions: [email protected].