# Errors

The API answers with an ordinary HTTP status. An error also has a body with a fixed code your system can act on and a message for a person.

## Statuses

Errors are returned as JSON with `code` and `message`. At 422 the answer also has `errors` with one field per error.

| Status | What it means |
| --- | --- |
| `400` | Invalid request. |
| `401` | The key is missing, deleted or expired. |
| `403` | The key does not have the scope, the organisation has no access to the unit, or the subscription is not active. |
| `404` | Not found. |
| `409` | The request cannot be carried out as things stand, for example because the component is already replaced. |
| `422` | Validation failed. The answer has `errors` with one field per error. |
| `429` | The request limit is reached. Wait the number of seconds given in `Retry-After`. |

## The error model

- `code` (string, required): A fixed code, such as `unauthenticated`, `missing_scope`, `forbidden`, `subscription_inactive`, `blocked`, `not_found`, `conflict`, `validation_failed` or `rate_limited`.
- `message` (string, required): An explanation for a person.
- `errors` (object): Only at 422. One field per error, each with a list of messages.

```json
{
  "code": "missing_scope",
  "message": "The key does not have the scope units:write."
}
```

```json
{
  "code": "validation_failed",
  "message": "The name field is required.",
  "errors": {
    "name": [
      "The name field is required."
    ]
  }
}
```

## Handling errors

- Act on `code`, which does not change. `message` is for a person and may be reworded.
- At 429, wait the seconds in `Retry-After` and try again.
- At 5xx or a network error, try again with a growing pause. Send `Idempotency-Key` with a POST, so a retry never makes two records.
- At 401, the key is missing, deleted or expired: make a new one in the app.
