Responses & Errors

Every endpoint across Checkout, Payout Embed, and Split returns one of two envelopes. This page is the shape reference — code samples elsewhere in these docs show only the contents of data to stay short.

Successful responses

A 2xxresponse body is always the endpoint's own payload wrapped in the same three fields:

response.json
{
  "success": true,
  "data": {
    "orderId": "pay_...",
    "provider": "STRIPE",
    "checkoutMode": "REDIRECT",
    "redirectUrl": "https://checkout.stripe.com/..."
  },
  "timestamp": "2026-08-24T18:02:11.482Z"
}
  • success — always true here.
  • data— the shape documented for that specific endpoint (e.g. Checkout's orderId/redirectUrl, or a Split's SplitResponse).
  • timestamp — ISO 8601, when the response was generated, not when anything happened business-wise.
This envelope is for API responses only. webhook delivery payloads (see Webhooks) are the raw event object with no { success, data } wrapper.

Errors

A non-2xx response carries the same fields on every endpoint:

error.json
{
  "success": false,
  "statusCode": 422,
  "timestamp": "2026-08-24T18:02:11.482Z",
  "path": "/api/commerce/integration/checkout",
  "method": "POST",
  "message": "Subscriptions are only supported for Stripe checkout",
  "error": "UnprocessableEntityException"
}
  • success — always false.
  • statusCode— the HTTP status, duplicated in the body so you don't need to read response headers to branch on it.
  • message — a string, or an array of strings for a request that failed multiple field validations at once (see below).
  • error— the exception class name, useful for logging/grouping; not guaranteed to be stable across a refactor, so don't branch your integration's logic on it — branch on statusCode instead.
  • code— present on some errors as a stable machine-readable identifier; omitted when there isn't one.
  • path / method — the request that failed, echoed back for logging.

Field validation (400)

A request body that fails validation (missing a required field, wrong type, a string over its max length) returns every failing field at once as an array, not just the first one:

validation-error.json
{
  "success": false,
  "statusCode": 400,
  "timestamp": "2026-08-24T18:02:11.482Z",
  "path": "/api/commerce/integration/checkout",
  "method": "POST",
  "message": [
    "amountMinorUnits must be a positive integer string",
    "returnUrl must be a URL address"
  ],
  "error": "BadRequestException"
}

Common status codes

  • 400 — request body failed validation (see above).
  • 401 — the Authorization header is missing, or the API key is invalid/revoked.
  • 403— the API key is valid but isn't scoped for this route (e.g. calling Split without split:write).
  • 404— the resource doesn't exist, or exists but belongs to a different company. Those two cases are intentionally indistinguishable — existence is never leaked across companies.
  • 422 — the request was well-formed but rejected by a business rule (e.g. a subscription opt-in with provider: "PAYPAL", or a Split roster row referencing a contractorPaymentProfileIdthat isn't an active creator).
  • 429— you've exceeded the per-route rate limit (each write endpoint documents its own limit). Back off using the Retry-After response header.
  • 5xx — something failed on our end. messageis always the generic "Something went wrong. Please try again." here — the real internal error is logged server-side, never sent to the client.
A 429response is built by the rate limiter itself, ahead of the rest of the API, so it doesn't carry the success: false field the other error cases do — everything else on it (statusCode, message, error, etc.) is the same.
rate-limit.json
{
  "statusCode": 429,
  "timestamp": "2026-08-24T18:02:11.482Z",
  "path": "/api/commerce/integration/checkout",
  "method": "POST",
  "message": "Rate limit exceeded, retry in 1 Minute",
  "error": "TooManyRequests"
}