Skip to main content
Migrate your integration from API v1 and v2 to v3 in two passes:
  • Part 1, the concept. Read this first. v3 is a remodel, not a rename: if you map old endpoints one-to-one you will fight the API. Ten minutes here saves days later.
  • Part 2, the API. Endpoint-by-endpoint mapping, request examples, state machines, and a migration checklist.
Grounded in the production OpenAPI spec (platform.swipelux.com/openapi.json). v1 and v2 remain live and are not yet deprecated; all new capability, recipient, task, and quoting features ship on v3 only. Subscribe to the api.deprecation webhook event for sunset notices.
Contents. Part 1: 1.1 why v3 exists, 1.2 object model, 1.3 per-capability readiness, 1.4 task loop, 1.5 money movement, 1.6 state machines, 1.7 conventions, 1.8 golden path. Part 2: 2.1 customers, 2.2 capabilities, 2.3 tasks and submissions, 2.4 accounts, 2.5 recipients and destinations, 2.6 quotes and transfers, 2.7 webhooks, 2.8 sandbox, 2.9 legacy endpoints, 2.10 migration order, 2.11 gotcha checklist.

Part 1, the concept

1.1 Why v3 exists

v1 and v2 grew four overlapping ways to make a customer payment-ready: /rails, /banks, /accounts/applications, and the business rail-applications surface, each with its own status vocabulary. Document collection (/documents, KYC imports, verification SDK tokens) was disconnected from the thing it actually unblocked. v3 collapses all of it into six resources, the customer plus five things it owns:

1.2 The object model

Two structural rules to internalize:
  1. Capabilities gate everything. Accounts are provisioned under a ready capability; quotes are priced against a capability. Onboarding equals getting the capabilities you need to ready.
  2. Tasks attach anywhere. A capability, an account, or an in-flight transfer can carry openTaskIds. Wherever you see them, the loop is the same: read task, submit answers, wait for review, re-read the parent.

1.3 Readiness is per capability, not per customer

v1 tangled /rails readiness with a customer-wide KYC gate. In v3 there is no customer status: a customer can be fully usable on stablecoin_transfers while their sepa capability still has open tasks. Pooled-account capabilities generally go ready faster than named ones, so start transacting on what is ready instead of waiting for everything. If your v1 or v2 code drives UI badges off customer verification status, rewrite it:
  • “Can they transact on X?” becomes capability X status == "ready".
  • “Do they need to do something?” becomes any task with status action_required (the capability typically shows restricted with statusReason.resolution: "complete_tasks").
  • “Are we waiting on Swipelux?” becomes tasks in_review, capability pending.

1.4 The task loop

Everything the old document and KYC surface did is now this one loop: Key properties:
  • A task carries requirements[], the individual asks. Each has a per-task requirementId, a stable key naming the ask (for example proof of address, deduplicate your UI by it), and a typed request describing exactly what input is wanted (text, date, select, document, attestation, and so on).
  • Submitting is review-gated: it never mutates capability or account state directly, acceptance does. One exception: profile answers write through to the customer profile on submit (2.3). After submitting, poll the task or the parent resource.
  • taskRevision (echo of the task’s revision) is a concurrency guard: if the task changed since you read it, re-read and rebuild your answers.
  • absence is a first-class answer (“I don’t have this because …”), use it instead of leaving requirements dangling.

1.5 Money movement

One flow for payins, payouts, and stablecoin moves. There is no direction input, you never declare payin vs payout. The in and out currency shapes derive a read-only direction on the quote and transfer: fiat_to_stablecoin (payin), stablecoin_to_fiat (payout), or stablecoin_move.

1.6 One state machine per resource

Every status-bearing resource has its own enum, and every non-happy status carries a structured reason. Accounts, applications, and transfers share the shape { code, message, actor, retryable }: accounts and applications expose it as statusReason, transfers as stateDetail. actor says who must act (customer, developer, provider, network, swipelux), retryable says whether retrying can help. Capabilities use { code, resolution, message }, where resolution (complete_tasks, wait, contact_support, none) says what moves the capability forward. code values are an open, append-only catalog: branch on resolution (or actor plus retryable), and tolerate codes you have never seen. States this guide does not walk through (rejected, suspended, disabled, failed, canceled) are terminal or support-driven; per-resource definitions are in the spec. Transfer, drawn out:

