Errores

Comprende los códigos de error y cómo manejarlos en tu integración

Estructura de Error Estándar

La mayoría de errores de la API siguen esta estructura simple con message y statusCode:

Error 401 - API Key Inválida
{
  "message": "Unauthorized",
  "statusCode": 401
}

Sin embargo, los errores de validación (400) incluyen detalles más específicos:

Error 400 - Validación de Items
1{
2  "mensaje": "📝 Items inválidos",
3  "errores": [
4    {
5      "item": 1,
6      "campo": "precioUni",
7      "valor": -10,
8      "regla": "El precio unitario debe ser mayor o igual a 0"
9    }
10  ],
11  "statusCode": 400
12}

Códigos de Estado HTTP

400Petición Incorrecta
La petición contiene parámetros inválidos o faltantes. Revisa la estructura de tu payload.
401No Autorizado
API key faltante, inválida, expirada o eliminada
403Prohibido
Has alcanzado el límite de tu cuota mensual de DTEs
404No Encontrado
El recurso solicitado no existe o el endpoint no es válido
429Límite de Velocidad Excedido
Has excedido el límite de peticiones por segundo/minuto. Revisa los headers de rate limit.
500Error del Servidor
Error interno del servidor o del Ministerio de Hacienda. Reintenta más tarde.

Errores de Autenticación (401)

API Key Inválida o Faltante

{
  "message": "Unauthorized",
  "statusCode": 401
}

API Key Expirada

{
  "message": "API Key expirada",
  "statusCode": 401
}

API Key Eliminada

{
  "message": "API Key eliminada",
  "statusCode": 401
}

Errores de Validación (400)

Errores de Items
Validaciones específicas de los items del DTE
1{
2  "mensaje": "📝 Items inválidos",
3  "errores": [
4    {
5      "item": 1,
6      "campo": "cantidad",
7      "valor": 0,
8      "regla": "La cantidad debe ser mayor a 0"
9    },
10    {
11      "item": 2,
12      "campo": "descripcion",
13      "regla": "La descripción es requerida"
14    }
15  ],
16  "statusCode": 400
17}
Errores de Pagos
Validaciones de los pagos del DTE
1{
2  "mensaje": "💰 Pagos inválidos",
3  "errores": [
4    {
5      "pago": 1,
6      "campo": "montoPago",
7      "valor": -100,
8      "regla": "El monto de pago debe ser mayor a 0"
9    }
10  ],
11  "sugerencia": "Revise los montos de los pagos",
12  "statusCode": 400
13}
Error de Totales
Cuando los totales no cuadran con una tolerancia de $0.01
1{
2  "mensaje": "🧮 Totales incorrectos",
3  "esperado": 150.00,
4  "recibido": 148.50,
5  "diferencia": 1.50,
6  "sugerencia": "La diferencia excede la tolerancia de $0.01",
7  "statusCode": 400
8}
Error de Schema JSON
Cuando el DTE no cumple con el schema oficial del Ministerio de Hacienda
1{
2  "mensaje": "❌ El DTE no cumple con el schema JSON del Ministerio de Hacienda",
3  "errores": [
4    {
5      "path": "/receptor/numDocumento",
6      "message": "must match pattern \"^[0-9]{4}-[0-9]{6}-[0-9]{3}-[0-9]$\"",
7      "expected": "NIT con formato 0614-010190-103-1"
8    }
9  ],
10  "statusCode": 400
11}

Errores de Cuota (403)

Cuota Mensual Agotada
1{
2  "mensaje": "🚫 Cuota de DTEs agotada",
3  "usado": 30,
4  "limite": 30,
5  "plan": "FREE",
6  "sugerencia": "Actualiza tu plan para emitir más DTEs este mes",
7  "statusCode": 403
8}

Errores del Ministerio de Hacienda (500)

Estos errores provienen directamente del Ministerio de Hacienda cuando el DTE es rechazado:

Respuesta de Error del MH
Cuando el MH rechaza el DTE, la respuesta incluye códigos y observaciones específicas
1{
2  "estado": "RECHAZADO",
3  "codigoGeneracion": "A1B2C3D4-E5F6-7890-ABCD-EF1234567890",
4  "fhProcesamiento": "2025-01-16T10:30:05.000Z",
5  "codigoMsg": "801",
6  "descripcionMsg": "Certificado de firma inválido o expirado",
7  "observaciones": [
8    "El certificado X.509 ha expirado",
9    "Debe renovar su certificado digital ante el Ministerio de Hacienda"
10  ],
11  "clasificaMsg": "ERROR"
12}

