# Errors (/docs/developer-resources/api-basics/errors-and-error-handling)

Every Monime API response says whether the request succeeded. A failed request returns an HTTP status of 400 or above and an **error** object that says why, so your code can decide whether to fix the request, retry it, or stop.

## The error response

A successful response has `"success": true` and a `result`. A failed one has `"success": false` and an `error`:

```json
{
  "success": false,
  "messages": [],
  "error": {
    "code": 409,
    "reason": "idempotency_key_in_use",
    "message": "Conflict: Idempotency key reused with a non-identical request.",
    "details": []
  }
}
```

| Field     | What it is                                                                                       |
| --------- | ------------------------------------------------------------------------------------------------ |
| `code`    | The HTTP status code, repeated in the body                                                       |
| `reason`  | A short, stable name for the error, such as `too_many_requests`. **Branch on this in your code** |
| `message` | A sentence for people, which can change: log it, but don't match on it                           |
| `details` | More about the error, such as the fields at fault, when there is more to say                     |

Every response also carries a `Monime-Request-Id` header. Log it with each failure, and include it when you contact Monime support: it identifies the exact request.

## Status codes

| Status      | What it means                                                                     | What to do                                                                                                                         |
| ----------- | --------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `2xx`       | The request succeeded                                                             | Use the `result`                                                                                                                   |
| `400`–`499` | The request can't succeed as it is, such as invalid input                         | Fix the request; sending it again unchanged fails again                                                                            |
| `401`       | The access token is missing, invalid or expired                                   | Check the `Authorization` header, or create a new token. See [Authentication](/docs/developer-resources/api-basics/authentication) |
| `403`       | The token is valid, but lacks the permission or Space access the request needs    | Check the token's scopes and the `Monime-Space-Id` header                                                                          |
| `404`       | The object doesn't exist, in this Space and environment                           | Check the ID, the Space and whether you used a test or live token                                                                  |
| `409`       | `idempotency_key_in_use`: an `Idempotency-Key` was reused for a different request | Fix how your code chooses keys. See [Idempotency](/docs/developer-resources/api-basics/idempotency)                                |
| `429`       | `too_many_requests`: a rate limit was reached. Nothing happened                   | Wait the seconds in `Retry-After`, then retry. See [Rate limiting](/docs/developer-resources/api-basics/rate-limiting)             |
| `500`–`599` | Something failed at Monime                                                        | Retry with backoff, with the same `Idempotency-Key`                                                                                |

## Retry or fix?

- **Retry** network errors, `429` and `5xx` responses. Wait between attempts, longer each time, and add a little randomness so many clients don't retry at once. On `429`, wait at least `Retry-After` seconds.
- **Send every retry of a create with the same `Idempotency-Key`,** method, URL and body, so a retry after a timeout can never create a second payout or payment.
- **Don't retry other `4xx` errors** without changing the request: fix what the error names first.
- **If you can't tell whether a create succeeded,** such as after a timeout, `GET` the object before trying again. A `404` means it was never created.

## Handle errors in code

```ts title="monime.ts"
type MonimeError = { code: number; reason: string; message: string; details: unknown[] };

export async function callMonime(path: string, init: RequestInit, attempts = 5): Promise<unknown> {
  for (let attempt = 1; ; attempt++) {
    let response: Response;
    try {
      response = await fetch(`https://api.monime.io${path}`, init);
    } catch (networkError) {
      if (attempt === attempts) throw networkError;
      await wait(backoff(attempt));
      continue;
    }

    const payload = await response.json();
    if (payload.success) return payload.result;

    const error: MonimeError = payload.error;
    const requestId = response.headers.get('Monime-Request-Id');
    const retryable = response.status === 429 || response.status >= 500;
    if (!retryable || attempt === attempts) {
      console.error(`Monime ${error.reason} (request ${requestId}): ${error.message}`);
      throw Object.assign(new Error(error.message), { reason: error.reason, requestId });
    }

    // On 429, Retry-After says how long to wait; otherwise back off.
    const retryAfter = Number(response.headers.get('Retry-After'));
    await wait(retryAfter > 0 ? retryAfter * 1000 : backoff(attempt));
  }
}

const backoff = (attempt: number) => 2 ** attempt * 500 + Math.random() * 250;
const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
```

Pass the same `init`, including its `Idempotency-Key` header, to every attempt, so each retry is identical.

## Errors in webhooks

Webhook deliveries are retried by Monime when your endpoint doesn't answer with a `2xx`. See [Delivery and retries](/docs/developer-resources/guides/webhook/delivery-and-retries) for how to answer them, and [Troubleshoot](/docs/developer-resources/guides/webhook/troubleshoot) for common failures.