1.7 Conventions

Idempotency rules worth internalizing before writing code:
  • Reusing a key with a different body is a 409 idempotency_conflict for as long as the key is retained (at least 7 days), so never plan to reuse a key. Generate a fresh UUID per logical operation and persist it with your job.
  • Replay covers errors too: if the original request ended in a terminal 4xx, the same key plus body returns that same problem response again.
  • Two concurrent requests with the same key: one wins, the other gets 409. Retry the loser after the winner settles; the replay returns the original response.

1.8 The golden path


Part 2, the API

2.1 Customers

Create, discriminated on type (illustrative values, field names per spec):
  • Creation is progressive: { "type": "individual" } alone is a valid create. Missing facts never invalidate the customer, they surface later as intake tasks on the capabilities that need them.
  • Businesses carry business plus registration data. v1’s shareholder CRUD maps onto related parties, widened to cover directors, officers, and owners: create them inline at customer creation (each gets a stable rp_ id) or manage them through the dedicated related-parties endpoints.
  • No customer status field, see 1.3.
  • Existing customers carry over: customers created on v1 or v2 are addressable by the same id on v3 endpoints. The v3 read is a sanitized view, legacy values that fail v3 validation come back absent. After your first v3 write that view becomes permanent: absent values do not come back on their own. So enrich early, budget a one-time pass that PATCHes the full profile in from your own records before relying on v3 reads. v1 metadata is a separate namespace and is not carried over, re-set it on v3.
  • externalId is first-class and unique across your customers on v3, per environment (409 duplicate_external_id). Archiving a customer does not release its externalId, clear it by PATCH before DELETE if you intend to reuse it.
  • DELETE is an archive cascade (no restore; ids never reused). It is blocked with 409 customer_has_active_resources plus blockingResources[] while any non-archived account or in-flight transfer exists.
  • PATCH merge rules: explicit null clears a nullable field, arrays replace wholesale (except inline related parties, which upsert by id), metadata keys merge. Full schemas and list filters are in the OpenAPI spec.

2.2 /rails, /banks, applications become Capabilities

  • A capability equals method (ach, wire, rtp, pix, sepa, swift, spei, pse, transfers_3_0, faster_payments, sepa_instant, uaefts, card, stablecoin_transfers, and so on) plus accountType (pooled or named, null for non-bank methods) plus directions (payin or payout). The public capabilityId is the qualified pair (sepa_pooled, ach_named) or the bare method for card and stablecoin_transfers.
  • Each capability request spawns an application, the per-attempt record under .../capabilities/{capabilityId}/applications (plus /{applicationId}/history), with its own statuses (1.6) and statusReason. It is the audit trail of a request; day-to-day, poll the capability itself.
  • capabilities/supported returns availability (available, beta, or disabled), eligibility, and institutions on offer. Bank selection happens at request time via the optional institutions array, there is no separate /banks resource. Omitting it (or sending []) selects every default institution; isDefault: true is a customer-and-capability-specific flag, not a global one. A non-empty list overrides the defaults, and a bank-backed capability with no applicable default returns 422 capability_institutions_required. Institution ids are opaque, tolerate new ones.
  • stablecoin_transfers is auto-granted at customer creation and born ready (so it is never requested and not cancelable). card is individual-only.
  • openTaskIds on the capability is your “what do I do next” pointer. Open equals action_required or in_review, and the rollup includes shared customer-level tasks reached through active dependencies.
  • cancel works only from pending or restricted and with no blocking resources, otherwise 409 capability_not_cancelable, whose problem body lists blockingResources. Re-requesting after cancel is a fresh create with a new idempotency key.
  • Poll the GET. Capability state refreshes when you read it; poll GET .../capabilities/{capabilityId} or subscribe to capability.status_changed, do not cache.
  • Requesting one method can make related methods available at once, treat capabilities as a set you re-read, not a single row you track.
  • Use tasks-preview to show onboarding asks before committing to a request.
  • Verification is not one-time: new tasks can appear on an already-ready capability (periodic or event-driven re-verification). Keep the task loop wired for the whole customer lifetime, not just onboarding.

