Errores
Todos los errores comparten un único envelope:
{
"error": {
"code": "validation_failed",
"message": "Field 'email' is required",
"details": {
"field": "email"
}
}
}
codees un identificador machine-readable. Estable entre releases.messagees texto legible en inglés. No se proveen strings localizadas — tradúcelas en el consumidor.detailses opcional y su forma depende del tipo de error. Para errores de validación incluye el campo ofensor; para errores de rate-limit incluye unretry_afternumérico.
Códigos estándar
| HTTP | code | Cuándo |
|---|---|---|
| 400 | bad_request | JSON malformado, Content-Type no soportado, etc. |
| 400 | validation_failed | El body o query falló la validación de schema. |
| 401 | mtls_required | Certificado de cliente ausente/inválido. |
| 401 | invalid_token | JWT malformado o firma inválida. |
| 401 | invalid_audience | aud no corresponde al entorno. |
| 401 | token_expired | Claim exp en el pasado. |
| 401 | token_revoked | jti listado en RevokedTokens. |
| 403 | insufficient_scope | El token carece del scope requerido. |
| 403 | insufficient_permission | La persona detrás del token no tiene el permiso que el endpoint exige; message es el slug del permiso. |
| 403 | forbidden | Origin shared-secret ausente o incorrecto, o el token no resuelve a ningún tenant — message es unknown_organization, no_membership o unknown_workspace. Consulta Autenticación. |
| 400 | workspace_required | La organización tiene varios workspaces y no se envió X-Workspace-Id. |
| 404 | not_found | El recurso no existe (o pertenece a otro workspace). |
| 409 | conflict | Conflicto de estado (p. ej. clave de idempotencia reutilizada con un body diferente, violación de unique constraint). |
| 422 | unprocessable_entity | Violación de regla de negocio (p. ej. no se puede desactivar un webhook con retries activos). |
| 429 | rate_limited | Límite por cuenta excedido. Incluye header Retry-After. |
| 500 | internal_error | Error no manejado. ID de correlación devuelto en details.request_id. |
| 503 | service_unavailable | Dependencia upstream (S3, DB, AI Gateway) con fallo. |
Leyendo details
details es intencionalmente laxo. Patrones comunes:
- Errores de validación —
{ "field": "subscribed_events.0", "issue": "must be a known SystemEvent slug" }. - Rate limiting —
{ "retry_after": 17, "limit": 600, "remaining": 0 }. - Conflicto de idempotencia —
{ "key": "...", "original_status": 201, "original_response_id": "..." }. - Not found entre workspaces — nunca filtra la existencia del recurso; la respuesta es indistinguible de un id realmente inexistente.
Correlación
Cada respuesta — exitosa o no — lleva un header X-Request-Id. Inclúyelo al reportar problemas para que podamos obtener el trace desde OpenTelemetry.
Guía de reintentos
| Clase de error | ¿Reintentar? |
|---|---|
400, 401, 403, 404, 409, 422 | No — corrige la solicitud. |
429 | Sí, después de Retry-After segundos. |
500, 503 | Sí, con backoff exponencial (comienza en 1s, tope de 30s, máx. 5 intentos). |
Para operaciones idempotentes (GET, PATCH, DELETE) reintentar siempre es seguro. Para operaciones POST incluye siempre un X-Idempotency-Key para que los reintentos no dupliquen la creación.