Errores
Errores
Cuando algo sale mal, la API responde con un código HTTP de error y siempre con el mismo cuerpo JSON.
{
"code": "not_found",
"message": "Subscription not found: 550e8400-e29b-41d4-a716-446655440000",
"details": null
}| Campo | Tipo | Para qué sirve |
|---|---|---|
code | string | Es el campo sobre el que debes ramificar. Identificador estable del tipo de error. |
message | string | Texto en inglés pensado para que un humano depure. Puede cambiar de redacción sin aviso — no lo compares contra una cadena fija. |
details | object | null | Información adicional cuando existe. Hoy es null en la práctica totalidad de los errores. |
Ramifica por code, nunca por message ni por el código HTTP. El code es parte del
contrato; el mensaje es prosa de diagnóstico que reescribimos cuando mejora la explicación, y un
mismo código HTTP puede llegar con distintos code.
Códigos de error
| HTTP | code | Qué pasó | Qué hacer |
|---|---|---|---|
| 400 | invalid_argument | El cuerpo o un parámetro no cumple el contrato: falta un campo obligatorio, un valor está fuera de rango, o mandaste una propiedad que el endpoint no acepta. | Corrige la solicitud. Reintentarla igual vuelve a fallar. |
| 401 | unauthenticated | Falta el header Api-Key, la clave no existe, está revocada, o no corresponde a la organización del header Organization. | Revisa ambos headers. Consulta Autenticación. |
| 403 | permission_denied | La clave es válida pero no tiene permiso sobre ese recurso. | No se resuelve reintentando. Las claves API no ejecutan acciones reservadas a administradores. |
| 404 | not_found | No existe un recurso con ese ID dentro de tu organización. | Verifica el ID. Ver la nota de abajo. |
| 409 | already_exists | Ya existe un recurso con ese identificador único (por ejemplo, un plan con el mismo nombre). | Usa otro valor, o actualiza el recurso existente. |
| 4xx | failed_precondition | La solicitud es válida, pero el recurso no está en el estado que la operación necesita (por ejemplo, reactivar una suscripción que ya dejó de estar cancelada). | Vuelve a consultar el recurso y decide con su estado actual. |
| 4xx | aborted | El recurso cambió mientras se procesaba tu solicitud, y se descartó para no pisar ese cambio. Ocurre si dos peticiones tocan la misma suscripción a la vez. | Vuelve a leer el recurso y reintenta con datos frescos. |
| 500 | internal | Error inesperado de nuestro lado. | Se puede reintentar. Si persiste, es un incidente nuestro. |
Las dos filas marcadas 4xx no llevan número por honestidad: son códigos que la API sí
devuelve, pero el número HTTP exacto con el que viajan no está confirmado de nuestro lado, y
preferimos no publicar una cifra que nadie verificó. Es justo el motivo de la advertencia de
arriba: el campo code es la parte estable del contrato, el número no.
Un ID con formato inválido también responde 404, no 400. Y un ID que existe pero pertenece a otra organización responde 404 igual que uno que no existe. Es deliberado: la API nunca revela si un ID ajeno existe.
Límite de peticiones
Los endpoints /v1 no tienen límite de peticiones: no vas a recibir un 429. No hace falta
que implementes espera con reintentos por ese motivo.
Ejemplo de manejo
const response = await fetch('https://api.venflow.app/v1/subscriptions/' + id, {
headers: {
'Api-Key': process.env.VENFLOW_API_KEY,
'Organization': process.env.VENFLOW_ORG_ID,
},
});
if (!response.ok) {
const error = await response.json();
switch (error.code) {
case 'not_found':
// El ID no existe en tu organización. No reintentar.
return null;
case 'unauthenticated':
case 'permission_denied':
// Problema de credenciales. No reintentar.
throw new Error(`Credenciales de Venflow: ${error.message}`);
case 'internal':
// Nuestro lado falló. Este sí vale la pena reintentar.
throw new RetryableError(error.message);
default:
throw new Error(`Venflow ${error.code}: ${error.message}`);
}
}Qué errores puede dar cada endpoint
Cada página de la Referencia API lista los códigos concretos de ese endpoint, con la
razón específica por la que los devuelve. Los cuatro de arriba (unauthenticated,
permission_denied, internal y, cuando el endpoint recibe un ID, not_found) los puede dar
cualquier endpoint.