Errores
Todos los errores de la API siguen el mismo formato (envelope), independientemente del código HTTP.
Formato de error
{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "El campo 'series' es requerido",
"details": {
"fields": {
"series": "El campo 'series' es requerido"
}
}
}
}Códigos
| code | HTTP | Significado |
|---|---|---|
BAD_REQUEST | 400 | Error de validación — revisa los campos indicados en details.fields. |
MISSING_TOKEN | 401 | No se envió Authorization: Bearer <key>. |
TOKEN_EXPIRED | 401 | La key es inválida, fue revocada o expiró. |
INSUFFICIENT_PERMISSIONS | 403 | La key no tiene el scope necesario para esta operación. |
NOT_FOUND | 404 | El recurso solicitado no existe. |
ALREADY_EXISTS | 409 | Ya existe un recurso con esos datos (por ejemplo, un correlativo duplicado). |
DOMAIN_ERROR | 422 | La solicitud es válida pero viola una regla de negocio (por ejemplo, anular un comprobante ya anulado). |
RATE_LIMIT_EXCEEDED | 429 | Superaste el límite de solicitudes por minuto de la key. |
INTERNAL_ERROR | 500 | Error inesperado del servidor — reintenta más tarde. |
Errores de validación (422)
Cuando varios campos fallan a la vez, details.fields trae una entrada por cada uno — úsalo para mapear el error directamente a tu formulario en lugar de parsear message.
Validación con múltiples campos
{
"success": false,
"error": {
"code": "BAD_REQUEST",
"message": "La solicitud tiene 2 errores de validación",
"details": {
"fields": {
"series": "El campo 'series' es requerido",
"items": "Debe incluir al menos un ítem"
}
}
}
}Un 200 OK no garantiza que SUNAT aceptó
Emitir un comprobante (
POST /comprobantes/emitir) responde 200 OK incluso cuando SUNAT lo rechaza — el rechazo no es un error HTTP. Revisa siempre data.documento.estado en la respuesta — ver Estados de documento.Comprobante creado, envío a SUNAT falló
Este caso tampoco sigue el envelope de arriba: si
POST /comprobantes/emitir crea el comprobante pero falla al transmitirlo, responde con code: "INVOICE_SEND_FAILED_AFTER_CREATE", comprobante_id y una ruta reintentar_en para reintentar solo el envío — ver el detalle en Crear comprobante.Reintentos
Ante un 500 INTERNAL_ERROR, reintenta con backoff exponencial (por ejemplo 1s, 2s, 4s) hasta 3 veces antes de escalar. Los códigos 4xx no son transitorios — reintentarlos sin corregir la solicitud siempre falla igual.