Error Catalog
Every API endpoint shares the same error codes. Look up the HTTP status or error.code to find the likely cause, whether it's retryable, and what to do.
Every error response looks like { ok: false, requestId, correlationId, error: { code, message, nextAction } }. Branch your code on error.code (a stable string) — message/nextAction are human-readable and their wording may change. Full envelope shape: API Fundamentals.
Validation & request shape
| HTTP | code | Common cause | Retryable? |
|---|---|---|---|
| 400 | VALIDATION_ERROR | Missing field, wrong type, or out of allowed range (e.g. a non-integer amount) | Fix the field and resend — retrying as-is won't help |
| 400 | IDEMPOTENCY_KEY_REQUIRED | A financial mutation (payment/refund/checkout) was sent without an Idempotency-Key header | Add the header and resend |
Auth & permissions
| HTTP | code | Common cause | Retryable? |
|---|---|---|---|
| 401 | UNAUTHENTICATED | Missing Authorization: Bearer, or the key doesn't exist / was revoked | Use a valid key and resend; retrying as-is won't help |
| 401 | TOKEN_EXPIRED | The access token of a signed-in API has expired (15 minutes) | Call the matching */Refresh with the refresh token, then resend |
| 401 | INVALID_TOKEN | The access token is malformed or its signature is wrong | Sign in again for a new token |
| 401 | INVALID_REFRESH_TOKEN | The refresh token was already used, signed out or expired | Sign in again; refresh tokens rotate and an old one never works again |
| 403 | WRONG_TOKEN_TYPE | A token of the wrong kind (e.g. a merchant token on Admin*) | Use the sign-in that matches the API |
| 404 | ROUTE_NOT_FOUND | The path doesn't exist (e.g. still using an old /v1/... path) | Check GET /api/OpenApi and use the correct path |
| 403 | SCOPE_DENIED | The key lacks the scope this operation needs | Create a key with the right scope |
| 403 | SCOPE_DISABLED | The requested scope (e.g. payouts:*) isn't publicly available yet | Not retryable — this capability isn't live, see Sandbox vs Production |
| 403 | WORKSPACE_ACCESS_DENIED | The key can only access the workspace it was created in, but the request named a different one | Check x-workspace-id or use the right key; not retryable |
| 403 | ENVIRONMENT_DENIED | The request specified an environment other than sandbox (Production isn't available yet) | Use sandbox; not retryable |
Resource state
| HTTP | code | Common cause | Retryable? |
|---|---|---|---|
| 404 | NOT_FOUND | Wrong ID, the resource belongs to another workspace, or it was deleted | Verify the ID and resend; retrying as-is won't help |
| 409 | CONFLICT | Conflicts with existing data (e.g. a duplicate unique value) | Adjust the field and resend |
Rate limiting
| HTTP | code | Common cause | Retryable? |
|---|---|---|---|
| 429 | RATE_LIMITED | This key exceeded its request quota (default 120 per 60 seconds) | Retryable — wait the number of seconds in Retry-After, see API Fundamentals |
Server errors
| HTTP | code | Common cause | Retryable? |
|---|---|---|---|
| 500 | INTERNAL_ERROR | An unexpected server error | Retry once or twice with backoff; if it persists, contact support with the requestId |
Payment "non-error" states (like a pending-reconcile
unknown payment, or the PAYMENT_UNKNOWN_NEEDS_RECONCILE code returned in a refund preview) aren't HTTP errors — they're business fields inside a 200 response. See Refunds & Idempotency; they're not in this table.When to contact support: if the same
requestId keeps returning 500 INTERNAL_ERROR after a retry, or you get an error.code not listed here, include the requestId/correlationId when you reach out — it makes triage much faster.
ReBate Pay Docs