2.3 Documents and KYC become Tasks and Submissions

Naming note. These endpoints briefly shipped as requirements and fulfillments. Since 2026-08-02 the public names are tasks and submissions. The rename covered the resources and endpoint paths only, the requirements[] array inside a task and its requirementId keep those names.
Every legacy document surface maps to the same replacement: read GET /v3/customers/{customerId}/tasks, answer with POST .../tasks/{taskId}/submissions. Around that loop:
  • Raw file storage: POST/GET/DELETE /v3/customers/{customerId}/documents (plus /{documentId}), upload once with your API key, then reference document ids in submission answers. This replaces every upload-token and direct-upload intake.
  • Net-new reads: GET /v3/tasks (merchant-wide inbox), GET /v3/transfers/{transferId}/tasks, GET .../tasks/{taskId}/history, GET .../tasks/{taskId}/submissions (plus /{submissionId}).
Submission (illustrative):
  • Answer types: profile, text, date, single_select, multi_select, boolean, attestation, document, resource_reference, absence. Each requirement’s request object tells you which type it expects.
  • A submission must answer every actionable requirement in the current round, with the exact taskRevision you read. Partial submissions are rejected.
  • profile answers write through: they update the customer profile via the normal validation path and immediately re-evaluate every capability referencing the same intake work. Sibling intake tasks whose requirements are all satisfied close automatically.
  • Requirements can form alternative groups (alternativeKey): submit exactly one of the group.
  • changes_requested bumps remediationRound and carries reviewFeedback. Re-read the task, submit again with a fresh idempotency key.
  • Hosted verification URLs appear only on customer-scoped task detail (GET /v3/customers/{customerId}/tasks/{taskId}) and only while the session is actionable; lists and GET /v3/tasks/{taskId} are deliberately URL-free.
  • Terms of service is a task too: openTaskIds can include a category: "terms_of_service" task whose hosted acceptance page is linked the same way (customer-scoped detail only). Generic submissions cannot accept terms, and KYC approval never implies terms acceptance.
  • Tasks are scoped per capability, so the “same” ask (for example proof of address) can appear once per capability. Deduplicate in your UI by requirement key.
  • No translation layer: posting to /v1/documents will not unblock v3 capabilities. Once a customer is on v3, drive all asks through tasks.

2.4 Accounts and wallets

Create, discriminated on origin plus type. Issued bank accounts take a single method; external bank accounts take a methods array instead (sending method there is rejected):
  • country on issued bank accounts is optional (defaulted per method); on external bank accounts provide it explicitly. Wallet accounts carry no country at all.
  • settlement.accountId is required on issued bank accounts: it names the issued wallet account that receives settled funds from deposits into the bank account.
  • Issued accounts expose details (IBAN or routing plus account or address), versioned routing (deposit coordinates can rotate, always render the latest read), fees, balances.
  • Networks: polygon, ethereum, base, arbitrum, optimism, bsc, avalanche.
  • The capability gate applies to issued accounts only: creating one against a non-ready capability fails with a capability-coded error, request the capability first (2.2). External accounts need no capability (and no customer approval); they get request-schema and bank-detail validation only.
  • Issued bank accounts are born provisioning with details: null. Poll the account or watch account.status_changed until ready.
  • DELETE archives, never hard-deletes. Accounts referenced by in-flight transfers return 409 account_has_active_transfers. Retry after those transfers reach a terminal state.
  • Net-new in v3: Rules, standing instructions on an issued wallet account (POST/GET /v3/customers/{customerId}/rules, GET/PATCH/DELETE .../rules/{ruleId}) that auto-sweep incoming funds to another account or a wallet destination. No v1 or v2 equivalent.

2.5 Recipients and destinations

