# Error & status codes

Every code you can receive, and the correct reaction.

## Envelope

```json
{ "error": { "type": "insufficient_scope", "message": "Missing required scope: payments:read",
  "request_id": "req_9f21c" } }
```

Always log `request_id` — support can trace any call with it.

## HTTP status map

| Status | Meaning | Retry? |
| --- | --- | --- |
| 400 | Malformed request or bad parameter | No — fix the call |
| 401 | Token missing, expired or revoked | Refresh once, then stop |
| 403 | Permission, suspension or mode problem | No |
| 404 | Object not found for this merchant | No |
| 409 | Conflicting state | No — re-read, then decide |
| 422 | Semantically invalid | No |
| 429 | Rate limited | Yes, honour `Retry-After` |
| 500/502/503 | Our side | Yes, exponential backoff with jitter |

## API error types

| Type | Cause | Fix |
| --- | --- | --- |
| `invalid_request` | Unknown parameter, bad pagination value | Correct the request |
| `invalid_token` | Expired or revoked access token | Refresh; on failure treat the install as gone |
| `insufficient_scope` | Scope not granted | Request it in a new version, re-consent |
| `installation_suspended` | Merchant or app suspended | Back off, keep data |
| `installation_uninstalled` | App was removed | Stop calling, delete data |
| `mode_mismatch` | Test token against live data or vice versa | Use the right credentials |
| `not_found` | Wrong id, or not this merchant's object | Check the id |
| `rate_limit_exceeded` | Quota exhausted | Backoff per `Retry-After` |
| `internal_error` | Unexpected failure | Retry, then contact support with `request_id` |

## OAuth errors

Returned on the redirect (`?error=…`) or from the token endpoint.

| Code | Cause |
| --- | --- |
| `access_denied` | Merchant declined consent |
| `invalid_request` | Missing parameter, e.g. no `code_challenge` |
| `invalid_client` | Wrong client id, or wrong mode |
| `invalid_grant` | Code expired/used, refresh token rotated away, bad `code_verifier` |
| `redirect_uri_mismatch` | URI is not an exact registered match |
| `invalid_scope` | Scope unknown or not approved for your app |
| `unsupported_grant_type` | Only `authorization_code` and `refresh_token` exist |

Authorization codes are single-use and expire after 10 minutes.

## Webhook-side failures

| Your response | Our behaviour |
| --- | --- |
| `2xx` | Delivered, done |
| `3xx` | Treated as a failure — we do not follow redirects |
| `4xx` | Retried on the standard schedule |
| `5xx` / timeout (>10 s) | Retried on the standard schedule |
| Failing 72 h continuously | Endpoint auto-disabled, you are notified |

## Retry recipe

Retry only `429` and `5xx`. Exponential backoff starting at 1 s, factor 2, max 5 attempts, plus random jitter of up to 250 ms.
