Ir al contenido

Emisión de e-CFRechazos y reintentos

Rechazos y reintentos

Ver como Markdown

Un rechazo de la DGII no siempre es definitivo. A veces el problema está en el propio comprobante y hay que emitir uno nuevo. Otras veces la DGII falla de forma pasajera, por ejemplo durante un mantenimiento, y basta con enviar de nuevo el mismo comprobante. NovaFE distingue ambos casos para que no tengas que hacerlo tú.

Un comprobante rechazado queda con status: "rejected" y el motivo en dgii.messages, una lista de objetos { code, value }. El campo dgii.sequenceUsed responde la pregunta más importante: si la DGII consumió el e-NCF.

dgii.sequenceUsed Significado
false La DGII no consumió el e-NCF. El número sigue asociado a este comprobante y se puede reenviar.
true o ausente La DGII consumió el e-NCF. Queda invalidado de forma permanente.
Situación Qué hace NovaFE Qué haces tú
Falla pasajera de la DGII (por ejemplo, el código 004) y el número quedó libre Reenvía el mismo comprobante por su cuenta, en intervalos. Nada. Espera el resultado final por webhook.
Rechazo por otro motivo y el número quedó libre Lo deja en rejected. Reenviarlo con POST /ecf/{id}/retry si lo consideras adecuado.
Rechazo con el número consumido Lo deja en rejected. /retry responde 409. Emitir un comprobante nuevo, que recibirá otro e-NCF.

Cuando la DGII rechaza un comprobante y dice que no consumió el e-NCF, y además el motivo es uno de los que NovaFE reconoce como falla pasajera de la DGII, el comprobante no llega a quedar rejected. En su lugar:

  1. Vuelve a signed. En dgii.messages puedes ver el último motivo que informó la DGII mientras se espera el reenvío.
  2. NovaFE lo reenvía más tarde, con el mismo XML, el mismo e-NCF, el mismo código de seguridad y el mismo QR. Por eso la representación impresa que ya hayas entregado sigue siendo válida.
  3. Si la DGII lo acepta, pasa a accepted como cualquier otro comprobante.

No se emite ecf.rejected mientras dure el reintento. Solo se emite si los reintentos se agotan y el rechazo pasa a ser definitivo. En cambio, cada reenvío que la DGII recibe emite de nuevo ecf.submitted, porque es una recepción nueva.

Por defecto, NovaFE reintenta hasta 6 veces, esperando 2 minutos, 10 minutos, 30 minutos, 2 horas, 6 horas y 12 horas, unas 20 horas en total. Es tiempo suficiente para un mantenimiento largo de la DGII sin dejar un comprobante pendiente de forma indefinida.

POST /api/v1/ecf/{id}/retry

Acepta un comprobante en estado failed, review o rejected. En el caso de rejected exige que la DGII haya dejado libre el e-NCF.

Respuesta Cuándo
202 El comprobante volvió a signed y se reenviará. La respuesta trae el comprobante actualizado.
404 El comprobante no existe o no es de tu tenant.
409 El estado no admite reintento, o la DGII consumió el e-NCF de un comprobante rejected.

GET /api/v1/ecf/{id}/attempts devuelve un renglón por cada resultado que la DGII dio para el comprobante, del más antiguo al más reciente. Es útil porque el comprobante solo muestra el último resultado: sin el historial, un rechazo anterior a un reenvío exitoso dejaría de verse.

[
{
"attempt": 1,
"outcome": "rejected",
"trackId": "d64c95e4-69c3-4f6f-bd9b-cd3da8b9284f",
"statusCode": 2,
"statusText": "Rechazado",
"sequenceUsed": false,
"messages": [
{
"code": 4,
"value": "Ha ocurrido un error validando en eCF, favor intentar nueva vez."
}
],
"retryDecision": "auto:4",
"xmlSha256": "3f8a1c9e2b7d4f60a5c8e1d2b3a4f5061c7e8d9f0a1b2c3d4e5f60718293a4b5",
"recordedAt": "2026-09-25T15:32:42-04:00"
},
{
"attempt": 2,
"outcome": "accepted",
"trackId": "a12b34c5-6d7e-4f80-9a1b-2c3d4e5f6a7b",
"statusCode": 1,
"statusText": "Aceptado",
"sequenceUsed": true,
"messages": [],
"retryDecision": null,
"xmlSha256": "3f8a1c9e2b7d4f60a5c8e1d2b3a4f5061c7e8d9f0a1b2c3d4e5f60718293a4b5",
"recordedAt": "2026-09-25T15:34:50-04:00"
}
]
Campo Nota
attempt 1, 2, 3, dentro del comprobante.
outcome El estado en que quedó el comprobante: accepted, accepted_conditional, rejected, review o failed.
retryDecision En un rechazo: auto:<código> si NovaFE lo reenvió por su cuenta, o exhausted si era reintentable pero se agotaron los reintentos. Ausente en los demás.
xmlSha256 Huella del XML enviado. Si dos renglones repiten el valor, se enviaron exactamente los mismos bytes.
recordedAt Cuándo se registró el resultado.

Un comprobante que aún no tiene ningún resultado devuelve una lista vacía. Los reintentos de transporte que no llegan a producir un resultado, como un tiempo de espera agotado, no generan renglón.

  • No emitas un comprobante nuevo ante un rechazo sin revisar dgii.sequenceUsed. Si es false, el comprobante original todavía puede completarse. Emitir otro dejaría un e-NCF sin usar y un comprobante duplicado.
  • Suscríbete a los webhooks en lugar de consultar en bucle. Un rechazo reintentable no genera ecf.rejected, así que recibir ese evento indica que el resultado ya es definitivo.
  • Manda siempre Idempotency-Key en POST /ecf (es obligatorio), para que un reintento de tu lado no emita dos comprobantes (ver Endpoints y semántica).