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_034A0gCCVsxdV2PjHLx9k1, app_034A0gCCVlvyhfAZssklTs). 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 cursor with direction=forward. When nextCursor is null on a forward page, there are no more results.
To walk back from a page you hold, send its meta.previousCursor as cursor with direction=backward. When previousCursor is null on a backward page, you reached the start of the list. A backward page always carries a nextCursor. Do not loop on nextCursor while direction=backward: it moves you one row, not one page.
To filter on several values, repeat the key, for example status=a&status=b. Bracket and comma forms are not accepted. A filter that takes one value refuses a repeated key. The status filter on the webhook delivery list is one example.
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, except that it can name a routing number), 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 kept, and they are kept for 30 days:
- Same key + same body after a success — returns the original 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 full 30 days, 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 are the exception: a payout that reached a terminal failed state stays bound to its key, so a same-key retry returns that original payout rather than re-evaluating — use a fresh key to submit a corrected payout.
onboard-{customer-external-id}).