# Errores

La forma de un error de la API y el conjunto cerrado de códigos que puede traer.

## Forma del error

El cuerpo es plano — nada anida bajo una clave `error`:

```json
{
  "code": "validation_failed",
  "message": "The request does not match the contract",
  "details": {
    "issues": [
      {
        "path": ["content", "subject"],
        "code": "too_small"
      }
    ]
  },
  "requestId": "01a0ba31-3fe4-7cc4-a1c6-64bdaaa5d99c"
}
```

### Atributos de la respuesta

- `code` `string` — Obligatorio. Del conjunto cerrado de la tabla de abajo.
- `message` `string` — Obligatorio. Legible, pero no pensada para el usuario final — no la traducimos.
- `details` `mapa libre | null` — Obligatorio. null salvo que el código tenga algo estructurado que agregar, como la lista de campos que no pasaron la validación.
- `requestId` `string` — Obligatorio. El mismo id que llega en el encabezado x-request-id de toda respuesta — de error o no —, así que puedes capturarlo aunque tu cliente no llegue a leer el cuerpo.

## Códigos que puede traer esta superficie

De los códigos que trae el sistema, estos son los que un caller de `/v1` puede recibir de
verdad:

| Código | Estado HTTP | Cuándo aparece |
| --- | --- | --- |
| `bad_request` | 400 | Una regla que el esquema no expresa por sí solo — `POST /v1/email` sin ninguna dirección entre `to`, `cc` y `bcc`. |
| `unauthorized` | 401 | Falta el bearer token, o no corresponde a ninguna clave activa. |
| `api_key_expired` | 401 | El bearer token nombra una clave que ya venció. La clave sigue siendo revocable y visible en el panel; solo deja de autenticar. |
| `payment_required` | 402 | La organización superó el límite de envíos de su plan. |
| `forbidden` | 403 | La clave es válida, pero no tiene el permiso que esta acción requiere. |
| `not_found` | 404 | El recurso no existe, o no existe para esta organización — la respuesta nunca revela cuál de las dos. |
| `conflict` | 409 | La `idempotencyKey` ya se usó con un cuerpo distinto, o la plantilla referenciada no está activa. |
| `validation_failed` | 422 | El cuerpo no cumple el esquema — un campo desconocido, un tipo equivocado, más de 50 direcciones en un campo. |
| `rate_limited` | 429 | Se superó el límite de solicitudes por segundo de esta clave. |
| `internal` | 500 | Algo falló de este lado. |

El conjunto completo es más largo — el resto son códigos de flujos que `/v1` no expone, como
una invitación vencida —, pero todo cliente puede tratar con seguridad un código que no
reconoce como un error genérico.
