Errores y códigos de respuesta
Cada respuesta no-2xx devuelve un envelope estructurado. Misma forma para 4xx y 5xx — leélo una vez y manejá todos los errores de la API con un solo branch.
Envelope de error
Toda respuesta de error — 4xx o 5xx — viene con esta forma. Sin variantes, sin sorpresas. Si necesitás soporte, copiá el request_id en el ticket y vamos a poder trazar el request exacto.
{
"error": {
"code": "invalid_input",
"message": "value must be a string",
"request_id": "req_a1b2c3d4e5f6g7"
}
}Campos del envelope
code— string · estable y machine-readable. Usalo para branchear en tu cliente.message— string · descripción human-readable en inglés por diseño. Si necesitás mensajes en español o portugués para usuarios finales, traducí en tu UI mapeando por code.request_id— string · matchea el header X-Request-Id de la respuesta. Lo loggeamos del lado nuestro por 30 días — incluilo en cualquier reporte a soporte.
Códigos de status HTTP
valid: false no es un error HTTP — devolvemos 200 con valid: false y razón legible. Los códigos abajo aplican a fallas de transporte, autenticación, schema o disponibilidad.
| Status | Significado | Cuándo | Qué hacer | error.code ejemplo |
|---|---|---|---|---|
| 200 | OK | Request válido y procesado. | Inspeccioná value.valid para el resultado de validación. | — |
| 400 | Bad Request | Falta un campo requerido, tipo incorrecto, JSON mal formado. | Revisá el error.code y el shape del body que estás mandando. | invalid_input · missing_required_field |
| 401 | Unauthorized | Header X-API-Key faltante o inválido. | Verificá el formato nd_… y que la key esté activa. | unauthorized · invalid_api_key |
| 403 | Forbidden | Endpoint no autorizado para tu scope. | Contactá a soporte si deberías tener acceso a este endpoint. | forbidden |
| 404 | Not Found | URL inexistente. | Revisá los paths de los endpoints (/v1/verify/…). | not_found |
| 422 | Unprocessable Entity | Input con shape incorrecto para el endpoint (ej. mandar un email a /verify/tax-id). | Usá Smart Parse o el endpoint correcto para el tipo de dato. | wrong_endpoint_for_input |
| 429 | Too Many Requests | Excedido el quota de tu plan. | Honrá el header Retry-After. Ver /docs/rate-limits. | rate_limited |
| 500 | Internal Server Error | Bug del lado nuestro. | Reintentá con backoff exponencial; reportá con el request_id. | internal_error |
| 502/503/504 | Bad Gateway · Unavailable · Timeout | Problema temporal de infraestructura o upstream. | Reintentá con exponential backoff + jitter. | bad_gateway · unavailable · timeout |
Códigos de warning
Cuando un endpoint procesa parcialmente o detecta algo sospechoso, devuelve warnings en el array warnings junto a un 200 OK. No son errores — son señales para tu UI o pipeline.
| Code | Cuándo aparece | Qué significa |
|---|---|---|
| UNKNOWN_TAX_ID | El formato del tax ID no matchea ningún país conocido. | Devuelto con valid: false cuando Smart Parse no puede detectar el país. Pedile al usuario que confirme el país y reenvíe. |
| LOW_CONFIDENCE | Smart Parse hizo un best-guess de ruteo con confidence < 0.5. | Pasá un country_hint para desambiguar y subir el confidence. |
| AMBIGUOUS_MATCH | Múltiples tipos matchearon el input con confidence similar. | Tomá el primer candidato (el más probable) o revisá el array warnings para ver todos los candidatos. |
| INVALID_CHECKSUM | Formato ok pero el dígito verificador no calza. | Probablemente un typo del usuario. Surface inline error en tu UI y pedile que revise. |
| COMPONENTS_IGNORED | Mandaste full_name y first_name juntos — full_name ganó. | Inspeccioná el diff entre source y normalized para ver qué se descartó. |
| DISPOSABLE_DOMAIN | El email viene de un proveedor desechable conocido. | Surfacealo como soft warning — no necesariamente bloquear, depende de tu caso de uso. |
| RESERVED_RANGE | El número de teléfono está en un rango reservado (ej. 555 en US). | Rechazá como data de prueba — esos números no son alcanzables en red real. |
X-Request-Id
Cada respuesta — éxito o error — incluye el header X-Request-Id con un ID único formato req_… que también aparece dentro del envelope de error.
Loggeamos request_id del lado nuestro por 30 días. Cuando abras un ticket en hello@normadata.io o reportes un bug, copiá el request_id — sin él no podemos trazar la request exacta en nuestros logs.
Estrategia de retries
Los códigos 5xx y 429 son seguros para reintentar — son fallas transitorias o de capacidad. Implementá exponential backoff con jitter para evitar thundering herd en recuperaciones.
Los códigos 4xx (excepto 429) NO se deben reintentar. Son fallas de request — el request va a fallar igual si lo mandás de nuevo sin cambios. Fijate primero el error.code y corregí el request antes de reintentar.
Retry seguro: 5xx, 429. NO retry: 400, 401, 403, 404, 422. Para 429, honrá Retry-After.