Errores y códigos de respuesta
Qué forma tiene una respuesta de la API, qué significa cada código HTTP y qué hacer con cada uno
Cómo interpretar lo que devuelve la API: la forma de una respuesta exitosa, la tabla de códigos, y las trampas que conviene conocer antes de escribir el manejo de errores.
El envelope de respuesta
Ningún endpoint devuelve el recurso "pelado". Todo viene envuelto en la misma estructura, y el recurso vive adentro de data.
Respuesta simple
| Campo | Tipo | Qué es |
|---|---|---|
responseId | string (uuid) | Identificador único de esta respuesta |
createdAt | string (date-time) | Momento en que se generó |
code | integer | El status HTTP repetido dentro del body |
data | object | El recurso solicitado |
{
"responseId": "0d4d1c1e-3f5a-4f0e-9b2a-2f5a7c9e1b44",
"createdAt": "2026-09-07T16:37:46+00:00",
"code": 200,
"data": {
"id": 1234,
"companyId": 77
}
}Respuesta de listado
Los endpoints que devuelven colecciones agregan un bloque meta y data pasa a ser un array.
| Campo | Tipo | Qué es |
|---|---|---|
responseId | string (uuid) | Identificador único de esta respuesta |
createdAt | string (date-time) | Momento en que se generó |
code | integer | El status HTTP repetido dentro del body |
meta.totalItems | integer | Total de registros que matchean la consulta, no los de esta página |
meta.itemsPerPage | integer | Tamaño de página efectivo |
meta.currentPage | integer | Página devuelta |
data | array | Los recursos de esta página |
{
"responseId": "0d4d1c1e-3f5a-4f0e-9b2a-2f5a7c9e1b44",
"createdAt": "2026-09-07T16:37:46+00:00",
"code": 200,
"meta": {
"totalItems": 250,
"itemsPerPage": 30,
"currentPage": 1
},
"data": []
}Cómo recorrer un listado completo está en Límites y paginación.
El code del body no es fuente de verdad. Duplica el status HTTP, pero hay errores que reusan este mismo envelope. Basá tu lógica de control de flujo en el status HTTP real de la respuesta, no en el code del JSON.
Headers de toda respuesta
| Header | Para qué sirve |
|---|---|
X-Request-ID | Identificador de la request del lado del servidor. Presente en 200, 401, 404 y 429 |
Content-Type | application/json en las respuestas de la aplicación. Algunos errores devuelven text/html |
Logueá el X-Request-ID de cada respuesta, incluidas las que salieron bien. Cuando escribas a soporte@dardo.ai, ese identificador es lo que nos permite encontrar tu request exacta en vez de pedirte que reproduzcas el problema. Un ticket con X-Request-ID se resuelve mucho más rápido que uno sin él.
Tabla de códigos
| Código | Significado | Causa típica | Qué hacer |
|---|---|---|---|
200 | OK | Lectura o acción ejecutada | Leer data |
201 | Created | Recurso creado | Leer data y guardar el id devuelto |
400 / 422 | Payload inválido | Body mal formado, campo requerido faltante o fuera de rango, lista de eventos inválida en webhooks | Corregir el payload. No reintentar sin cambiarlo |
401 | No autenticado | Header X-API-Key ausente, mal escrito, o clave inválida | Revisar el header y el valor de la clave. No reintentar |
403 | Sin permiso | El plan de la cuenta no habilita la API, o el recurso está fuera del alcance de la clave | Verificar el plan y el nivel de la clave. No reintentar |
404 | No existe | ID inexistente, recurso borrado, o path mal escrito | Verificar el ID y la URL. No reintentar |
429 | Demasiados pedidos | Se superó el límite de tasa | Reintentar con backoff exponencial, leyendo antes la advertencia de idempotencia |
5xx | Error del servidor | Falla temporal de nuestro lado | Reintentar con backoff. Si persiste, ticket con el X-Request-ID |
Si recién arrancás y todo te da 403, lo primero a revisar no es tu código: es el plan de la cuenta. La API está disponible únicamente en el plan Business. Una cuenta en Start o Grow tiene el dashboard funcionando perfecto y aun así no accede a la API. Es el error más probable de un integrador nuevo, y se pierden horas buscándolo en el lugar equivocado.
El segundo sospechoso es el nivel de la clave: una clave de sub-cuenta pegándole a endpoints de administración de cuentas es un rechazo esperable. Ver alcance de la clave.
Errores de autenticación: la forma real
Comportamiento verificado contra https://api.dardo.ai/api/v2/profile.
Sin header X-API-Key:
{ "message": "Full authentication is required to access this resource." }Con una clave inválida o revocada:
{ "message": "API key is invalid" }Los dos llegan con status 401, Content-Type: application/json y su X-Request-ID.
La diferencia entre los dos mensajes es útil para diagnosticar: si recibís "Full authentication is required", la API no encontró ninguna credencial —el header no llegó, o llegó con otro nombre—. Si recibís "API key is invalid", el header llegó bien y lo que falla es el valor de la clave.
X-API-Key es el único header de autenticación que la API lee para estos endpoints. Mandar la clave como Authorization: Bearer <clave> devuelve "Full authentication is required", porque ese header directamente se ignora. El nombre no distingue mayúsculas de minúsculas: x-api-key funciona igual.
Punto de venta: 403, no 401
Las tres operaciones de Punto de venta usan X-App-Token en lugar de X-API-Key, y responden con 403:
{ "errors": ["Missed header X-App-Token"] } // el header no llegó
{ "errors": ["Authentication failed"] } // el header llegó, el token no sirveEn este grupo, el cuerpo se valida antes que la credencial. Un pedido con el Check mal armado devuelve 400 por el cuerpo aunque el X-App-Token también esté mal — el 403 aparece recién cuando el cuerpo es válido.
Al depurar, arreglá primero lo que dice el 400: hasta que el cuerpo no pase, no vas a ver si tu token funciona.
Conviven varios formatos de error
Esto es lo más importante de esta página a la hora de escribir el cliente HTTP.
Hoy no hay una única forma canónica de error en la API. Conviven al menos tres:
| Forma | Dónde aparece | Ejemplo |
|---|---|---|
{"message": "..."} | Errores de autenticación | {"message":"API key is invalid"} |
{"errors": [...]} | Las tres operaciones de Punto de venta | {"errors":["Authentication failed"]} |
El envelope Response | Los 400 de Webhooks | {"responseId":"...","code":400,"data":{}} |
| HTML | 404 y 429 (ver abajo) | — |
No podés parsear los errores de forma uniforme todavía. Escribí el manejo de errores defensivo: ramificá por status HTTP, chequeá el Content-Type antes de parsear —o envolvé el JSON.parse en un try/catch— y tratá el mensaje del body como texto para loguear, no como un contrato estable.
Es información accionable, no una excusa: si asumís JSON con una forma fija, tu integración se va a caer el día que toque un endpoint del otro grupo.
Errores que no vienen de la aplicación
Dos casos donde la respuesta no es JSON:
404 en un path inexistente devuelve una página HTML de error, con Content-Type: text/html.
429 por límite de tasa lo emite nginx antes de llegar a la aplicación, también en HTML:
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx</center>
</body>
</html>Ese 429 no trae Retry-After ni headers X-RateLimit-*. No hay forma de saber cuánto esperar: el backoff es a ciegas. Detalles en Límites y paginación.
Qué declara la especificación
Un dato honesto para calibrar expectativas: la enorme mayoría de las operaciones de la API documenta únicamente respuestas 2xx. Solo tres grupos declaran respuestas de error en la especificación:
| Grupo | Errores declarados |
|---|---|
| Punto de venta | 400 y 403, con la forma {"errors": [...]} |
| Webhooks | 400, reusando el envelope Response |
| Servicios externos | Una respuesta default sin contenido declarado |
Los grupos que va a usar una integración típica —Tarjetas, Clientes, Operaciones— no declaran ninguna respuesta de error en la referencia. Eso no significa que no las devuelvan: significa que la referencia no las lista, y por eso esta página existe.
La tabla de códigos de arriba es la guía práctica. No esperes encontrar una tabla de errores endpoint por endpoint en la referencia, porque hoy no está.
Estrategia de reintentos
| Código | ¿Reintentar? | Cómo |
|---|---|---|
400 / 422 | No | El payload está mal, reintentar da lo mismo |
401 | No | Credencial inválida, arreglar la configuración |
403 | No | Plan o permiso insuficiente, escalar |
404 | No | El recurso no existe |
429 | Sí | Backoff exponencial con jitter |
5xx | Sí | Backoff exponencial, 3 a 5 intentos como máximo |
| Timeout de red | Con cuidado | Ver la advertencia de idempotencia antes de reintentar una escritura |