Errores
Cada fallo vuelve en un solo envoltorio, sea cual sea la ruta que lo rechazó:
{
"error": {
"type": "https://reelwire.io/errors/forbidden",
"code": "forbidden",
"message": "This key may not read here. Give it the \"Team: read\" permission, or use a key that has it.",
"requestId": "01M2SR17R25EKN6DM1H9DVDS0D",
"details": []
}
}
Los campos y para qué sirve cada uno:
| Campo | Qué es |
|---|---|
code | El motivo legible por máquina. Ramifique según este, nunca según el mensaje. |
type | Un URI de documentación estable, https://reelwire.io/errors/{code}. No lleva más información que el código. |
message | Escrito para que lo lea una persona y se pueda mostrar sin riesgo en su propia interfaz. Nunca lleva una traza de pila ni un secreto. |
requestId | Un ULID que correlaciona este fallo con la línea del log de cada contenedor que tocó la solicitud. El mismo valor está en el encabezado de respuesta x-request-id de cada respuesta, correcta o no. Si envía un ULID propio en ese encabezado, se usa en su lugar; cualquier cosa que no sea un ULID se sustituye por uno nuevo. |
details | Una entrada por problema, cada una con un JSON Pointer al cuerpo de la solicitud. |
retryAfterSeconds | Presente cuando lo que lo arregla es esperar: en un límite de frecuencia, con el mismo número en un encabezado Retry-After. |
Cite el requestId cuando pregunte por un fallo. Es el único valor que permite encontrar de nuevo la solicitud.
Las páginas que una persona abre en un navegador en lugar de llamarlas son la excepción, porque las lee una persona: el kit de publicación, la página de un grupo de sindicación y las páginas que abren sus correos responden con una página propia, como 410 Gone para un enlace cuyo tiempo ha pasado.
Los detalles llevan un puntero por problema
details es lo que hace que un rechazo sea accionable: un cliente puede señalar el campo exacto en lugar de imprimir un párrafo y dejar que alguien lo busque.
curl http://localhost:4000/v1/stream \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"streamId":"st_30472168d941","eventId":"not-a-ulid","occurredAt":"yesterday","data":{}}'
{
"error": {
"type": "https://reelwire.io/errors/schema_validation",
"code": "schema_validation",
"message": "Stream envelope is not valid",
"requestId": "01M2SQNH1MDKPWQ83Y3ECG60M3",
"details": [
{ "path": "/eventId", "message": "must be a Crockford base32 ULID", "rule": "invalid_string" },
{ "path": "/occurredAt", "message": "Invalid datetime", "rule": "invalid_string" }
]
}
}
path es un JSON Pointer según RFC 6901 al cuerpo que envió. Una cadena vacía significa el documento en su conjunto. rule es una pista opcional legible por máquina, y expected y received aparecen cuando el verificador conocía ambos.
Los códigos y el estado que lleva cada uno
Una ruta nunca elige su propio estado: lo decide el código, en una sola tabla. La tabla es común a todas las fronteras de Reelwire, así que algunos códigos, como replayed y timestamp_skew, pertenecen a entregas firmadas y no a nada que usted llame.
| Código | Estado | Cuándo |
|---|---|---|
malformed_json | 400 | El cuerpo no es JSON. |
schema_validation | 400 | JSON bien formado que no encaja en el formato. |
ratio_not_supported | 400 | La plantilla no viene en ese formato. |
locale_not_supported | 400 | No se renderiza nada en ese idioma. |
blocked_url | 400 | Una URL que Reelwire no obtiene ni a la que entrega. |
unauthenticated | 401 | Credenciales ausentes o no válidas, o un token de asistente de IA enviado directamente a la API en lugar de a través del servidor MCP de Reelwire. |
signature_invalid | 401 | Una firma que no es correcta. |
timestamp_skew | 401 | Una solicitud firmada demasiado alejada del momento actual. |
forbidden | 403 | Una credencial válida que no puede hacer esto, o una contraseña incorrecta donde una ruta la pide de nuevo, como al mostrar una clave. |
not_found | 404 | No existe, o no es suyo. |
unknown_source | 404 | Ningún feed incluido con ese slug de fuente. |
unknown_format | 404 | Ningún formato así en esa fuente. |
unknown_template | 404 | Ninguna plantilla así. |
unknown_stream | 404 | Ningún stream con ese id en esta cuenta. |
conflict | 409 | Un conflicto con lo que ya hay. |
idempotency_conflict | 409 | El mismo eventId con un cuerpo distinto. |
invalid_state_transition | 409 | La cosa no está en un estado al que se aplique esta acción. |
stream_disabled | 409 | El stream existe y está desactivado. |
no_sample | 409 | Se preguntó por los nombres de campo propios de un stream antes de que hubiera recibido nada. |
template_not_approved | 409 | La plantilla no está autorizada para este espacio de trabajo. |
replayed | 409 | Un id de entrega que ya se ha procesado. |
payload_too_large | 413 | Por encima del límite de tamaño de esta ruta. |
unsupported_media_type | 415 | El Content-Type equivocado, como JSON enviado a una ruta que admite un archivo. |
rate_limited | 429 | Demasiadas solicitudes. retryAfterSeconds indica cuánto esperar. También una cuota agotada: no quedan videos este mes, o no queda espacio en la parte de la biblioteca multimedia del almacenamiento. Esa no lleva retryAfterSeconds, porque esperar no lo arregla. |
internal | 500 | Un fallo por parte de Reelwire. |
render_failed | 502 | El renderizador rechazó el trabajo. |
asset_unavailable | 502 | No se pudo obtener un archivo que necesitaba el render, o se pidió un video antes de que terminara. |
checksum_mismatch | 502 | Llegó un archivo pero no es el que se prometió. |
upstream_unavailable | 503 | Una dependencia está caída, como cuando ningún renderizador es accesible. |
render_timeout | 504 | El renderizador tardó demasiado. |
Dos resultados que suenan a fallos a propósito no lo son. Un evento más antiguo de lo que acepta su feed se responde con 202 y status: "stale" en una respuesta ordinaria y no en un envoltorio de error: estaba bien formado y se guardó, y no se renderizó nada. Un productor que reproduce historial ve un éxito en lugar de un muro de 4xx que ocultaría entre ellos un error real. El código stale_event lleva 202 en la tabla por la misma razón. Consulte Enviar eventos.
El otro es un cambio en lo que paga un espacio de trabajo cuando el banco de la tarjeta quiere que el titular confirme el pago: 202 con { "confirmation": { "url": "...", "label": "...", "expiresAt": "..." } }, la página donde lo confirma. Se aceptó y aún no está hecho. Esas rutas son para una persona con sesión iniciada, así que solo se encuentra con él un cliente que actúa por una.
404 y 403 no son intercambiables
Algo que pertenece a otro espacio de trabajo responde exactamente igual que algo que no existe. Es a propósito: decirle a un extraño que st_000000000000 existe pero no es suyo es decirle algo. Un 403 significa que la credencial es suya y no tiene permitida la acción, y el mensaje nombra el permiso que hay que añadir. Consulte Permisos.
Reintentar
Merece la pena reintentar 429 y 503, tras retryAfterSeconds donde se indique y con un retroceso en otro caso. Los propios límites se listan en Límites de frecuencia. Un 429 sin retryAfterSeconds es la excepción: es una cuota agotada y responde igual hasta que se reinicie el mes, se compre más o se eliminen archivos para dejar espacio; su mensaje indica cuál. 502 y 504 en un render merecen un reintento. Todo lo demás en el rango 4xx no: la solicitud se rechazará igual hasta que cambie.
Los envíos llevan un eventId que hace idempotente un reintento, de modo que reintentar tras un tiempo de espera agotado no puede publicar dos veces. Consulte Enviar eventos.