Códigos de Error
Referencia completa de los errores que puede retornar la API y cómo manejarlos.
Formato de Respuesta de Error
Los errores siguen este formato JSON (algunos añaden campos extra, como supplier_ruc o usage):
{"detail": "Mensaje descriptivo","error_code": "categoria.error_especifico"}
El campo error_code usa el formato categoria.error para facilitar el manejo programático de errores. Las respuestas del control de cuota (por ejemplo quota_exceeded) no llevan punto. Los mensajes detail están mayormente en español; algunos de cuota y de error interno vienen en inglés: usa siempre error_code para tu lógica.
Códigos HTTP
Bad Request
La petición tiene un formato inválido o faltan parámetros requeridos. No reintentar.
Posibles causas:
- •Formato de document_id incorrecto
- •JSON malformado en el body
- •API Key sin Clave SOL configurada (Receptor)
- •Emisor: el RUC del document_id no coincide con el RUC de la API Key
Unauthorized
No se proporcionó API Key o la key es inválida. No reintentar.
Posibles causas:
- •Header Authorization faltante
- •API Key expirada o revocada
- •Formato incorrecto del Bearer token
Forbidden
API Key válida pero sin permisos para el recurso. No reintentar.
Posibles causas:
- •Plan sin acceso a la función (por ejemplo, PDF)
- •Período de prueba terminado (auth.trial_expired / trial_expired)
- •Plan mensual inactivo (plan_inactive)
Not Found
El recurso solicitado no existe. Es una respuesta definitiva: no reintentar.
Posibles causas:
- •SUNAT no tiene el comprobante (sunat.document_not_found): verifica el ID y el módulo (receptor/emisor)
- •Endpoint incorrecto
Unprocessable Entity
La petición es válida pero no se puede atender para este caso. Definitivo: no reintentar.
Posibles causas:
- •CDR no disponible para ese comprobante (sunat.resource_not_available)
- •Emisor excluido de forma permanente (sunat.supplier_excluded): omite sus comprobantes
- •Usuario SOL sin permiso de Recibos por Honorarios (sunat.rxh_permission_required)
- •Parámetros inválidos en el listado de RxH (rxh.*)
Too Many Requests
Límite excedido. Espera los segundos del header Retry-After y reintenta.
Posibles causas:
- •Demasiadas peticiones por minuto
- •Cuota mensual agotada (quota_exceeded): no se renueva hasta el próximo ciclo; Retry-After indica cuándo
- •Demasiadas descargas simultáneas para un mismo RUC (sunat.ruc_concurrency_limit)
- •Cuota del proveedor enterprise (enterprise_quota_exceeded)
Internal Server Error
Error interno del servidor (internal_error).
Posibles causas:
- •Error inesperado en el servidor
- •Reintentar con espera creciente; si persiste, contactar a soporte con el X-Correlation-ID
Bad Gateway
SUNAT rechazó las credenciales SOL (sunat.auth_failed). NO reintentar.
Posibles causas:
- •Clave SOL incorrecta, cuenta SOL bloqueada o permiso no habilitado
- •Reintentar con las mismas credenciales puede bloquear la cuenta SOL del titular
- •Actualiza la Clave SOL de la API Key (elimínala y crea una nueva)
Service Unavailable
Falla pasajera o "todavía no". Reintentar respetando el header Retry-After.
Posibles causas:
- •SUNAT no respondió o falló (sunat.request_failed, sunat.auth_unavailable): reintentar con espera creciente
- •SUNAT todavía no tiene el XML/PDF/info (sunat.document_not_ready): reintentar en horas, no en segundos
- •No se pudo verificar la cuota (service_unavailable): reintentar en unos segundos
Códigos de Error por Categoría
El campo error_code identifica el tipo específico de error:
auth.*Autenticación
Errores relacionados con la API Key y permisos
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
auth.invalid_api_key | 401 | API Key inválida o no proporcionada | Verificar API Key en el dashboard |
auth.api_key_required | 401 | Este endpoint requiere autenticación con API Key | Usar API Key (sk_live_xxx) en vez de session token |
auth.trial_expired | 403 | Tu período de prueba ha terminado | No reintentar: suscribirse a un plan o comprar una bolsa |
auth.max_rucs_exceeded | 403 | Has alcanzado el límite de RUCs para tu plan | Actualizar a un plan superior. En /enterprise/api-keys responde 400: eliminar llaves sin uso o ampliar el contrato |
auth.feature_not_allowed | 403 | Función no disponible en tu plan actual | Actualizar plan para acceder a esta función |
auth.master_key_required | 401 | Master key requerida (endpoints /enterprise/*) | Enviar Authorization: Bearer mk_live_... |
auth.master_key_invalid | 401 | Master key invalida o inactiva | Verificar la master key; si fue rotada, usar la nueva |
auth.master_key_expired | 401 | Master key expirada | Solicitar una master key nueva |
auth.provider_inactive | 403 | Proveedor enterprise suspendido o terminado | Contactar a IntegraCPE |
auth.master_key_renew_denied | 403 | La master key no puede renovar llaves | Para rotar la Clave SOL de un RUC, eliminar la API key y crear una nueva |
auth.module_not_enabled | 403 | El proveedor no tiene habilitado el módulo del endpoint | No reintentar: contactar a IntegraCPE para habilitar el módulo |
auth.module_mismatch | 403 | La API key no es del módulo que requiere el endpoint | No reintentar: usar una API key del módulo correcto |
validation.*Validación
Errores en el formato de los datos enviados
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
validation.bad_request | 400 | Solicitud inválida | Revisar formato del request |
validation.invalid_ruc | 400 | RUC debe ser exactamente 11 dígitos numéricos | Validar RUC antes de enviar |
validation.invalid_document_id | 400 | Formato de documento inválido | Usar formato: {RUC}-{TipoDoc}-{Serie}-{Numero} |
validation.module_not_enabled | 400 | Módulo no habilitado para este proveedor enterprise | Contratar el módulo o usar uno habilitado |
validation.credentials_required | 400 | Faltan credenciales SUNAT para el módulo | Enviar sunat_ruc, sunat_username y sunat_password |
resource.*Recursos
El recurso pedido no existe
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
resource.not_found | 404 | API key no encontrada | Verificar el id; una llave ya eliminada también da 404 |
sunat.*SUNAT
Errores relacionados con la comunicación con SUNAT
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
sunat.auth_failed | 502 | SUNAT rechazó las credenciales SOL (clave incorrecta, cuenta bloqueada o permiso no habilitado) | No reintentar: actualizar la Clave SOL de la API Key (eliminarla y crear una nueva). Reintentar puede bloquear la cuenta SOL |
sunat.auth_unavailable | 503 | SUNAT no respondió durante la autenticación | Reintentar con espera creciente, respetando Retry-After (30 s) |
sunat.request_failed | 503 | SUNAT falló al entregar el documento (500/502/503 de SUNAT) | Reintentar con espera creciente (backoff), respetando Retry-After (30 s) |
sunat.document_not_found | 404 | SUNAT no tiene este comprobante | No reintentar: definitivo (ningún documento reapareció en 30 días). Verificar el ID y el módulo (receptor/emisor) |
sunat.document_not_ready | 503 | SUNAT todavía no tiene resultados para este comprobante | Reintentar en horas (mediana observada: 1,4 h), respetando Retry-After (1 hora). Boletas informadas por Resumen Diario: pueden responder esto de forma sostenida |
sunat.resource_not_available | 422 | El recurso (normalmente el CDR) no está disponible para este comprobante | No reintentar: es definitivo (el emisor no registró el CDR en SUNAT) |
sunat.supplier_excluded | 422 | El emisor (supplier_ruc) está excluido de forma permanente por un problema conocido en SUNAT. Incluye supplier_ruc, reason y excluded_since | No reintentar: omitir los demás comprobantes de ese emisor. No consume cuota |
sunat.ruc_concurrency_limit | 429 | Demasiadas descargas simultáneas para ese RUC | Reducir las peticiones concurrentes por RUC y reintentar tras Retry-After (2 s) |
sunat.rxh_permission_required | 422 | El usuario SOL no tiene habilitado Recibo por Honorarios Electrónico - Consulta | No reintentar: el titular del RUC debe habilitar la opción en SUNAT |
sunat.token_expired | 401 | El token de SUNAT expiró | Reintentar la petición |
sunat.credentials_not_configured | 400 | API Key sin credenciales SUNAT configuradas | Configurar Clave SOL en la API Key (solo Receptor) |
rate_limit.*Límite de peticiones
Límites de peticiones excedidos. Enterprise: los límites vigentes se consultan en GET /enterprise/limits
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
rate_limit.exceeded | 429 | Límite de peticiones excedido | Esperar según header Retry-After |
rate_limit.token_auth | 429 | Límite de autenticación SUNAT excedido (el token está en caché) | Esperar según header Retry-After |
rxh.*Recibos por Honorarios
Parámetros inválidos en el listado de RxH
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
rxh.invalid_periodo | 422 | El periodo debe tener formato YYYYMM | No reintentar: corregir el parámetro periodo |
rxh.invalid_page | 422 | page debe ser >= 1 | No reintentar: corregir el parámetro page |
cuota.*Cuota y plan
Respuestas del control de cuota y errores generales. Su error_code no lleva punto; el cuerpo puede traer además upgrade_url, usage, limit_type y reason
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
quota_exceeded | 429 | Límite por minuto o cuota mensual excedidos (limit_type: per_minute o per_month; el detail viene en inglés) | Esperar Retry-After. Si es per_month, ampliar plan o comprar bolsa |
enterprise_quota_exceeded | 429 | Límite del proveedor enterprise excedido para este ciclo | Esperar al próximo ciclo o ampliar el contrato |
trial_expired | 403 | Período de prueba terminado (reason: requests o days) | No reintentar: suscribirse a un plan o comprar una bolsa |
plan_inactive | 403 | Tu plan mensual está inactivo | No reintentar: activar el plan desde el portal |
service_unavailable | 503 | No se pudo verificar la cuota | Reintentar tras Retry-After (5 s) |
emisor_ruc_mismatch | 400 | Módulo emisor: el RUC del document_id no coincide con el RUC de las credenciales de la API Key | No reintentar: solo se consultan comprobantes emitidos por tu propio RUC |
internal_error | 500 | Error interno no controlado (el detail viene en inglés) | Reintentar con espera creciente; si persiste, contactar a soporte con el X-Correlation-ID |
server.*Servidor
Errores internos del servidor
| Código | HTTP | Mensaje | Acción |
|---|---|---|---|
server.key_creation_failed | 200 | Falla interna al crear una llave en el alta masiva (viene por ítem en errors[]) | Reintentar ese ítem; si persiste, contactar a IntegraCPE |
server.service_unavailable | 503 | Servicio no disponible temporalmente | Reintentar usando el header Retry-After |
server.internal_error | 500 | Error interno del servidor | Contactar soporte si persiste |
Manejo de Errores Recomendado
async function downloadXML(documentId: string) {try {const response = await fetch(`https://api.integracpe.com/api/v1/sunat/receptor/xml/${documentId}`,{headers: {'Authorization': 'Bearer sk_live_tu_api_key'}});if (!response.ok) {const error = await response.json();const [category] = String(error.error_code).split('.');switch (category) {case 'auth':if (error.error_code === 'auth.trial_expired') {console.log('Trial expirado. Suscríbete a un plan.');// Redirigir a página de planes} else {console.log('Error de autenticación:', error.detail);}break;case 'sunat': {const retryAfter = response.headers.get('Retry-After');if (error.error_code === 'sunat.auth_failed') {// Definitivo: la clave SOL no sirve. NO reintentar (puede bloquear la cuenta SOL)console.log('Actualizar la Clave SOL de la API Key:', error.detail);} else if (error.error_code === 'sunat.document_not_ready') {// "Todavía no": reintentar en horas, no en segundosconsole.log(`Aún no disponible. Reintentar en ${retryAfter}s o más`);} else if (error.error_code === 'sunat.request_failed' ||error.error_code === 'sunat.auth_unavailable') {console.log(`Falla pasajera de SUNAT. Reintentar en ${retryAfter}s (con backoff)`);} else {// document_not_found, resource_not_available, supplier_excluded: definitivosconsole.log('Definitivo, no reintentar:', error.detail);}break;}case 'rate_limit':console.log(`Rate limit. Esperar ${response.headers.get('Retry-After')}s`);break;case 'validation':console.log('Error de validación:', error.detail);// Mostrar error al usuariobreak;default:// Cuota y otros sin prefijo con punto: quota_exceeded, enterprise_quota_exceeded...if (error.error_code === 'quota_exceeded' || error.error_code === 'enterprise_quota_exceeded') {console.log(`Cuota excedida. Reintentar en ${response.headers.get('Retry-After')}s`);} else {console.log('Error:', error.detail);}}throw new Error(error.detail);}return await response.json();} catch (error) {console.error('Error de conexión:', error);throw error;}}
Política de Reintentos
Reintentar
- ✓
429- Cuota o concurrencia por RUC: esperar el header Retry-After - ✓
503 sunat.request_failed / auth_unavailable- Falla pasajera de SUNAT: backoff, respetando Retry-After - ✓
503 sunat.document_not_ready- Todavía no: reintentar en horas (mediana 1,4 h), no en segundos - ✓
500- Error interno: backoff; si persiste, soporte con X-Correlation-ID
No Reintentar
- ✗
400 / 401 / 403- Request inválido, API Key inválida, sin permisos o trial terminado - ✗
404- Documento no existe en SUNAT: definitivo - ✗
422- CDR no disponible, emisor excluido, sin permiso RxH: definitivo - ✗
502 sunat.auth_failed- Clave SOL rechazada: reintentar puede bloquear la cuenta SOL
Límite de peticiones y headers
La API incluye headers sobre tu uso y sobre cuándo reintentar:
| Header | Descripción |
|---|---|
| X-RateLimit-Limit | Límite mensual de tu plan (peticiones por ciclo). En un 429 por cuota, el límite que se excedió |
| X-RateLimit-Remaining | Peticiones que te quedan en el ciclo actual (0 en un 429 por cuota) |
| X-RateLimit-Reset | Momento en que se reinicia el ciclo (timestamp Unix, en segundos) |
| Retry-After | Segundos a esperar antes de reintentar (en 429 y en 503) |
| X-Correlation-ID | Identificador de la petición. Se devuelve en las respuestas y puedes enviarlo tú (máx. 36 caracteres alfanuméricos o guion) para trazar una llamada |
Logging
error_code completo en tus logs para facilitar el debugging. Es más útil que solo el código HTTP.Soporte
X-Correlation-ID).