XmlPeruDevDocs

Cómo leer la respuesta

Qué significa cada combinación de campos y qué tienes que hacer en cada caso.

Toda respuesta contesta tres preguntas distintas, y confundirlas es el error de integración más caro que existe: lleva a emitir dos veces un comprobante que ya estaba declarado.

Campo Pregunta que contesta
success ¿La API pudo trabajar?
connection ¿Se pudo hablar con SUNAT?
sunat_success ¿Qué dijo SUNAT? true · false · null

Son independientes. success: true no significa aceptado: significa que no hubo un fallo nuestro. El veredicto está en sunat_success, nunca en el código HTTP ni en success.

sunat_success es el campo corto: true aceptado (con o sin observaciones), false rechazado, null todavía sin pronunciarse. Ese null viaja siempre, también cuando no hay veredicto: un campo ausente te obligaría a distinguir «no llegó el dato» de «SUNAT no ha contestado», y son cosas distintas.

Si necesitas el detalle —en qué etapa está, no solo si terminó— está en resolved y status_code (en la superficie de migración se llaman resuelto y state_type_id; son los mismos valores):

La tabla

Busca tu fila y haz lo que dice la última columna.

sunat_success resolved status_code Qué pasó Qué haces
null false 01 Firmado. El envío está en cola Nada. Consulta más tarde
null false 02 Firmado, esperando a que lo mandes Mandarlo: compat o v1
null false 03 Enviado. SUNAT todavía no contesta Nada. No reenvíes
null false 04 Boleta esperando el resumen del día Nada
true true 05 Aceptado Nada. Guarda el CDR
true true 07 Aceptado con observaciones Nada. Es válido y está declarado
false true 09 Rechazado: no existe para SUNAT Corregir y emitir de nuevo

Y si success es false, nada de lo anterior aplica: la API no pudo trabajar y el motivo está en message y errors.

Los tres errores que cuestan dinero

«No está aceptado, lo emito otra vez». Mientras resolved sea false no hay veredicto: el comprobante puede estar aceptándose en este momento. Emitirlo de nuevo consume otro correlativo y te deja dos comprobantes.

if (!aceptado)                  reemitir()   // MAL: también entra lo que sigue en curso
if (r.status === 'rejected')    corregir()   // BIEN
if (!r.resolved)                esperar()    // BIEN

«Observado es un fallo». 07 está aceptado. SUNAT lo declaró y anotó una observación. Tratarlo como rechazo re-emite algo que ya existe.

«El HTTP dice 200, entonces salió bien». El HTTP dice si nuestra API pudo trabajar. Un comprobante rechazado por SUNAT llega con 200 y status: "rejected".

Cuando no se sabe si llegó

sunat_success te dice el veredicto, pero no si el comprobante llegó. Para eso está la señal de llegada, que viaja en las dos superficies — llego_a_sunat suelto en /api/cpe/*, y reached_sunat dentro de result en /v1—:

Valor Significa Qué haces
false No llegó Se puede reintentar sin riesgo
true Llegó y hay veredicto Leer code y message
null No se sabe Consultar. Nunca reenviar a ciegas

El null es el caso importante: pasa cuando SUNAT no responde a tiempo. El comprobante ya salió, así que reenviarlo puede duplicarlo. Nosotros lo reconciliamos solos consultando a SUNAT; tú solo tienes que no reenviar.

El campo estado, y por qué no deberías usarlo

En /api/cpe/* verás también un campo estado. No lo uses para decidir nada: existe por compatibilidad y significa cosas distintas según el caso — el código de etapa ("01", "03") mientras el comprobante está en curso, y el número 200 cuando ya se resolvió, que es un código HTTP repetido dentro del cuerpo y no un estado.

Está ahí para no romper integraciones que ya lo leían. Para saber qué pasó, usa resolved y status_code, que significan siempre lo mismo — y son los que tiene también la API v1.