v2 had no recipient concept. If you are on v2 and pay out to third parties, this is new surface, not a rename.
  • Recipient equals who: individual (first and last name) or business (company name), with required relationship (employee, contractor, vendor, subsidiary, merchant, customer, landlord, family, other). Recipients and destinations are for third parties only. A first-party payout does not use a recipient at all: target one of the customer’s own acc_ accounts as the quote destinationId (2.6).
  • Destination equals where: typed per method, sepa (iban, bic optional), ach or wire (routing plus account), swift (full coordinates plus optional intermediary), spei (clabe), pse, transfers_3_0 (cbu), and so on, plus wallet destinations. Each destination has its own status. Watch destination.status_changed.
  • Fiat destinations require the recipient’s complete address (street, city, postal code, country) before creation. Missing pieces fail with 422 recipient_address_required. Wallet destinations skip the address but require top-level ownership (self_custodied, or custodial with a custodian name).
  • Beneficiary-name accuracy matters: receiving banks match the account’s legal name. Send exact legal first plus last name or company name, not a display nickname.
  • Per-method destination field schemas are in the OpenAPI spec.

2.6 Quotes and transfers

  • destinationId takes an acc_ (customer-owned account) or dst_ (recipient destination) id. Fiat-funded quotes (payins) must target an acc_ account, a dst_ target always means a payout (422 quote_direction_invalid otherwise).
  • externalId on quotes and transfers is a non-unique correlation reference (echoed on reads, filterable on lists). The per-environment uniqueness rule (2.1) applies to customer externalId only.
  • Execute a quote exactly once, before expiresAt. An expired quote fails with 409 quote_expired, a second execution with 409 quote_already_executed (the problem carries the existing transferId).
  • Transfer cancellation is not yet supported: POST .../cancel returns 409 transfer_not_cancelable in every state. Today’s canceled transfers come from the funding window expiring on an unfunded payin, not from this endpoint.
  • Payins start awaiting_funds: render GET .../instructions to the payer, bank coordinates plus reference or memo code for fiat, deposit address for crypto. The reference code is how the deposit is matched. Always display it.
  • state plus stateDetail for machine-readable substates; action_required means a compliance task is attached (openTaskIds, GET .../tasks), answer via submissions.
  • Inbound deposits detected on issued accounts appear as transfers with origin: "inbound_deposit" (vs "quoted").
  • Payment-network references consolidated under references: transactionHash, traceNumber, imad, uetr, explorerUrl, returnedTransferId.
v1 status translation: Two migration warnings:
  • Transfers do not cross versions. Transfers created on v1 or v2 are not readable from v3. The list omits them and GET /v3/transfers/{transferId} 404s. Cut over creation first, keep the v1 read path until those transfers reach terminal states, then drop it.
  • No token swaps. stablecoin_move requires the same currency in and out: USDC to USDT fails with 422 recipient_destination_invalid carrying a currency_mismatch field error. Same network on both sides, no bridging, and wallet-to-wallet moves currently support zero-fee delivery only: a quote whose platform or developer fee is non-zero fails with 422 amount_not_deliverable.

2.7 Webhooks

Event catalog: customer.created, customer.updated, customer.archived, capability.created, capability.status_changed, application.status_changed, recipient.status_changed, destination.status_changed, account.created, account.status_changed, account.details_changed, transfer.created, transfer.state_changed, api.deprecation.
  • GET /v3/webhooks/portal returns a hosted management-portal URL for delivery logs, retries, and manual replay.
  • transfer.created is currently delivered with the legacy v1 payload shape (the v3 envelope activates when v1 webhooks sunset). Treat it purely as a hint and GET the transfer; do not build against its body.
  • Events are hints: on receipt, GET the resource and act on the read. Never build state off event payloads or ordering. Delivery is at-least-once and may be delayed or reordered. Deduplicate by event id, and recover missed events with each list’s inclusive updatedAfter filter.
  • Subscribe to api.deprecation, the machine channel for version sunsets.
  • No task.* event today: after submitting, poll the task or its parent.

2.8 Sandbox

Same base URL; the sandbox API key selects the environment. The v3 sandbox simulates the review loop end to end: create a task, submit against it, review it to accepted or rejected, watch the capability unblock. Rehearse your remediation UX before production. Both sandbox-created tasks and the regular intake tasks that appear on requested capabilities are reviewable this way; as in production, no task webhooks fire, poll (2.7).

