Ir al contenido

Errores

Todos los errores del API tienen la misma estructura, incluidos los de validación y los que no vienen de nuestro código:

{
"status": 422,
"code": "VALIDATION",
"message": "texto legible en español",
"errors": [
{ "rule": "FAJ43b", "field": "numbering_range_id", "message": "..." }
]
}
Campo Descripción
status El mismo código HTTP de la respuesta.
code Identificador estable. Programa contra esto, no contra el texto.
message Explicación en español, pensada para leerse. Puede cambiar.
errors Detalle por campo. Vacío cuando no aplica.
code HTTP Qué pasó Qué hacer
UNAUTHORIZED 401 Falta la cabecera, o el token es inválido o venció Pide otro token
FORBIDDEN 403 La credencial es válida pero no abre esa puerta Revisa qué credencial estás usando
code HTTP Qué pasó Qué hacer
VALIDATION 400 / 422 Un campo falta, sobra o no cumple Mira errors[]: dice el campo
BAD_REQUEST 400 La petición no encaja con el estado actual Léelo: el mensaje dice qué falta
NOT_FOUND 404 No existe, o no es de tu emisor Comprueba el id
code HTTP Qué pasó
CERT_MISSING — El emisor no tiene certificado digital cargado
CERT_EXPIRED — El certificado venció
CERT_NIT_MISMATCH — El certificado es de otro NIT
SOFTWARE_NOT_CONFIGURED — Falta registrar el software (id y PIN) ante la DIAN

Los cuatro se arreglan en la configuración del emisor, no en tu código.

code HTTP Qué pasó Qué hacer
RANGE_EXHAUSTED 422 Se acabó el rango autorizado Pedirle otro a la DIAN
RANGE_EXPIRED 422 La resolución venció Pedir una nueva
RANGE_INACTIVE 422 La resolución se retiró de circulación a mano Usar otra, o reactivarla con PATCH /v1/numbering-ranges/{id}/actividad

Los rangos de notas crédito y débito se salen de los dos primeros consejos: no llevan vigencia, así que RANGE_EXPIRED no les puede pasar, y cuando se agotan no hay nada que pedirle a la DIAN — se registra el tramo siguiente con el mismo prefijo. Ver rangos de numeración.

Emitir es asíncrono: la DIAN no contesta dentro de la petición, así que su respuesta nunca llega como error HTTP. Un «no» de la DIAN y una caída de la DIAN son estados del documento, no un code:

Situación Estado Dónde está el motivo
La DIAN rechazó el documento REJECTED dian_errors[] — son los suyos, literales — y dian_status_code
La DIAN no respondió tras cinco intentos CONTINGENCY dian_errors[]

Ambos llegan por webhook (document.rejected, document.contingency) o consultando el documento. Ver Estados de un documento.

code HTTP Qué pasó
IDEMPOTENCY_CONFLICT 409 Ese reference_code ya existe para otro tipo de documento

Repetir un reference_code del mismo tipo no es un error: devuelve el documento que ya existe.

code HTTP Qué pasó
PLAN_REQUIRED — El emisor no tiene plan activo
PLAN_EXHAUSTED — Se agotó el cupo de documentos
PLAN_EXPIRED — El plan venció
code HTTP Qué pasó
INTERNAL 500, 501, 429, 404… Algo se rompió de nuestro lado, o el error no tiene un code propio

INTERNAL es el único code que no identifica una sola situación: además de los fallos nuestros, es el valor de relleno que reciben todos los errores que no pasan por nuestra lista de códigos, conservando cada uno su HTTP real.

El fallo interno de verdad, el 500, se ve así:

{ "status": 500, "code": "INTERNAL", "message": "Error interno del servidor", "errors": [] }

Si un 500 se repite, escríbenos con el reference_code y la hora.

300 por minuto y por IP por defecto. Al superarlo, 429 — con code: "INTERNAL", no un código propio, y con el message en inglés:

{
"status": 429,
"code": "INTERNAL",
"message": "ThrottlerException: Too Many Requests",
"errors": [{ "message": "ThrottlerException: Too Many Requests" }]
}

Reconócelo por status, no por code.

Las rutas de autenticación de personas tienen cupos propios, bastante más estrictos:

Ruta Límite
POST /v1/auth/login 10 por minuto
POST /v1/auth/refresh 30 por minuto
POST /v1/auth/password/reset 10 por minuto
POST /v1/auth/password/forgot 3 por minuto

Siguen siendo por minuto y por IP. El resto del API —incluidas POST /v1/auth/logout y /logout-all— va con el límite por defecto.

Reintentables — la operación puede salir bien después: INTERNAL cuando status es 500 o 429. Reintenta con espera creciente y el mismo reference_code: la idempotencia impide que se duplique.

No reintentables — hay que arreglar algo primero: VALIDATION, BAD_REQUEST, NOT_FOUND, FORBIDDEN, los cuatro de certificado y software, los de plan, y el INTERNAL que llega con status 501 o 404. Reintentar sin cambiar nada da el mismo error.

Caso aparte: RANGE_EXHAUSTED y RANGE_EXPIRED no los arregla tu código ni el nuestro — hay que pedirle numeración a la DIAN, salvo en las notas, que numeran sin resolución. Vale la pena alertar antes de llegar al final del rango.