Tema
Errores
La forma
Todos los errores del API son RFC 7807, con Content-Type: application/problem+json:
json
{
"type": "https://api.factura.sv/errores/documento-invalido",
"title": "Unprocessable Entity",
"status": 422,
"detail": "El receptor de un Comprobante de Crédito Fiscal necesita NIT y NRC.",
"errors": [
"receptor.nit: es obligatorio para el tipo 03",
"receptor.nrc: es obligatorio para el tipo 03"
]
}Programa contra type. Es un identificador estable; el último segmento de la URL es el código del error. detail está escrito para mostrárselo a una persona y su redacción puede cambiar sin aviso.
errors aparece cuando hay varios problemas a la vez. Trae todos, no solo el primero: devolver uno obligaría a corregir de uno en uno y a descubrir el siguiente en el siguiente viaje.
javascript
const codigo = problema.type.split("/").pop();
switch (codigo) {
case "falta-idempotency-key": /* bug tuyo, arréglalo */ break;
case "configuracion-incompleta": /* avisa al contribuyente */ break;
default: /* registra y muestra `detail` */
}Qué hacer según el código HTTP
| Qué significa | Qué hacer | |
|---|---|---|
| 400 | La petición está mal formada. | Es un bug de tu integración. No reintentes. |
| 401 | Credencial ausente, inválida o revocada. | Revisa la API key. No reintentes. |
| 403 | La credencial es válida pero le falta el permiso. | Pide que le añadan el scope. No reintentes. |
| 402 | El contribuyente se quedó sin DTE contratados, o su servicio está detenido. | Muéstrale detail tal cual: tiene que entrar al panel. No reintentes. |
| 404 | No existe, o no es de este contribuyente. No se distingue cuál. | No reintentes. |
| 409 | El estado actual no permite la operación. | Depende del código; casi nunca se arregla reintentando lo mismo. |
| 413 | El cuerpo es demasiado grande. | Divide el lote, o reduce la imagen. |
| 422 | Los datos no pasan la validación. | Corrige lo que diga errors. No reintentes igual. |
| 429 | Demasiadas peticiones, o cuenta bloqueada. | Espera y reintenta con espera creciente. |
| 500 | Fallo del servidor. | Reintenta con espera exponencial. |
| 503 | Falta una dependencia (catálogos sin sembrar, almacén caído). | Reintenta más tarde; probablemente hay que avisar. |
Reintentar automáticamente solo tiene sentido en 429, 500 y 503. Todo lo demás volverá a fallar igual, y en emisión gastarías el intento sin necesidad. Recuerda mantener la misma Idempotency-Key en el reintento.
Los que te van a pasar
falta-idempotency-key · 400
Emitiste sin la cabecera. → Idempotencia
saldo-dte-agotado · 402
El contribuyente agotó los DTE de su paquete. No es un fallo tuyo ni algo que se arregle reintentando: hasta que no contrate más, ninguna emisión va a pasar.
Lo que tu integración tiene que hacer es enseñarle el detail a quien esté delante de la pantalla, porque ahí dice qué hacer: entrar al panel y contratar. Un mensaje genérico de «error al facturar» deja al cajero llamando al soporte por algo que resuelve el propio contribuyente en dos minutos.
Las invalidaciones siguen pasando aunque el saldo esté agotado, hasta un pequeño margen: nadie debe quedarse sin poder anular un documento erróneo dentro de las 24 horas que da la ley.
suscripcion-bloqueada · 402
Hay una factura del servicio pendiente de pago. Contratar más DTE no lo soluciona; hay que regularizar el pago desde el panel.
suscripcion-suspendida · 402
El servicio está suspendido por decisión comercial. Solo lo reactiva el proveedor.
documento-invalido · 422
El más frecuente al empezar. errors dice exactamente qué falta. Las causas habituales:
- Receptor incompleto en un CCF: le faltan
nit,nrc,codActividado la dirección. - Un código de catálogo que no existe. Ojo con el municipio: no es único por sí solo, se repite entre departamentos.
documentosRelacionadosen un tipo que no los admite, o ausente en una nota.- Intentar ajustar con una nota un documento tipo 01 o 14: no transfieren crédito fiscal, así que no hay nada que ajustar. Solo se pueden ajustar 03, 05, 06, 07, 08, 09 y 11.
configuracion-incompleta · 409
Al contribuyente le falta el certificado de firma, las credenciales de Hacienda o los correlativos. No es un problema de tu integración: avisa a quien administre esa cuenta. Reintentar no va a servir hasta que lo carguen.
punto-venta-invalido · 422
Mandaste un codEstable o codPuntoVenta que no existe. Consulta GET /api/v1/dte/establecimientos.
documento-no-sellado · 409
Intentaste invalidar un documento que no está SELLADO. Un rechazado no existe para Hacienda: no hay nada que anular.
ya-invalidado · 409
Ese documento ya se invalidó. Es idempotente en la práctica: el resultado que querías ya está.
existencia-insuficiente · 409
Solo en /gestion/ventas. No hay inventario para lo que se intenta vender. Es un error de negocio, no un fallo: alguien tiene que decidir qué hacer.
demasiados-intentos / cuenta-bloqueada · 429
Limitador del login. Espera y reintenta con espera creciente.
Catálogo completo
Ordenado por código HTTP. El identificador es el último segmento de type.
400 — la petición está mal formada
| Código | Cuándo |
|---|---|
falta-idempotency-key | Emitir sin la cabecera obligatoria. |
json-invalido | El cuerpo no es JSON válido, o trae campos desconocidos. |
cuerpo-vacio | Se esperaba un cuerpo y no vino. |
cuerpo-invalido | El cuerpo no tiene la forma esperada. |
datos-incompletos | Faltan campos obligatorios de la petición. |
fecha-invalida | Una fecha no viene como AAAA-MM-DD. |
periodo-invalido | Faltan desde/hasta, o el rango está al revés. |
rango-invalido | hasta es anterior a desde. |
espera-invalida | El parámetro esperar no es una duración válida, o pasa de 20 s. |
401 — no autenticado
| Código | Cuándo |
|---|---|
credencial-invalida | API key ausente, mal formada, inexistente o revocada; o contraseña incorrecta. No se distingue cuál, para no filtrar qué llaves existen. |
enlace-invalido | El enlace de recuperación de contraseña es inválido o venció. |
totp-invalido | El código TOTP no coincide, o el desafío del login venció o ya se usó. |
403 — autenticado pero sin permiso
| Código | Cuándo |
|---|---|
sin-permiso | A la credencial le falta el scope. |
sesion-requerida | La operación exige una sesión de persona; una API key no sirve. |
reautenticacion-requerida | Hace falta step-up: la contraseña otra vez, aunque la sesión siga viva. Si el segundo factor está activo, también el código. |
totp-pendiente | El rol exige TOTP y todavía no está activo. Solo aplica a sesiones de persona (admin y operador), nunca a una API key. |
solo-maestro | Solo el usuario maestro del contribuyente puede hacerlo. |
codigo-requerido / codigo-invalido | Acceso de soporte con código de autorización. |
404 — no existe
| Código | Cuándo |
|---|---|
documento-no-encontrado | Ese código de generación no existe o no es de este contribuyente. |
no-encontrado / no-encontrada | Genérico para el resto de recursos. |
catalogo-no-encontrado | Ese catálogo no existe. |
clave-no-encontrada | Esa API key no existe. |
webhook-no-encontrado | Ese webhook no existe. |
lote-no-encontrado | Ese lote no existe. |
exportacion-no-encontrada | Esa exportación no existe. |
sin-logotipo | El contribuyente no tiene logotipo cargado. |
no-configurado | Falta configurar algo que la operación necesita. |
409 — el estado actual no lo permite
| Código | Cuándo |
|---|---|
configuracion-incompleta | Falta certificado, credenciales de MH o correlativos. |
emisor-no-configurado | El contribuyente no tiene datos fiscales cargados. |
documento-no-sellado | Se intentó invalidar o retornar algo que no está sellado. |
ya-invalidado | Ya se invalidó. |
retorno-vigente | Hay un Evento de Retorno en camino o sellado; hay que invalidarlo antes de invalidar la factura. |
correlativo-retrocede | Se intentó posicionar una serie hacia atrás. Serían dos documentos con el mismo número de control. |
existencia-insuficiente | No hay inventario. |
venta-no-anulable | La venta tiene un DTE con valor fiscal: hay que invalidarlo ante MH primero. |
plazo-vencido | Se pasó el plazo legal de la operación. |
duplicado, nit-duplicado, usuario-duplicado, operador-duplicado, nombre-repetido | Ya existe algo con esa clave. |
plantilla-en-uso | La plantilla está asignada. ?forzar=1 borra también las asignaciones. |
rol-en-uso / rol-de-sistema | El rol tiene usuarios, o es uno de los tres de sistema. |
maestro-protegido / sin-maestro / ultimo-operador | Protecciones para no dejar una cuenta sin quien la administre. |
exportacion-no-lista | El ZIP todavía se está armando. |
sin-documentos | El período no tiene nada que exportar. |
sin-cuenta-de-soporte | El contribuyente no tiene habilitado el acceso de soporte. |
413 — demasiado grande
| Código | Cuándo |
|---|---|
cuerpo-grande | El cuerpo pasa del máximo. Un DTE de 2000 ítems no llega a 1 MB. |
logotipo-grande | La imagen del logotipo pasa del máximo. |
422 — los datos no son válidos
| Código | Cuándo |
|---|---|
documento-invalido | El DTE no pasa las reglas de su tipo o el esquema de MH. errors trae todo. |
datos-invalidos | Genérico de validación. |
datos-incompletos | Faltan campos obligatorios. |
punto-venta-invalido | El establecimiento o el punto de venta no existen. |
invalidacion-invalida | Los datos de la anulación no son válidos. |
tipo-sin-retorno | Ese tipo de DTE no admite Evento de Retorno (un CCF se ajusta con Nota de Crédito). |
retorno-excede | La suma de retornos superaría el valor del documento original. |
retorno-invalido | Los datos del retorno no son válidos. |
certificado-invalido / contrasena-llave-incorrecta | El archivo no es un certificado válido, o la contraseña de la llave no abre. |
ambiente-invalido | Ambiente distinto de 00 o 01. |
nit-inmutable | El NIT no se cambia desde el panel: es la llave del login. |
contrasena-debil | La contraseña no cumple el mínimo. |
scopes-invalidos | Se pidió un scope que no existe. |
url-invalida | La URL del webhook no sirve. |
eventos-invalidos | Se pidió un evento de webhook desconocido. |
lote-vacio / lote-invalido / lote-excedido | Problemas con el lote. |
contingencia-invalida | Los datos de la contingencia no son válidos. |
responsable-incompleto / responsable-no-configurado | Falta el responsable de contingencias. |
plantilla-invalida / plantilla-desconocida / diseno-desconocido / asignacion-invalida | Problemas con la plantilla del PDF. |
imagen-invalida | El logotipo no es una imagen que se pueda leer. |
formato-desconocido | Se pidió un formato de salida que no existe. |
no-se-pudo-dibujar | El PDF no se pudo generar con esa plantilla. |
configuracion-invalida | La configuración enviada no es coherente. |
429 — demasiadas peticiones
| Código | Cuándo |
|---|---|
demasiados-intentos | Limitador por IP en el login. |
cuenta-bloqueada | Bloqueo progresivo por fallos de contraseña. |
500 y 503 — el problema es del servidor
| Código | Cuándo |
|---|---|
error-interno | Fallo no previsto. Reintenta con espera exponencial. |
documento-corrupto | El documento guardado no se puede leer. Hay que reportarlo. |
catalogos-sin-sembrar | La instalación no tiene los catálogos de MH cargados. |
sin-almacen | El almacén de objetos no responde. |
correo-no-entregado | El proveedor de correo rechazó el envío (502). |