Get started

Errors

Every error returns JSON with a stable code, a human-readable message, and an optionaldetails object for field-specific guidance. HTTP status follows the closest semantic match — 4xx for caller errors, 5xx for ours.

Error body shape

{
  "ok": false,
  "error": {
    "code": "insufficient_scope",
    "message": "This endpoint requires scope \"customers:write\".",
    "details": {
      "requiredScope": "customers:write",
      "grantedScopes": ["payments:read", "balance:read"]
    }
  }
}

Every API response — success or failure — is wrapped in an envelope:{ok: true, data: ...} on 2xx, or{ok: false, error: {...}} on 4xx/5xx. Branch on error.code for programmatic handling — messages are subject to copy edits.

Codes

StatusCodeMeaning
400validation_failedBody or query failed Zod schema validation. The response includes a `details` array with field-level issues.
401unauthorizedMissing / malformed Authorization header, OR the Bearer token does not match any stored key hash, OR the key has been revoked or has expired. The `message` field distinguishes the cases.
403ip_not_allowedSource IP is not in the key's allowlist.
403insufficient_scopeThe key was not issued with the scope this endpoint requires. The required scope is echoed in the error body.
403mode_mismatchThe endpoint requires a live (or test) key but the caller supplied the other.
403merchant_inactiveThe merchant account is suspended or closed.
403tier_requiredAction gated behind a higher KYC tier than the merchant has reached.
402plan_requiredThe merchant's plan does not include this feature.
402key_limit_reachedThe merchant has hit their plan's live-key cap. Revoke an existing key or upgrade.
404not_foundThe requested resource doesn't exist or doesn't belong to the calling merchant.
409idempotency_key_in_useThe Idempotency-Key was reused with a different request body. Supply a fresh key.
409terminal_statusAttempt to transition a resource that's already in a terminal state (e.g. mark-paid a cancelled payout).
422stripe_bank_rejectedUnderlying Stripe call refused the request. `details.stripeCode` carries the upstream code.
429rate_limitedPer-merchant request budget exhausted. Retry after `resetSeconds`.
429rate_limited_keyPer-key request budget exhausted (half the merchant cap).
500internal_errorUnexpected server-side failure. Try again or contact support if it persists.
502screening_unavailableCompliance screening provider timed out — retryable.
503stripe_disabledThe deployment is not configured for card processing.

Rate limiting

Limits are per merchant plan (10 req/s starter → 250 req/s enterprise), with a per-key sub-budget at half the merchant cap. Both 429 codes include resetSeconds in the error body so callers can back off cleanly.