# Errors

Every error has the same shape, and the code is the stable part.

Every error, from every endpoint, has the same shape.

```json
{ "error": { "code": "not_found", "message": "Post nao encontrado" } }
```

`code` is the stable contract: branch your code on it. `message` is human text meant to be shown or logged, it is written in Portuguese, and it can change without notice. Never match on it.

| `code` | HTTP | What happened |
| --- | --- | --- |
| `unauthenticated` | `401` | No key, or a key that is malformed, unknown or revoked |
| `forbidden` | `403` | The plan does not include the API, or the role cannot do this |
| `not_found` | `404` | The resource does not exist, or is not in this workspace |
| `invalid_input` | `400` | A field is missing, malformed or out of range |
| `invalid_state` | `409` | The resource exists but is in a state where this cannot happen |
| `rate_limited` | `429` | Too many requests, see Rate limits |
| `upstream_failure` | `500` | Something on our side failed |

> A resource in another workspace answers `404`, never `403`. Telling the difference would confirm that the id exists somewhere, which is itself a leak.

## Method not allowed

A path that exists but not with that verb answers `405` with an `Allow` header listing what it does accept. The body is the same error shape, so a client never has to special-case it.
