Versioning
All endpoints are prefixed with/v2. The version is part of the URL path, not a request header.
Request Format
All request bodies use JSON (Content-Type: application/json), except file uploads which use multipart/form-data.
Response Format
All responses return JSON.- Dates are ISO 8601 strings in UTC (e.g.,
2026-01-15T09:30:00.000Z). - Optional fields with no value are omitted from the response, never sent as
null— a field likeclientReferenceIdis simply absent when none was supplied. - A few fields are nullable — when a field’s description says it can be
null(for example the wire-receive bank details on a deposit, orpayout.railon webhook payloads),nullmeans “no value yet / not applicable”, distinct from an omitted optional field. These fields are always present within their parent object — but the parent itself may be optional. - Empty collections are returned as
[], nevernull.
IDs
Resources use prefixed IDs (e.g.,cus_abc123, app_xyz789). The prefix identifies the resource type:
Pagination
List endpoints use cursor-based pagination. Pass query parameters to control paging:
All list responses use the
{data, meta} envelope shown above. meta.mode is "cursor" or "offset" depending on the endpoint.
meta.nextCursor as the cursor parameter in the next request to fetch the next page. When nextCursor is null, there are no more results.
A cursor is tied to the sort order it was issued under. If you change the sort (sortBy/sortOrder) you must start again from the first page without a cursor — reusing an older cursor under a different sort returns 400 INVALID_CURSOR instead of a misleading page.
Cursors are opaque strings. Do not parse or construct them manually.
Errors
All error responses follow RFC 9457 (Problem Details for HTTP APIs):Always match on the
type field for error handling, not status or title.
The type code is stable across API versions.VALIDATION_ERROR (400), ONBOARDING_NOT_READY (422), and DOCUMENT_IDS_NOT_FOUND (400) include an errors array with one entry per offending field. Each entry has a pointer (JSON pointer to the invalid field), a detail (what is wrong, never echoes the rejected value), and an optional category (field, document, or individual) when the entry came from the requirements validator. category lets you group blockers without parsing pointers; it’s omitted on top-level schema rejections.
Idempotency
POST, PATCH, and PUT requests support idempotency via theidempotency-key header. Only successful responses are cached. Within a 5-minute window:
- Same key + same body after a success — returns the cached response instead of creating a duplicate resource. Replayed responses carry an
Idempotency-Replayed: trueheader. - Same key + different body, after a success or a
5xx— rejected with409 IDEMPOTENCY_KEY_CONFLICT. The key is bound to its first request body for the 5-minute window, so a modified payload never silently runs under another request’s key; send a genuinely new request with a fresh key. - Retry after a
4xx— client errors commit nothing, so retrying with the same key is re-evaluated against current state, and a corrected body is accepted on the same key (fix a validation error and retry — no new key needed). On order execution, for example, a422 INSUFFICIENT_FUNDSclears on retry once the balance covers the request. Payouts additionally bind the key to a terminally failed attempt — to retry a failed payout, use a fresh key.
onboard-{customer-external-id}).