Códigos Comunes del MH

001Procesado correctamente ✅
801Certificado inválido o expirado
802Número de control duplicado
803Establecimiento no registrado
804NIT del emisor inválido
805Firma digital inválida

Rate Limiting (429)

La API implementa rate limiting en 3 niveles:

Short
3 req/segundo

Máximo 3 peticiones por segundo

Medium
20 req/10s

Máximo 20 peticiones cada 10 segundos

Long
100 req/minuto

Máximo 100 peticiones por minuto

Headers de Rate Limit

Headers en cada respuesta
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642341600
Error 429
{
  "message": "ThrottlerException: Too Many Requests",
  "statusCode": 429
}

Manejo Recomendado

Ejemplo completo de manejo de errores
1async function emitirDTE(dteData) {
2  try {
3    const response = await fetch('http://localhost:3000/factura/emitir', {
4      method: 'POST',
5      headers: {
6        'X-API-Key': 'sk_user_tu_hash_aqui',
7        'Content-Type': 'application/json'
8      },
9      body: JSON.stringify(dteData)
10    });
11
12    if (!response.ok) {
13      const error = await response.json();
14
15      // Manejar diferentes tipos de error
16      switch (response.status) {
17        case 400:
18          console.error('Error de validación:', error.mensaje);
19          if (error.errores) {
20            console.error('Detalles:', error.errores);
21          }
22          // NO reintentar - corregir datos
23          break;
24
25        case 401:
26          console.error('API key inválida:', error.message);
27          // NO reintentar - verificar API key
28          break;
29
30        case 403:
31          console.error('Cuota agotada:', error.mensaje);
32          console.log('Plan actual:', error.plan);
33          console.log('Usado:', error.usado, 'de', error.limite);
34          // NO reintentar - actualizar plan
35          break;
36
37        case 429:
38          console.log('Rate limit excedido, esperando...');
39          const resetTime = response.headers.get('X-RateLimit-Reset');
40          const waitTime = (resetTime * 1000) - Date.now();
41          await new Promise(resolve => setTimeout(resolve, waitTime));
42          // REINTENTAR después de esperar
43          return emitirDTE(dteData);
44
45        case 500:
46          console.error('Error del servidor:', error.message);
47          // REINTENTAR con backoff exponencial
48          await new Promise(resolve => setTimeout(resolve, 5000));
49          return emitirDTE(dteData);
50
51        default:
52          console.error('Error desconocido:', error);
53      }
54
55      throw new Error(error.message || error.mensaje);
56    }
57
58    const resultado = await response.json();
59
60    // Verificar estado del MH
61    if (resultado.estado === 'RECHAZADO') {
62      console.error('DTE rechazado por Hacienda');
63      console.error('Código:', resultado.codigoMsg);
64      console.error('Descripción:', resultado.descripcionMsg);
65      console.error('Observaciones:', resultado.observaciones);
66      throw new Error(resultado.descripcionMsg);
67    }
68
69    console.log('✅ DTE emitido exitosamente');
70    console.log('Código de generación:', resultado.codigoGeneracion);
71    console.log('Número de control:', resultado.numeroControl);
72
73    return resultado;
74
75  } catch (error) {
76    console.error('Error al emitir DTE:', error);
77    throw error;
78  }
79}

Mejores Prácticas

Recomendaciones
  • 400 Bad Request: NO reintentar. Corregir los datos del payload.
  • 401 Unauthorized: NO reintentar. Verificar API key válida y no expirada.
  • 403 Forbidden: NO reintentar. Actualizar plan o esperar al próximo ciclo.
  • 429 Too Many Requests: SÍ reintentar. Respetar headers X-RateLimit-Reset.
  • 500 Internal Server Error: SÍ reintentar con backoff exponencial (5s, 10s, 20s).
  • • Registrar todos los errores incluyendo codigoGeneracion para auditoría.
  • • Guardar las observaciones del MH para debugging.
  • • Usar timeouts apropiados (30-60 segundos para emisión de DTEs).
  • • Monitorear headers de rate limit para evitar errores 429.