API referenceCollections

Collections

Collect money from your payers over open banking. You open a collection, Swappr returns a checkout_url, and your payer completes the payment inside their own bank. When it settles, the amount is credited to your wallet in that currency.

Available in GBP and EUR.

How it differs from a virtual account

A virtual account is a standing bank account you hand out once, and anything paid into it is yours because the account number identifies you. A collection is the opposite shape: it is opened per payment, for a specific amount, and it is Swappr’s record of that collection — not the account the money lands in — that routes the funds to your wallet.

Two things follow from that, and both are worth designing around:

  • The checkout_url is the deliverable. Creating a collection does not move money; it produces a link. Surface it however suits you — redirect the payer, put it in an invoice, render it behind a button.
  • The destination wallet is fixed when the collection is opened. It is not re-derived at settlement, so the funds land where you expected even if your account configuration changes while the payer is paying.

Before you collect

  • Enable the capability on your key. The API key must carry the collections capability — enable Create collections on the key under API keys. A key without it gets 403 forbidden_collection_create. This is a property of the key itself, so it keeps working regardless of later changes to the team member who created it.
  • You need a wallet in that currency. Collections credit an existing GBP or EUR wallet. If you hold no wallet in the currency, the request is rejected — there is no separate enrolment step beyond having the wallet.
  • Idempotency-Key is required. A retry with the same key returns the same collection and the same checkout_url. That matters more here than elsewhere: a payer may already be part-way through paying, and a second checkout would leave one of them stranded.
  • There is a minimum amount. A collection must be worth more than it costs to collect — see Minimum amount.

POSTCreate collection

POST/v1/collections

Open a hosted checkout and get back the link to send your payer to.

Request body
amount_minorstringRequired

The amount to request from the payer, in minor units (string for values > 2⁵³). Must exceed the cost of collecting it — see Minimum amount.

currencystringRequired

GBP or EUR.

redirect_urlstringRequired

Where the payer is returned after paying or abandoning. Must be https.

narrationstring

Shown to the payer on the checkout page. Up to 255 characters.

referencestring

Your own reference — an invoice number, an order id. Echoed back on every representation of the collection. It is never used to match the payment, so it does not need to be unique.

metadataobject

Arbitrary key-value data stored with the collection and returned unchanged.

Response fields

FieldTypeNotes
objectstringAlways collection.
idstringThe collection id. Use it to retrieve the collection later.
referencestring | nullYour reference, exactly as you sent it. null if you sent none.
amount_minorstringWhat you asked the payer for (minor units).
settled_amount_minorstring | nullWhat actually arrived. null until settlement, and it may differ from amount_minor.
currencystringGBP or EUR.
statusstringSee Lifecycle.
checkout_urlstring | nullWhere to send your payer.
expires_atstring | nullAfter this the checkout can no longer be paid.
settled_atstring | nullWhen the funds settled.
settlement_railstring | nullThe rail the funds arrived over. Only meaningful once settled — and it affects timing, so read it rather than assuming.
failure_code / failure_messagestring | nullPresent on a terminal failure.
envstringtest or live.
metadataobject | nullWhatever you stored.
created_atstringISO 8601.

Errors

CodeHTTPCause
idempotency_key_required400The Idempotency-Key header was absent.
missing_field400amount_minor, currency or redirect_url was not supplied.
invalid_amount400amount_minor is not a positive integer.
invalid_format400redirect_url is not https, or narration / reference / metadata is the wrong shape or too long.
invalid_json / invalid_body400The body is not a JSON object.
forbidden_collection_create403The API key is not enabled for collections.
wallet_frozen403The destination wallet is frozen and cannot receive.
collections_not_enabled403Collections are not enabled for this account. Account-level and not retryable — contact support to enable them.
collections_paused403Collections are temporarily paused for this account. Not retryable — contact support.
idempotency_key_conflict409The same Idempotency-Key was already used with a different body.
currency_not_supported422The currency is outside GBP/EUR, or collections in it are not currently available.
wallet_not_found422You hold no wallet in that currency in this environment.
amount_below_minimum422The amount does not exceed the cost of collecting it. See below.
collection_create_failed502The checkout could not be opened. No payment was requested — retry with a fresh Idempotency-Key.
collections_unavailable503Collections are temporarily not being opened.
collection_cost_unpriceable503The collection could not be priced. Contact support if it persists.
collections_disabled503Collections are paused platform-wide. Temporary and platform-side — retry with backoff.
collections_rail_disabled503The collection rail is unavailable for this environment. Temporary and platform-side — retry with backoff.

The two 503 codes collections_disabled and collections_rail_disabled return the same message — “Collections are temporarily unavailable. Please try again later.” The code is the only thing that distinguishes them; both are temporary and platform-side, so retry with backoff.