2.9 Legacy endpoints with no v3 replacement

These have no v3 replacement. Most stay on v1 unchanged (keep your existing calls); two are retired outright (see Disposition): Every other public v1 or v2 endpoint appears in a mapping table above.

2.10 Suggested migration order

Each step ships independently; v1 or v2 and v3 run side by side against the same customer base. Rehearse every step against your sandbox key (2.8) before repeating it in production.
1

Plumbing

Idempotency-Key on all effectful requests (POST, PATCH, PUT, DELETE; sandbox endpoints exempt); money as strings; cursor pagination helpers.
2

Webhooks

Register v3 endpoints per event, including api.deprecation. The v1 single-endpoint config is a separate surface, leave it in place; the two run side by side until the drain in step 9.
3

Profile enrichment

PATCH /v3/customers/{id} with the full profile you hold (v1 collected less than v3 exposes) and re-set metadata. Make this deliberately the first v3 write per customer: it fills the sanitized view before that view becomes permanent (2.1).
4

Reads

Point customer, capability, and account reads at v3; rewrite customer-status logic per 1.3. Only after step 3, unenriched reads come back with legacy-invalid fields absent.
5

Onboarding writes

Create via POST /v3/customers; request capabilities instead of /rails, /banks, or applications; build the task loop (largest net-new UI work, tasks-preview helps show asks upfront). From this point, stop posting /v1/documents for v3-driven customers, they do not unblock capabilities (2.3).
6

Accounts

Issue via v3; move imports to origin: external.
7

Payouts

Recipients plus destinations, then quote and transfer.
8

Payins

Quote, transfer, instructions; keep rendering the reference code.
9

Drain

Transfers do not cross versions (2.6). Keep the v1 or v2 read path and the v1 webhook endpoint for transfers created there, dual-read until they reach terminal states, then drop the old client and the v1 webhook config.

2.11 Gotcha checklist

  • Fresh UUID per logical operation, persisted with your job and reused on retry; never reuse a key with a changed body (409 idempotency_conflict). Sandbox endpoints are exempt from the header.
  • Enrich existing customers (PATCH the full profile, re-set metadata, it does not carry over) before any other v3 write, the first v3 write makes the sanitized view permanent.
  • externalId is unique per environment and not released by archive, clear it by PATCH before DELETE if you plan to reuse it.
  • No customer status field exists, derive readiness per capability.
  • action_required and in_review both mean an open task.
  • Submissions are review-gated (submitting is not the same as unblocked) and must answer every actionable requirement with the exact taskRevision. On mismatch, re-read and rebuild.
  • No task.* webhook exists, poll the task (or its parent) after every submit.
  • changes_requested retry equals re-read the task, fresh answers, fresh idempotency key.
  • Posting to /v1/documents never unblocks a v3 capability, once a customer is on tasks, drive every ask through tasks.
  • New tasks can appear on an already-ready capability, keep the task loop wired after onboarding, not just during it.
  • Capability cancel works only from pending or restricted with no blocking resources (409 capability_not_cancelable); re-requesting after cancel is a fresh create with a fresh idempotency key.
  • Capability must be ready before issuing accounts under it or quoting against it.
  • Quote has amount on exactly one side; no direction field; execute exactly once before expiresAt (409 quote_expired or 409 quote_already_executed).
  • Stablecoin moves are same-currency, same-network only (USDC to USDT fails 422); wallet-to-wallet supports zero-fee delivery only.
  • DELETE archives, never hard-deletes. Account delete is blocked by in-flight transfers (409 account_has_active_transfers); customer delete additionally by any non-archived account (409 customer_has_active_resources, blockingResources[] names them).
  • v1 or v2 transfers are invisible to v3 reads (list omits, GET 404s), dual-read until drained, then drop the old paths.
  • Deposit routing and instructions can rotate, always render the latest GET, and always show the reference code.
  • Webhooks are hints; GET is truth, deduplicate by event id, recover missed events with updatedAfter.

Next step

Start at step 1 of the migration order (2.10), idempotency keys, money as strings, cursor pagination, and rehearse each step against your sandbox key (2.8) before repeating it in production.