Ciclo de vida de un e-CF

Ciclo de vida de un e-CF

Enviar un e-CF y saber si la DGII lo acepto son dos eventos distintos, separados en el tiempo. Esta página describe el ciclo completo. Si solo lees una página de conceptos antes de integrar, que sea esta.

Los estados

status Terminal Significado
processing No El documento existe en FiscalFlow y está en camino a la DGII, o ya llegó y la DGII todavía no resuelve. Es el estado con el que nace todo documento.
accepted Si La DGII lo acepto. El e-CF tiene validez fiscal, el PDF y el XML firmado están disponibles.
rejected Si Rechazado. error_message trae el motivo, casi siempre en las palabras exactas de la DGII. Ver Errores y rechazos.
accepted_conditional Si Aceptado con observaciones. Se trata como autorizado en todo el sistema. Hoy la respuesta de la DGII se normaliza a accepted, así que en la práctica no lo verás; el valor existe para no romper clientes si eso cambia.
cancelled Si Anulado. Ver Anular un e-CF.
draft No Valor por defecto de la base de datos. Ningun documento creado por la API llega a tus manos en este estado.

La respuesta de cualquier documento trae además dos campos derivados que suelen ser más comodos que comparar status a mano: authorized (booleano, verdadero para accepted y accepted_conditional) y legal_status.

El camino normal: asíncrono

Al recibir POST /v1/documents, FiscalFlow arma el XML, lo firma y lo transmite a la DGII dentro de la misma petición. Lo que la DGII devuelve ahí no es un veredicto: es un acuse con un trackId. El documento queda en processing y la respuesta HTTP es 202 Accepted.

202 Response
{
  "success": true,
  "data": {
    "id": "01jz8k4qm2n5p7r9t1v3w5x7y9",
    "external_ref": "venta-2026-000412",
    "type": "31",
    "type_label": "Factura Crédito Fiscal",
    "encf": "E310000000001",
    "status": "processing",
    "legal_status": null,
    "authorized": false,
    "authorization_number": "4A8F2C1E9B",
    "environment": "testecf",
    "track_id": "4A8F2C1E9B",
    "totals": { "subtotal": 5000, "exempt_amount": 0, "itbis": 900, "total": 5900 },
    "currency": "DOP",
    "buyer_rnc": "101000001",
    "buyer_name": "Cliente de Prueba SRL",
    "error_message": null,
    "error_code": null,
    "sequence_consumed": true,
    "attempts": 0,
    "_links": {
      "self": "https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9",
      "xml": "https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9/xml",
      "pdf": "https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9/pdf",
      "qr": "https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9/qr",
      "notify": "https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9/notify"
    },
    "emitted_at": "2026-08-14T15:04:11+00:00",
    "accepted_at": null,
    "created_at": "2026-08-14T15:04:10+00:00",
    "updated_at": "2026-08-14T15:04:11+00:00"
  },
  "exec_time_ms": 1840,
  "message": "Document is still being processed."
}
Un 202 no es un error y no es un rechazo. Significa que el documento está en manos de la DGII y su veredicto llegara después. Guarda el id y sigue adelante.

La excepción: el canal RFCE responde de inmediato

Las facturas de consumo E32 con total menor a RD$250,000 viajan por el canal de resumen B2C (RFCE), que devuelve el veredicto en la misma respuesta HTTP. En ese caso el documento ya llega terminal y la respuesta es 200 OK con status en accepted o rejected.

Es estrictamente menor: una E32 de exactamente RD$250,000.00 va por el canal normal y es asíncrona. Ningun otro tipo usa RFCE.

Esto hace que una E32 de monto bajo sea la mejor primera prueba de una integración nueva: sabes en segundos si el certificado, el RNC y la secuencia están bien, sin esperar un ciclo de consulta.

Como te enteras del veredicto

Tres mecanismos, de menos a más trabajo de tu parte.

1. Webhooks (recomendado)

Suscríbete al evento document.emission.completed. FiscalFlow te llama en cuanto el documento llega a un estado terminal, venga de la respuesta inmediata de la DGII o de la consulta posterior. Ver Webhooks.

2. Consulta puntual

POST /v1/documents/{id}/refresh-status le pregunta a la DGII ahora mismo por el veredicto de un documento que sigue en processing. Es la acción detras del boton "actualizar estado" de una interfaz.

POST /api/v1/documents/{id}/refresh-status
curl -X POST \
  -H "X-API-Key: ffs_test_ejemplodeclavenoreal000000000000000000" \
  https://api.fiscalflowsupport.com/api/v1/documents/01jz8k4qm2n5p7r9t1v3w5x7y9/refresh-status
import requests

doc_id = "01jz8k4qm2n5p7r9t1v3w5x7y9"
r = requests.post(
    f"https://api.fiscalflowsupport.com/api/v1/documents/{doc_id}/refresh-status",
    headers={"X-API-Key": "ffs_test_ejemplodeclavenoreal000000000000000000"},
)
body = r.json()
print(body["data"]["status"], body["changed"])
200 Response
{
  "success": true,
  "data": { "status": "accepted", "accepted_at": "2026-08-14T15:06:02+00:00" },
  "changed": true,
  "message": "Status updated from the DGII: accepted."
}

changed te dice si esta consulta movió el estado. Si la DGII todavía no resuelve, devuelve changed: false y el documento sigue en processing: no es un error.

No conviertas esto en un bucle de sondeo agresivo. Consume tu límite de peticiones por minuto y no acelera a la DGII. Si vas a sondear, espacia las consultas y desiste con backoff.

3. Reconciliación automática

Aunque no hagas nada, FiscalFlow consulta por su cuenta cada cinco minutos el veredicto de todo documento que siga en processing y tenga track_id. Cuando resuelve, actualiza el documento y dispara el webhook. Es la red de seguridad: si tu webhook estaba caído y no sondeas, el estado se corrige solo y lo verás al consultar el documento.

Reintentos ante fallos de transporte

Si la DGII no responde, responde con un 5xx o hay un fallo de red, el envío se reintenta solo con espera creciente: 1 minuto, 5, 15, 1 hora y 6 horas. A los cinco intentos el documento queda en rejected con error_message empezando por Max attempts reached.. El campo attempts de la respuesta te dice en que intento va.

Los errores de contenido no se reintentan. Si el XML no cumple el esquema de la DGII, o falta un dato obligatorio como expiration_date, o una fecha es ilegible, el documento se rechaza de inmediato con error_code igual a XSD_VALIDATION_FAILED y no se vuelve a intentar: reintentarlo daría exactamente el mismo resultado. Corrige el dato y envía un documento nuevo.

Qué hacer con un documento rechazado

Depende de si la DGII consumió el número. El campo sequence_consumed lo dice.

  • sequence_consumed: false — el e-NCF sigue libre. Puedes reenviar el mismo documento con POST /v1/documents/{id}/retry.
  • sequence_consumed: true — la DGII ya quemó ese número. Reenviarlo solo produce "Este número de secuencia ya ha sido utilizado". Hay que descartar el documento y enviar uno nuevo con el siguiente e-NCF.

El flujo de corrección completo, con ejemplos, esta en Secuencias y fechas.

Resumen del flujo

POST /api/v1/documents
      |
      +-- E32 < RD$250,000  -->  200 OK, status accepted | rejected   (fin)
      |
      +-- todo lo demás     -->  202 Accepted, status processing
                                        |
                                        +-- webhook document.emission.completed
                                        +-- POST /documents/{id}/refresh-status
                                        +-- reconciliación cada 5 minutos
                                        |
                                        v
                                  accepted | rejected