Request
# Collect £1,500.00 (150000 minor) from a payer
curl -X POST 'https://api.swappr.me/api/v1/collections' \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount_minor": "150000",
    "currency": "GBP",
    "redirect_url": "https://merchant.example/collections/return",
    "narration": "Invoice 4021",
    "reference": "INV-4021"
  }'
Response
{
  "object": "collection",
  "id": "clx8k2p0000abc123def456",
  "reference": "INV-4021",
  "amount_minor": "150000",
  "settled_amount_minor": null,
  "currency": "GBP",
  "status": "awaiting_user",
  "checkout_url": "https://checkout.example/c/01KZ4M8N2P",
  "expires_at": "2026-07-29T15:25:00.000Z",
  "settled_at": null,
  "settlement_rail": null,
  "failure_code": null,
  "failure_message": null,
  "env": "live",
  "metadata": null,
  "created_at": "2026-07-29T15:00:00.000Z"
}

Lifecycle

A collection moves through these states. Only settled means money reached you.

StatusMeaning
pendingOpened on our side; the checkout is being prepared.
awaiting_userWaiting for the payer to authorise the payment in their bank.
processingThe payer has authorised; the payment is in flight.
pending_settlementAuthorised and clearing. The funds have not arrived yet.
settledThe funds arrived and your wallet has been credited.
failedTerminal. The payment did not complete.
expiredTerminal. The checkout was not paid before expires_at.
cancelledTerminal. The payment was cancelled.
⚠️

Wait for settled — and prefer the webhook. processing and pending_settlement mean a payer has authorised a payment, not that money has arrived, and a payment can still fail after authorisation. Release goods or credit a customer on settled only.

Knowing when it settles

You do not need to poll. A settled collection credits your wallet and fires the standard wallet_funded webhook, which is the same event a virtual-account credit produces — so if you already handle wallet funding, settlement is already wired.

settled_amount_minor is the authoritative figure for what arrived, and it can differ from amount_minor. Reconcile on the settled amount, not the requested one.

Minimum amount

Collecting money costs money, and that cost does not scale to zero. A collection small enough that the cost swallows it can complete successfully while leaving nothing to credit — so Swappr refuses to open one, rather than letting your payer pay for a transfer that delivers nothing.

If the amount is too small you get 422 amount_below_minimum, and the error tells you both figures:

{
  "error": {
    "type": "invalid_request_error",
    "code": "amount_below_minimum",
    "message": "amount_minor 100 is not greater than the provider cost 500 for a GBP collection, so the collection could settle without the funds ever arriving. The minimum accepted amount_minor is 501.",
    "field": "amount_minor",
    "detail": {
      "provider_cost_minor": "500",
      "minimum_amount_minor": "501"
    }
  }
}

Read detail.minimum_amount_minor rather than deriving it — the cost is not always a flat figure, so the minimum is not always “cost plus one”.

GETRetrieve collection

GET/v1/collections/{id}

Fetch one collection. Reading needs no capability beyond authentication — Create collections governs opening one, not looking at it.

A collection belonging to another account, or to the other environment, returns the same 404 as one that does not exist.

CodeHTTPCause
collection_not_found404No collection with that id is visible to this key.
Request
curl 'https://api.swappr.me/api/v1/collections/clx8k2p0000abc123def456' \
  -H "Authorization: Bearer sk_live_..."

GETList collections

GET/v1/collections

Your collections, newest first, scoped to the environment of the API key.

Query parameters
statusstring

Filter by lifecycle status — any value from Lifecycle.

currencystring

Filter by GBP or EUR.

limitinteger

1–100. Defaults to 50.

starting_afterstring

Cursor — the id of the last collection on the previous page.

CodeHTTPCause
invalid_limit400limit is outside 1–100.
invalid_filter400status or currency is not a recognised value.
Request
curl 'https://api.swappr.me/api/v1/collections?status=settled&currency=GBP&limit=20' \
  -H "Authorization: Bearer sk_live_..."
Response
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "collection",
      "id": "clx8k2p0000abc123def456",
      "reference": "INV-4021",
      "amount_minor": "150000",
      "settled_amount_minor": "150000",
      "currency": "GBP",
      "status": "settled",
      "checkout_url": "https://checkout.example/c/01KZ4M8N2P",
      "expires_at": "2026-07-29T15:25:00.000Z",
      "settled_at": "2026-07-29T15:11:42.000Z",
      "settlement_rail": "bacs",
      "failure_code": null,
      "failure_message": null,
      "env": "live",
      "metadata": null,
      "created_at": "2026-07-29T15:00:00.000Z"
    }
  ]
}

Sandbox

A sandbox (sk_test_…) key opens sandbox collections against your sandbox wallets; a live key opens live ones. The two are entirely separate — a sandbox key can neither see nor create a live collection, and Idempotency-Key values are namespaced per environment.