# Errors and retries

## The error envelope

Every error has the same shape:

```json
{ "error": { "code": "not_found", "message": "no data for this object" } }
```

Match on `code`, which is stable. `message` is for humans and may change.

| Status | `code` | Meaning |
|--------|--------|---------|
| `400` | `bad_request` | Malformed JSON, an unknown field, or a failed validation. `message` says which. |
| `401` | `unauthorized` | Missing, invalid, expired or revoked credentials. See [Authentication](https://sigwise.ai/docs/guide/authentication.md). |
| `402` | `payment_required` | The prepaid balance is empty. See [Billing](https://sigwise.ai/docs/guides/billing.md). |
| `403` | `forbidden` | The credential can't do this, e.g. a console-only action with an API key. |
| `404` | `not_found` | The object, signal, rule, endpoint or key doesn't exist. |
| `409` | `conflict` | The request conflicts with the current state, e.g. backfilling a disabled signal. |
| `422` | `idempotency_key_reused` | An `Idempotency-Key` was already used for a different object. |
| `429` | `rate_limited` | Too many requests. `/v1` requests are limited per account (3,000 a minute by default), and so are concurrent `wait: true` analyses. Wait for `Retry-After` seconds. |
| `502` | `internal_error`, `payment_provider_error` | An upstream service (the analyzer, Stripe) failed. |
| `503` | `analyzer_busy` | The analyzer is throttling a `wait: true` request. The events were recorded; don't resend them. After `Retry-After`, request an analysis or read the answers later. |
| `503` | `payments_not_configured`, `not_ready` | A feature is unavailable on this deployment, or the service is starting. |
| `500` | `internal_error` | Something went wrong on our side. |

## Retrying

- **Safe to retry:** `GET`, `PUT` and `DELETE` requests, after a network
  error, a timeout, a `429` or a `5xx`. Back off exponentially with jitter.
- **Safe to retry with an idempotency key:** event ingestion. Send an
  `Idempotency-Key` header and retry with the same key; the events are recorded
  once. See [Ingesting events](https://sigwise.ai/docs/guides/ingesting-events.md#retrying-safely).
- **Not safe to retry blindly:** other `POST` requests, and ingestion without
  an `Idempotency-Key`. A timeout doesn't tell you whether the events were
  recorded, and retrying may record them twice.
- **Never retry** other `4xx` errors unchanged; fix the request.

The SDKs follow these rules: they retry idempotent requests twice by default,
honor `Retry-After`, and never retry a `POST`.

## Timeouts

Most requests answer in milliseconds. [Direct moderation](https://sigwise.ai/docs/guides/moderation.md)
waits for the analyzer and can take seconds; give it a longer timeout. The
server itself gives up on any request after 30 seconds.
