API e-CF (DGII) — Guía para integradores#
Esta guía explica cómo emitir comprobantes fiscales electrónicos (e-CF) de la DGII a través de nuestro API: cómo autenticarse, qué ambiente usar, cómo armar el JSON, cómo se calculan los totales, qué pasa después del envío y cómo consultar el resultado.
Todo lo que aparece aquí sale del código del API y de los XSD oficiales de la DGII. Los
ejemplos (uno por tipo de comprobante) se validan automáticamente contra las mismas reglas
del endpoint y el XSD de la DGII: puedes enviarlos tal cual, cambiando solo el encf.
| Recurso | Para qué |
|---|---|
| Referencia del API | Todos los endpoints con request, respuestas y ejemplos. Permite probar las llamadas desde el navegador. |
| OpenAPI 3.1 | El contrato en formato estándar: impórtalo en Postman, Insomnia o un generador de clientes. |
| Esta guía | Cómo armar el JSON, cómo calcular los totales y qué hacer con cada respuesta. |
Inicio rápido#
-
Pide una API Key de
testecfal administrador de tu empresa (portal → API Keys). Cada key es de un RNC y un ambiente. -
Envía el ejemplo
E31-credito-fiscal.json, cambiando elencfpor uno de tu secuencia:curl -X POST https://ecf.paciolird.com/api/testecf/factura/enviar-directo \ -H "Authorization: Bearer $API_KEY" \ -H "Content-Type: application/json" \ -d @E31-credito-fiscal.json -
Guarda la respuesta 201:
codigo_seguridad,url_dgiiyqr_imageson lo que va en la factura impresa, yecf_queue_idsirve para consultar después. -
Consulta el resultado unos minutos después (el envío a la DGII es en segundo plano):
curl "https://ecf.paciolird.com/api/testecf/ecf/estado?encf=E310000000001" \ -H "Authorization: Bearer $API_KEY"Repite hasta que
data.dgii.estadosea final (Aceptado,Aceptado Condicional,Rechazado,resumido,duplicadoofallido). Si no esAceptado, el motivo está endata.dgii.mensaje.
Cuando funcione en testecf, pide la key de produccion y cambia testecf por produccion en la URL.
Contenido#
- Ambientes y autenticación
- Flujo completo
- Endpoint principal:
POST /{ambiente}/factura/enviar-directo - Campos del request
- Cálculo de totales, redondeo y cuadre
- Respuestas y errores
- Estados del comprobante
- Endpoints de consulta
- Tablas de códigos
- Resumen por tipo de comprobante
- Recetas: casos frecuentes
Documentación por tipo: E31 · E32 · E33 · E34 · E41 · E43 · E44 · E45 · E46 · E47
1. Ambientes y autenticación#
URL base#
https://ecf.paciolird.com/api/{ambiente}/...
{ambiente} |
Ambiente DGII | Para qué |
|---|---|---|
testecf |
TesteCF | Pruebas libres. Nada tiene validez fiscal. |
certecf |
CerteCF | Proceso de certificación como emisor electrónico ante la DGII. |
produccion |
Producción (eCF) | Comprobantes reales, con validez fiscal. |
Cada ambiente lleva su propia secuencia de e-NCF en la DGII: el mismo e-NCF puede usarse en
testecf y luego en produccion sin conflicto.
API Key#
Cada request lleva el header:
Authorization: Bearer <api_key>
- Una API Key por ambiente. La key está registrada para un RNC y un ambiente. Si llamas
/produccion/...con una key detestecf, la respuesta es401 {"message": "Token inválido o entorno no coincide"}. - El RNC emisor y la razón social se toman de la key. No hace falta enviarlos en el JSON (ver
rnc_emisor). - Las keys las crea el administrador de la empresa en el portal (sección API Keys).
Errores de autenticación (todos HTTP 401):
| Cuerpo | Causa |
|---|---|
{"message": "Token requerido"} |
Falta el header Authorization. |
{"message": "Token inválido o entorno no coincide"} |
Key inexistente, inactiva o de otro ambiente. |
{"message": "Emisor no encontrado"} |
La key apunta a un RNC sin emisor configurado. |
Los endpoints de comprobantes recibidos (/api/ecf/recibidos...) no llevan ambiente en la URL:
aceptan una key activa de cualquier ambiente y devuelven los comprobantes recibidos por su RNC.
2. Flujo completo#
Tu sistema API DGII
│ POST /{ambiente}/factura/enviar-directo │
│──────────────────────────────▶│ 1. valida el JSON │
│ │ 2. arma el XML del e-CF │
│ │ 3. lo firma con el certificado │
│ │ 4. lo valida contra el XSD de DGII │
│ │ 5. guarda y encola (estado pendiente) │
│◀──────────────────────────────│ 201: código de seguridad, URL, QR │
│ │ │
│ (imprimes / entregas el comprobante con el QR) │
│ │ worker (cada minuto) │
│ │──────────────────────────────────────▶│ envío
│ │◀──────────────────────────────────────│ trackId / estado
│ GET /{ambiente}/ecf/estado │ │
│──────────────────────────────▶│ estado local (Aceptado, Rechazado…) │
- Envías el comprobante a
POST /{ambiente}/factura/enviar-directo. - En la misma respuesta (201) recibes el código de seguridad, la URL de consulta del timbre DGII, la imagen QR (PNG en base64) y el XML firmado. Con eso ya puedes imprimir la representación impresa. Todo el paso 1–5 es atómico: si algo falla (validación, firma, XSD), no se guarda nada y puedes reintentar con el mismo e-NCF.
- El envío a la DGII es asíncrono. Un proceso corre cada minuto y envía los comprobantes en estado
pendiente. Las Facturas de Consumo (E32) menores de RD$250,000 no se envían una por una: van en un Resumen de Factura de Consumo (RFCE) y quedan en estadoresumido(ver E32). - Consultas el resultado con
GET /{ambiente}/ecf/estado?encf=...(o porecf_queue_id/track_id) hasta que llegue a un estado final (Aceptado,Aceptado Condicional,Rechazado,resumido,fallido). Ver estados.
3. Endpoint principal#
POST https://ecf.paciolird.com/api/{ambiente}/factura/enviar-directo
Authorization: Bearer <api_key>
Content-Type: application/json
Cuerpo: un objeto con dos claves.
{
"factura": { "...": "encabezado: tipo, e-NCF, fechas, emisor, comprador, totales" },
"detalle": [ { "...": "una entrada por línea" } ]
}
Ejemplo mínimo completo (Factura de Crédito Fiscal): ejemplos/E31-credito-fiscal.json.
Hay un ejemplo validado por cada tipo en ejemplos/.
Solo se aceptan los campos listados en la sección 4. Cualquier otra clave
(por ejemplo nombre_comercial, telefono_emisor, descripcion_item, identificador_extranjero) se
descarta en silencio y no llega al XML.
4. Campos del request#
Columna "XML": el elemento del e-CF que se genera. Las restricciones de largo/formato son las del XSD
de la DGII; si no se cumplen, la respuesta es 422 Comprobante inválido según el esquema de DGII.
Fechas: envíalas como YYYY-MM-DD. El API las convierte al formato DGII (dd-mm-aaaa).
Tipos JSON. Estos campos se validan como texto y deben ir como string JSON (entre comillas); un
número ("tipo_ecf": 31) se rechaza con 422 Error de validación ("must be a string"):
tipo_ecf, encf, tipo_ingresos, termino_pago, rnc_emisor, razon_social_emisor,
direccion_emisor, municipio_emisor, provincia_emisor, correo_emisor, website_emisor,
codigo_vendedor, numero_factura_interna, numero_pedido_interno, zona_venta, rnc_comprador,
razon_social_comprador, direccion_comprador, municipio_comprador, provincia_comprador,
correo_comprador, numero_orden_compra, codigo_interno_comprador, ncf_modificado,
codigo_modificacion, razon_modificacion y, en el detalle, nombre_item y unidad_medida.
tipo_pago y formas_pago[].forma_pago aceptan "1" o 1 (se recomienda string). Los montos y
los indicadores (indicador_monto_gravado, indicador_facturacion, indicador_bien_servicio,
indicador_nota_credito, indicador_agente_retencion_percepcion, numero_linea) van como número.
Identificación del comprobante#
| Campo | Req. | XML | Notas |
|---|---|---|---|
tipo_ecf |
Sí | TipoeCF |
"31", "32", "33", "34", "41", "43", "44", "45", "46", "47". |
encf |
Sí | eNCF |
13 caracteres: E + tipo (2) + secuencia (10). Ej. E310000000001. Debe coincidir con tipo_ecf (un E32 numerado E31… se rechaza con 422). No se rellena: uno mal formado se rechaza. |
fecha_emision |
Sí | FechaEmision |
|
fecha_vencimiento |
Según tipo | FechaVencimientoSecuencia |
Fecha de vencimiento de la secuencia de e-NCF autorizada por la DGII, no el vencimiento de la factura. Obligatoria por XSD en 31, 33, 41, 43, 44, 45, 46 y 47. En 32 y 34 se ignora. |
tipo_ingresos |
No | TipoIngresos |
"01"–"06" (string). Si no se envía, "01". No se emite en 41, 43 y 47. |
tipo_pago |
Según tipo | TipoPago |
"1" contado, "2" crédito, "3" gratuito. Obligatorio por XSD en 31, 32, 33, 34, 44, 45 y 46; opcional en 41, 43 y 47. Otro valor → 422. |
formas_pago |
No | TablaFormasPago |
Lista de hasta 7 { "forma_pago": "1"-"8", "monto": 0.00 } (ver tabla). Las filas con monto 0 se descartan. Si no se envía, se deduce de tipo_pago: contado → una fila 1 (efectivo) por monto_total; crédito → una fila 4 (venta a crédito); gratuito → ninguna. No se emite en 34 ni 43, ni si falta tipo_pago. Debe sumar monto_total. |
fecha_limite_pago |
No | FechaLimitePago |
No se emite en 43. |
termino_pago |
No | TerminoPago |
Máx. 15 caracteres (letras, dígitos, espacios). Ej. "30 dias". No se emite en 34 ni 43. |
indicador_monto_gravado |
No | IndicadorMontoGravado |
0 (precios sin ITBIS, recomendado) o 1 (precios con ITBIS incluido). Si no se envía, 0. Solo se emite en 31, 32, 33, 34, 41 y 45. Ver 5.4. |
Emisor#
| Campo | Req. | XML | Notas |
|---|---|---|---|
rnc_emisor |
No | RNCEmisor |
Se toma de la API Key. No lo envíes: si lo envías se usa tal cual en el XML, y si no coincide con el de la key el comprobante sale a nombre de otro RNC. |
razon_social_emisor |
No | RazonSocialEmisor |
Si no se envía, la registrada para la key. |
direccion_emisor |
Envíalo siempre | DireccionEmisor |
Obligatorio por XSD; máx. 100. Si falta, el XML sale con el texto literal DIRECCION. |
municipio_emisor, provincia_emisor |
No | Municipio, Provincia |
Código DGII de 6 dígitos (ej. 010100 Santo Domingo de Guzmán, 010000 Distrito Nacional). No se emiten en 34. |
correo_emisor |
No | CorreoEmisor |
No se emite en 34. |
website_emisor |
No | WebSite |
Máx. 50. No se emite en 34. |
numero_factura_interna |
No | NumeroFacturaInterna |
Tu número interno. Máx. 20. |
numero_pedido_interno |
No | NumeroPedidoInterno |
Solo dígitos, máx. 20. |
zona_venta |
No | ZonaVenta |
Máx. 20. No se emite en 41, 43 y 47. |
codigo_vendedor |
No | — | Se acepta pero hoy no se incluye en el XML. |
El NombreComercial del XML se toma de la empresa registrada (o su razón social); en 34 no se emite.
Comprador#
| Campo | XML | Notas |
|---|---|---|
rnc_comprador |
RNCComprador |
RNC (9 dígitos) o cédula (11 dígitos), sin guiones. |
razon_social_comprador |
RazonSocialComprador |
Máx. 150. |
correo_comprador |
CorreoComprador |
|
direccion_comprador |
DireccionComprador |
Máx. 100. |
municipio_comprador, provincia_comprador |
MunicipioComprador, ProvinciaComprador |
Códigos DGII de 6 dígitos. |
fecha_orden_compra, numero_orden_compra |
FechaOrdenCompra, NumeroOrdenCompra |
No se emiten en 41. numero_orden_compra máx. 20. |
codigo_interno_comprador |
CodigoInternoComprador |
Máx. 20. |
La sección comprador se emite si viene rnc_comprador o razon_social_comprador. En 43 nunca se
emite. En 32 se emite vacía si no hay datos (consumidor final). Para 47 ver E47.
Qué es obligatorio en cada tipo está en la página del tipo.
Totales#
| Campo | XML | Notas |
|---|---|---|
monto_gravado_total |
MontoGravadoTotal |
I1 + I2 + I3. |
monto_gravado_i1 |
MontoGravadoI1 |
Base gravada a la tasa 1 (18 %). |
monto_gravado_i2 |
MontoGravadoI2 |
Base gravada a la tasa 2 (16 %). |
monto_gravado_i3 |
MontoGravadoI3 |
Base gravada a la tasa 3 (0 %). |
monto_exento |
MontoExento |
Suma de líneas exentas. |
itbis1, itbis2, itbis3 |
ITBIS1/2/3 |
Tasa en % (entero). Por defecto 18, 16 y 0. Solo se emiten si la base correspondiente es mayor que 0. |
total_itbis |
TotalITBIS |
TotalITBIS1 + 2 + 3. |
total_itbis1, total_itbis2, total_itbis3 |
TotalITBIS1/2/3 |
|
monto_total |
MontoTotal |
Requerido. |
valor_pagar |
ValorPagar |
Requerido. Si es mayor que 0, se envía a la DGII igual a monto_total (si mandas otro valor se corrige). Si es 0 (p. ej. comprobante gratuito), ValorPagar no se emite. |
total_itbis_retenido, total_isr_retencion |
TotalITBISRetenido, TotalISRRetencion |
31, 33, 34, 41 (y 47 solo ISR). Si no se envían, se suman de monto_itbis_retenido / monto_isr_retenido del detalle, también en 31, 33 y 34 aunque ahí no se emita el bloque Retencion por línea. |
total_itbis_percepcion, total_isr_percepcion |
TotalITBISPercepcion, TotalISRPercepcion |
31, 33, 34 y 41. |
Montos en 0 no se emiten. En 43, 44, 46 y 47 el API no emite montos gravados ni ITBIS (ver cada tipo).
Se aceptan pero no se usan: monto_propina_legal, total_descuento, monto_no_facturable, subtotal_sin_impuesto.
Referencia (solo 33 y 34)#
| Campo | XML | Notas |
|---|---|---|
ncf_modificado |
NCFModificado |
e-NCF (o NCF) del comprobante que se modifica. Obligatorio. |
fecha_ncf_modificado |
FechaNCFModificado |
Fecha de emisión de ese comprobante. Obligatorio. |
codigo_modificacion |
CodigoModificacion |
"1"–"5" (string) (ver tabla). Obligatorio. |
razon_modificacion |
RazonModificacion |
Opcional, máx. 90. |
indicador_nota_credito |
IndicadorNotaCredito |
Solo 34. 0/1. Si no se envía se calcula (ver E34). |
Si falta ncf_modificado, fecha_ncf_modificado o codigo_modificacion en un 33/34, la respuesta es
500 con el mensaje El campo ... es obligatorio para Notas de Crédito (tipo 34) (también en 33).
Detalle (cada línea)#
| Campo | Req. | XML | Notas |
|---|---|---|---|
numero_linea |
Sí | NumeroLinea |
1, 2, 3… |
nombre_item |
Sí | NombreItem |
Máx. 80. |
cantidad |
Sí | CantidadItem |
Mayor que 0. Se envía con 2 decimales. |
precio_unitario |
Sí | PrecioUnitarioItem |
Hasta 4 decimales. |
monto_item |
Sí | MontoItem |
Se recalcula: cantidad × precio_unitario − descuento, redondeado a 2 decimales. Ver 5.1. |
unidad_medida |
No | UnidadMedida |
Código DGII como string; por defecto "43" (unidad). Ver tabla. |
indicador_bien_servicio |
No | IndicadorBienoServicio |
1 bien, 2 servicio. Por defecto 2. |
indicador_facturacion |
No | IndicadorFacturacion |
1 18 %, 2 16 %, 3 0 %, 4 exento, 0 no facturable. Por defecto 1. |
descuento |
No | DescuentoMonto + TablaSubDescuento |
Monto en pesos (no porcentaje) descontado de la línea. También se acepta como monto_descuento o descuento_monto. En 43 se ignora; en 47 no está permitido (422). |
indicador_agente_retencion_percepcion |
Solo 41/47 | Retencion/IndicadorAgenteRetencionoPercepcion |
1 retención, 2 percepción. Por defecto 1. |
monto_itbis_retenido |
41 (ver nota) | Retencion/MontoITBISRetenido |
En 41 se emite siempre (0.00 si no se envía). En 31, 33 y 34 no va por línea pero se suma a TotalITBISRetenido si no envías el total. |
monto_isr_retenido |
41; obligatorio en 47 | Retencion/MontoISRRetenido |
En 41 y 47 cada línea lleva siempre el bloque Retencion (lo exige el XSD). En 31, 33 y 34 no se emite el bloque por línea, pero monto_itbis_retenido / monto_isr_retenido
del detalle se suman para TotalITBISRetenido / TotalISRRetencion si no envías esos totales.
5. Cálculo de totales, redondeo y cuadre#
El API no calcula los totales del encabezado: usa los que envías. La DGII sí los verifica contra el
detalle; si no cuadran, el comprobante vuelve como Aceptado Condicional o Rechazado. Calcula así:
5.1 MontoItem#
monto_item = round(cantidad × precio_unitario − descuento, 2)
El API siempre envía a la DGII el MontoItem calculado con esa fórmula (la DGII valida la misma
igualdad), aunque el monto_item que mandes sea otro. Excepción: si cantidad o precio_unitario son 0 se
respeta el monto_item enviado. En E43 el descuento no se resta (E43 no admite descuentos).
CantidadItem viaja con 2 decimales: si usas cantidades con más decimales, redondéalas tú antes de
calcular, para que cantidad × precio cuadre con lo que ve la DGII.
5.2 Totales por tasa (indicador_monto_gravado = 0)#
monto_gravado_i1 = Σ monto_item de las líneas con indicador_facturacion = 1
monto_gravado_i2 = Σ monto_item con indicador_facturacion = 2
monto_gravado_i3 = Σ monto_item con indicador_facturacion = 3
monto_exento = Σ monto_item con indicador_facturacion = 4
monto_gravado_total = i1 + i2 + i3
total_itbis1 = round(monto_gravado_i1 × 0.18, 2)
total_itbis2 = round(monto_gravado_i2 × 0.16, 2)
total_itbis3 = 0
total_itbis = total_itbis1 + total_itbis2 + total_itbis3
monto_total = monto_gravado_total + monto_exento + total_itbis
valor_pagar = monto_total
Ejemplo (E31 con descuento y exento):
| Línea | Cálculo | MontoItem | Indicador |
|---|---|---|---|
| Laptop | 2 × 35,000.00 − 3,500.00 | 66,500.00 | 1 (18 %) |
| Yogurt | 10 × 85.00 | 850.00 | 2 (16 %) |
| Libro | 3 × 1,200.00 | 3,600.00 | 4 (exento) |
MontoGravadoI1 66,500.00 · MontoGravadoI2 850.00 · MontoGravadoTotal 67,350.00 · MontoExento 3,600.00
· TotalITBIS1 11,970.00 · TotalITBIS2 136.00 · TotalITBIS 12,106.00 · MontoTotal 83,056.00.
5.3 Retenciones no se restan#
valor_pagar no es el neto después de retenciones. La DGII valida
ValorPagar = MontoTotal + SaldoAnterior − MontoAvancePago; el API no maneja saldo anterior ni avances,
así que ValorPagar = MontoTotal siempre que valor_pagar sea mayor que 0 (si mandas el neto, el API lo corrige). Las retenciones viajan
aparte, en cada línea (monto_itbis_retenido, monto_isr_retenido) y en el encabezado
(total_itbis_retenido, total_isr_retencion). Ver E41 y E47.
5.4 indicador_monto_gravado#
| Valor | Significado | Se cumple |
|---|---|---|
0 |
Los monto_item no incluyen ITBIS |
monto_total = monto_gravado_total + total_itbis + monto_exento |
1 |
Los monto_item ya incluyen el ITBIS |
monto_total = monto_gravado_total + monto_exento |
Usa 0. Todos los ejemplos de esta guía usan 0. El API revisa los totales y, si una de las dos
igualdades cuadra y la otra no, envía el indicador que cuadra aunque hayas mandado el otro.
5.5 Redondeo#
- Montos del encabezado y
MontoItem: 2 decimales.PrecioUnitarioItem: 4 decimales. - Calcula el ITBIS sobre la base total de cada tasa (no línea por línea) para que
TotalITBIS1 = MontoGravadoI1 × 18 %cuadre exactamente. formas_pagodebe sumar exactamentemonto_total.
6. Respuestas y errores#
201 — Comprobante emitido#
{
"mensaje": "Factura procesada exitosamente",
"factura_id": 1234,
"encf": "E310000000001",
"codigo_seguridad": "a1B2c3",
"url_dgii": "https://ecf.dgii.gov.do/TesteCF/consultatimbre?rncemisor=101000001&encf=E310000000001&FechaEmision=01-10-2026&montototal=19706.00&FechaFirma=01-10-2026+10%3A15%3A30&codigoseguridad=a1B2c3&rnccomprador=131000001",
"qr_image": "data:image/png;base64,iVBORw0KGgo...",
"xml_firmado": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ECF>...</ECF>",
"estado": "pendiente",
"ecf_queue_id": 5678
}
(Valores ilustrativos.)
| Campo | Uso |
|---|---|
codigo_seguridad |
6 caracteres; va impreso en la representación impresa. |
url_dgii |
URL de consulta del timbre en la DGII; es el contenido del QR. Para E32 < RD$250,000 usa fc.dgii.gov.do/.../consultatimbreFC; para todo lo demás ecf.dgii.gov.do/.../consultatimbre. |
qr_image |
QR en PNG base64 (data URI), listo para <img src="...">. |
xml_firmado |
XML firmado que se enviará a la DGII. Guárdalo. |
estado |
Siempre pendiente en este punto: aún no se ha enviado a la DGII. |
factura_id |
Para GET /{ambiente}/factura/{factura_id}/estado. |
ecf_queue_id |
Para GET /{ambiente}/ecf/estado?ecf_queue_id=..., /{ambiente}/ecf-queue/{id} y el PDF. |
Prefijo de url_dgii según ambiente:
| Ambiente | E32 < RD$250,000 | Resto |
|---|---|---|
testecf |
https://fc.dgii.gov.do/TesteCF/consultatimbreFC |
https://ecf.dgii.gov.do/TesteCF/consultatimbre |
certecf |
https://fc.dgii.gov.do/CerteCF/consultatimbreFC |
https://ecf.dgii.gov.do/CerteCF/consultatimbre |
produccion |
https://fc.dgii.gov.do/ecf/consultatimbreFC |
https://ecf.dgii.gov.do/ecf/consultatimbre |
Parámetros: rncemisor, encf, FechaEmision (dd-mm-aaaa), montototal, FechaFirma,
codigoseguridad y, si hay comprador con RNC, rnccomprador.
Errores de enviar-directo#
| HTTP | error |
Causa | Qué hacer |
|---|---|---|---|
| 401 | — (message) |
Ver autenticación. | Revisa la key y el ambiente. |
| 409 | eNCF duplicado |
Ya existe un comprobante con ese e-NCF para tu RNC en ese ambiente. | Ver abajo. |
| 422 | Error de validación |
Falta un campo requerido o tiene tipo/valor inválido. mensajes trae el detalle por campo. |
Corrige y reenvía con el mismo e-NCF. |
| 422 | eNCF no corresponde al tipo |
El prefijo del e-NCF no coincide con tipo_ecf. |
Usa un e-NCF de la secuencia del tipo. |
| 422 | Comprobante inválido según el esquema de DGII |
El XML no cumple el XSD de la DGII (campo obligatorio faltante para el tipo, largo excedido, código fuera de catálogo, e-NCF mal formado…). mensaje trae el error del XSD. |
Corrige y reenvía con el mismo e-NCF. |
| 500 | Error al procesar factura |
Error interno, certificado del emisor con problemas, o reglas del constructor (ej. faltan datos de referencia en 33/34, falta monto_isr_retenido en 47). mensaje trae la causa. |
Corrige si el mensaje lo indica; si no, reintenta más tarde. |
En cualquier 422 o 500 no se guarda nada: puedes reenviar con el mismo e-NCF.
Ejemplo de 422 por validación:
{
"error": "Error de validación",
"mensajes": {
"factura.encf": ["The factura.encf field is required."]
}
}
409 — e-NCF duplicado y cómo recuperar la respuesta#
El 409 protege contra la doble emisión. Si el comprobante existente ya tiene código de seguridad, el 409 trae sus datos para que puedas recuperarlos (por ejemplo, si tu request anterior hizo timeout y no recibiste el 201):
{
"error": "eNCF duplicado",
"mensaje": "Ya existe una factura con eNCF E310000000001 para este emisor",
"factura_id": 1234,
"encf": "E310000000001",
"codigo_seguridad": "a1B2c3",
"url_dgii": "https://ecf.dgii.gov.do/TesteCF/consultatimbre?...",
"qr_image": "data:image/png;base64,...",
"estado": "Aceptado",
"track_id": "a2b9c1d4-...",
"ecf_queue_id": 5678
}
Regla práctica ante un timeout: reenvía exactamente el mismo request. Si el primero se procesó,
recibes 409 con los datos; si no, se procesa ahora. Antes de usar los datos del 409, verifica que
corresponden a la misma venta (el 409 también aparece si reutilizaste por error un e-NCF de otra venta).
El 409 no incluye xml_firmado; puedes obtenerlo con GET /{ambiente}/ecf-queue/{ecf_queue_id}/xml.
7. Estados del comprobante#
Es el valor de data.dgii.estado en GET /{ambiente}/ecf/estado (y de estado en las demás consultas).
| Estado | Significado | ¿Final? | Qué hacer |
|---|---|---|---|
pendiente |
En cola, aún sin respuesta de la DGII. También mientras se reintenta una falla transitoria o mientras una nota de crédito espera a que su original sea aceptado (ver mensaje). |
No | Esperar. |
enviado |
La DGII recibió el comprobante (hay track_id) y aún no se tiene el resultado. |
No | Esperar; una tarea horaria consulta la DGII y actualiza. |
En Proceso / Recibido |
La DGII lo está evaluando. | No | Esperar. (Para habilitar una nota de crédito, un original Recibido ya cuenta como válido: ver E34.) |
Aceptado |
La DGII lo aceptó. | Sí | Nada. |
Aceptado Condicional |
Aceptado con observaciones (por ejemplo, totales que no cuadran). Tiene validez fiscal. | Sí | Revisa mensaje y corrige tu cálculo para los próximos. |
Rechazado |
La DGII lo rechazó; no tiene validez fiscal. | Sí | Revisa mensaje (cómo leer el motivo), corrige y emite un comprobante nuevo. Reenviar el mismo e-NCF por enviar-directo devuelve 409. |
resumido |
E32 menor de RD$250,000: incluido en un Resumen de Factura de Consumo (RFCE) que la DGII recibió. | Sí | Nada. Ver E32. |
duplicado |
La DGII respondió que la secuencia (e-NCF) ya fue utilizada. | Sí | Verifica si ese e-NCF ya se usó en otra venta. |
fallido |
Error definitivo: la DGII rechazó la recepción (HTTP 400/422), se agotaron los reintentos, el RFCE fue rechazado, o una nota de crédito cuyo original no existe en el sistema o fue rechazado. | Sí | Lee mensaje, corrige y emite de nuevo. |
Reintentos automáticos (valores por defecto): una falla de conexión con la DGII se reintenta hasta 12
veces; cualquier otra falla, hasta 3. Entre intentos se espera intentos × 5 minutos. Si un intento
anterior llegó a la DGII aunque la respuesta se perdiera, el sistema lo detecta y no reenvía.
Cuando la DGII recibe el comprobante (enviado, Aceptado, Aceptado Condicional, resumido…), si
tiene rnc_comprador el sistema intenta entregarlo al comprador (si este es receptor electrónico). El resultado aparece en
data.comprador de ecf/estado.
8. Endpoints de consulta#
Todos con Authorization: Bearer <api_key> y el prefijo de ambiente, salvo que se indique. Solo
devuelven comprobantes del RNC de la key.
| Método y ruta | Para qué |
|---|---|
GET /{ambiente}/ecf/estado?encf=E31… ?ecf_queue_id=… ?track_id=… |
Consulta recomendada. Estado local (no llama a la DGII). Por encf toma el registro más reciente del ambiente. 404 si no existe; 422 si no envías ninguno de los tres. |
GET /{ambiente}/factura/{factura_id}/estado |
Estado por factura_id (el del 201). Incluye codigo_seguridad, url_dgii, track_id, respuesta_dgii, intentos. |
GET /{ambiente}/ecf/consulta-trackid?trackId=… |
Consulta en vivo a la DGII (ConsultaResultado). Devuelve la respuesta de la DGII tal cual. 503 si la DGII no responde. |
GET /{ambiente}/ecf-queue |
Listado paginado. Filtros: estado, tipo_ecf, encf (parcial), fecha_desde, fecha_hasta, per_page. |
GET /{ambiente}/ecf-queue/{ecf_queue_id} |
Registro completo. |
GET /{ambiente}/ecf-queue/{ecf_queue_id}/xml |
XML firmado (JSON con campo xml). |
GET /{ambiente}/ecf-queue/{ecf_queue_id}/descargar-xml |
XML firmado como archivo. |
GET /{ambiente}/ecf-queue/estadisticas |
Conteos por estado y tipo. |
GET /api/pdf/{ecf_queue_id} |
Representación impresa en PDF. Sin prefijo de ambiente; lleva la API Key y solo devuelve comprobantes de tu RNC (sirve una key de cualquier ambiente). |
GET /api/ecf/recibidos |
Comprobantes que otros emisores te enviaron. Filtros: aprobacion_estado, tipo_ecf, encf, rnc_emisor, desde, hasta, per_page (máx. 100). Sin prefijo de ambiente. |
GET /api/ecf/recibidos/{id} · GET /api/ecf/recibidos/{id}/xml |
Detalle (con el XML interpretado) y XML crudo de un recibido. |
Errores de ecf/estado:
{ "error": true, "mensaje": "Comprobante no encontrado." }
(404). Sin track_id, encf ni ecf_queue_id: 422 con
{ "error": true, "mensaje": "Debe proveer al menos uno de: track_id, encf, ecf_queue_id." }.
Los endpoints /api/ecf/recibidos* y /api/pdf/{id} usan otra validación de key: una key inexistente o inactiva responde
401 {"message": "Token inválido"} (sin header: 401 {"message": "Token requerido"}).
Respuesta de ecf/estado:
{
"status": true,
"data": {
"ecf_queue_id": 5678,
"track_id": "a2b9c1d4-...",
"encf": "E310000000001",
"tipo_ecf": "31",
"fecha_emision": "2026-10-01",
"monto_total": "19706.00",
"dgii": {
"estado": "Aceptado",
"codigo_respuesta": "1",
"mensaje": null,
"fecha_procesado": "2026-10-01T10:16:02-04:00",
"intentos": 0
},
"comprador": {
"rnc": "131000001",
"estado": "entregado",
"url": "...",
"fecha": "...",
"error": null,
"arecf": "..."
}
}
}
(Valores ilustrativos.) dgii.mensaje trae los mensajes de la DGII (lista {codigo, valor}) o el texto
del sistema que explica un fallido o una espera. comprador es null si no hay rnc_comprador.
Por qué la DGII rechazó un comprobante#
enviar-directo no puede decirte si la DGII va a rechazar el comprobante: responde antes de enviarlo
(estado: pendiente). El motivo aparece después, cuando consultas el estado:
GET /{ambiente}/ecf/estado?encf=E310000000001
{
"status": true,
"data": {
"ecf_queue_id": 5679,
"track_id": "80ef4915-...",
"encf": "E310000000001",
"dgii": {
"estado": "Rechazado",
"codigo_respuesta": "2",
"mensaje": [
{ "codigo": 145, "valor": "texto del motivo, tal como lo devuelve la DGII" }
],
"fecha_procesado": "2026-10-25T12:20:11-04:00",
"intentos": 0
}
}
}
(Valores ilustrativos.)
dgii.mensajees una lista{codigo, valor}con lo que respondió la DGII. Puede traer varios motivos; muéstralos todos. Si la DGII solo devolvió el mensaje vacío ({"codigo": 0, "valor": ""}), lleganull.- Es un texto en vez de lista cuando el mensaje lo pone el sistema: un
fallido(por ejemplo, la DGII no aceptó la recepción) o el resultado de una verificación posterior que cambió el estado. codigo_respuestaes el código de la DGII:1Aceptado,2Rechazado,3En Proceso,4Aceptado Condicional.- Lo mismo aplica a
Aceptado Condicional:mensajeexplica las observaciones.
GET /{ambiente}/factura/{factura_id}/estado también trae el motivo, en mensaje y en respuesta_dgii,
pero ahí mensaje llega como texto JSON sin interpretar ("[{\"codigo\":145,...}]"). Para leer el
motivo usa ecf/estado.
Un rechazado no se corrige reenviando el mismo e-NCF: enviar-directo responde 409 porque el
comprobante ya existe en el sistema. Corrige lo que indica el mensaje y emite un comprobante nuevo.
9. Tablas de códigos#
Fuente: enumeraciones de los XSD oficiales de la DGII.
TipoeCF#
| Código | Comprobante |
|---|---|
| 31 | Factura de Crédito Fiscal Electrónica |
| 32 | Factura de Consumo Electrónica |
| 33 | Nota de Débito Electrónica |
| 34 | Nota de Crédito Electrónica |
| 41 | Comprobante Electrónico de Compras |
| 43 | Comprobante Electrónico para Gastos Menores |
| 44 | Comprobante Electrónico para Regímenes Especiales |
| 45 | Comprobante Electrónico Gubernamental |
| 46 | Comprobante Electrónico para Exportaciones |
| 47 | Comprobante Electrónico para Pagos al Exterior |
TipoIngresos#
| Código | Descripción |
|---|---|
| 01 | Ingresos por operaciones (no financieros) |
| 02 | Ingresos financieros |
| 03 | Ingresos extraordinarios |
| 04 | Ingresos por arrendamientos |
| 05 | Ingresos por venta de activo depreciable |
| 06 | Otros ingresos |
TipoPago#
| Código | Descripción |
|---|---|
| 1 | Contado |
| 2 | Crédito |
| 3 | Gratuito |
FormaPago#
| Código | Descripción |
|---|---|
| 1 | Efectivo |
| 2 | Cheque / transferencia / depósito |
| 3 | Tarjeta de débito / crédito |
| 4 | Venta a crédito |
| 5 | Bonos o certificados de regalo |
| 6 | Permuta |
| 7 | Nota de crédito |
| 8 | Otras formas de pago |
tipo_pago (contado/crédito/gratuito) y forma_pago (cómo se pagó) son catálogos distintos: no pongas
un código de forma de pago en tipo_pago.
IndicadorFacturacion#
| Código | Descripción | Va en |
|---|---|---|
| 0 | No facturable | — |
| 1 | ITBIS tasa 1 (18 %) | monto_gravado_i1 / total_itbis1 |
| 2 | ITBIS tasa 2 (16 %) | monto_gravado_i2 / total_itbis2 |
| 3 | ITBIS tasa 3 (0 %) | monto_gravado_i3 |
| 4 | Exento | monto_exento |
IndicadorBienoServicio#
| Código | Descripción |
|---|---|
| 1 | Bien |
| 2 | Servicio |
IndicadorMontoGravado#
| Código | Descripción |
|---|---|
| 0 | Los montos de las líneas no tienen ITBIS incluido |
| 1 | Los montos de las líneas tienen ITBIS incluido |
CodigoModificacion#
Para notas de débito (33) y crédito (34).
| Código | Descripción |
|---|---|
"1" |
Anula el NCF modificado |
"2" |
Corrige texto del comprobante fiscal modificado |
"3" |
Corrige montos del NCF modificado |
"4" |
Reemplazo de NCF emitido en contingencia |
"5" |
Referencia a Factura de Consumo Electrónica |
IndicadorNotaCredito (34)#
| Código | Descripción |
|---|---|
| 0 | Nota de crédito emitida hasta 30 días calendario después del comprobante modificado |
| 1 | Emitida más de 30 días después |
IndicadorAgenteRetencionoPercepcion (41 y 47)#
| Código | Descripción |
|---|---|
| 1 | Retención |
| 2 | Percepción |
UnidadMedida#
Catálogo completo en el XSD (UnidadMedidaType, códigos 1–62). Los más usados:
| Código | Unidad | Código | Unidad |
|---|---|---|---|
| 43 | Unidad (por defecto) | 19 | Hora |
| 6 | Caja | 12 | Día |
| 10 | Conjunto | 21 | Kilogramo |
| 13 | Docena | 23 | Libra |
| 15 | Galones | 24 | Litro |
| 17 | Gramo | 26 | Metro |
| 31 | Paquete | 27 | Metro cuadrado |
| 34 | Pieza | 44 | Elemento |
| 45 | Millar | 2 | Bolsa |
10. Resumen por tipo de comprobante#
| Tipo | Cuándo usarlo | Campos clave |
|---|---|---|
| 31 Crédito Fiscal | Venta a un contribuyente que usará el ITBIS como crédito fiscal (empresa o persona con RNC). | rnc_comprador y razon_social_comprador obligatorios; fecha_vencimiento; totales con ITBIS. |
| 32 Consumo | Venta a consumidor final. | Comprador opcional por debajo de RD$250,000; desde RD$250,000 la DGII exige RNC o cédula del comprador (el API aún no lo valida); sin fecha_vencimiento; < RD$250,000 va en resumen RFCE (resumido). |
| 33 Nota de Débito | Aumentar el monto de un comprobante ya emitido (cargos, intereses, diferencias). | ncf_modificado, fecha_ncf_modificado, codigo_modificacion. |
| 34 Nota de Crédito | Anular o disminuir un comprobante ya emitido (devoluciones, descuentos, errores). | Referencia obligatoria; tipo_pago obligatorio; sin fecha_vencimiento; espera a que el original esté aceptado. |
| 41 Compras | Tu empresa compra a un proveedor que no emite comprobantes (persona física/informal). | Proveedor en rnc_comprador/razon_social_comprador; retención por línea; sin tipo_ingresos. |
| 43 Gastos Menores | Gastos menores de la empresa sin comprobante (caja chica: taxis, peajes, parqueos). | Sin comprador, sin ITBIS, sin descuentos: todo exento. |
| 44 Regímenes Especiales | Venta a empresas en regímenes especiales exentos (zonas francas, etc.). | Sin ITBIS: todo monto_exento; comprador con razón social. |
| 45 Gubernamental | Venta a entidades del gobierno. | Como el 31: comprador con RNC y totales con ITBIS. |
| 46 Exportaciones | Venta de bienes/servicios al exterior. | Líneas al 0 % (indicador_facturacion 3), comprador con razón social. No envíes monto_exento (> 0 → 422 por XSD). Hoy no se emiten MontoGravado*, ITBIS* ni TotalITBIS*. ⚠ Limitaciones conocidas; aceptación DGII no verificada: coordina con soporte antes de producción. |
| 47 Pagos al Exterior | Pago a un proveedor en el exterior (no residente). | ISR retenido por línea obligatorio; todo exento; hoy sin datos del comprador. ⚠ Limitaciones conocidas; aceptación DGII no verificada: coordina con soporte antes de producción. |
Advertencia E46 y E47: la salida actual tiene limitaciones conocidas: el E46 no emite ningún
MontoGravado*,ITBIS*niTotalITBIS*(sus totales solo llevanMontoTotalyValorPagar) y el E47 sale sin datos del comprador. No se ha verificado que la DGII los acepte; coordina con soporte antes de usarlos enproduccion.
Qué emite el API por tipo (según el XSD y el constructor del XML):
| Elemento | 31 | 32 | 33 | 34 | 41 | 43 | 44 | 45 | 46 | 47 |
|---|---|---|---|---|---|---|---|---|---|---|
FechaVencimientoSecuencia (obligatoria) |
Sí | — | Sí | — | Sí | Sí | Sí | Sí | Sí | Sí |
IndicadorMontoGravado |
Sí | Sí | Sí | Sí | Sí | — | — | Sí | — | — |
TipoIngresos |
Sí | Sí | Sí | Sí | — | — | Sí | Sí | Sí | — |
TipoPago obligatorio |
Sí | Sí | Sí | Sí | opc. | opc. | Sí | Sí | Sí | opc. |
TablaFormasPago |
Sí | Sí | Sí | — | Sí | — | Sí | Sí | Sí | Sí |
| Comprador | oblig. | opc. | opc. | opc. | oblig. | — | oblig. | oblig. | oblig. | — |
| Montos gravados / ITBIS | Sí | Sí | Sí | Sí | Sí | — | — | Sí | — | — |
MontoExento |
Sí | Sí | Sí | Sí | Sí | Sí | Sí | Sí | — | Sí |
| Retenciones | opc. | — | opc. | opc. | por línea | — | — | — | — | ISR por línea |
| Descuento por línea | Sí | Sí | Sí | Sí | Sí | — | Sí | Sí | Sí | — |
Referencia (ncf_modificado…) |
— | — | oblig. | oblig. | — | — | — | — | — | — |
11. Recetas: casos frecuentes#
Mi request a enviar-directo dio timeout#
No sabes si se procesó. Reenvía exactamente el mismo request: si el primero se procesó recibes 409
con el código de seguridad, la URL y el QR; si no, se procesa ahora. Ver 409.
La DGII rechazó el comprobante#
Consulta GET /{ambiente}/ecf/estado?encf=... y lee data.dgii.mensaje: es la lista {codigo, valor}
que devolvió la DGII (detalle). Corrige y emite un comprobante
nuevo; reenviar el mismo e-NCF devuelve 409.
Quedó Aceptado Condicional#
Tiene validez fiscal, no hay que reemitir. data.dgii.mensaje dice qué observó la DGII (por ejemplo,
totales que no cuadran con el detalle). Revisa tu cálculo con la sección 5
para los próximos comprobantes.
Quedó fallido#
El comprobante no llegó a la DGII o la DGII no aceptó recibirlo. data.dgii.mensaje trae el texto del
error. Corrige y emite de nuevo.
Sigue en pendiente o enviado después de varios minutos#
Es normal si la DGII está lenta: el sistema reintenta solo (ver estados).
data.dgii.intentos dice cuántas veces lo intentó y data.dgii.mensaje qué está esperando.
Necesito el XML firmado otra vez#
GET /{ambiente}/ecf-queue/{ecf_queue_id}/xml (JSON con el campo xml) o
GET /{ambiente}/ecf-queue/{ecf_queue_id}/descargar-xml (archivo {rnc}{encf}.xml).
Necesito el PDF#
GET /api/pdf/{ecf_queue_id} con tu API Key (header Authorization). Para mostrárselo a un cliente,
descárgalo desde tu sistema y entrégale el archivo: el enlace no abre sin la key.
Tengo que anular o corregir una factura ya aceptada#
No se anula: se emite una nota de crédito (E34) que referencia la factura original, o una nota de débito (E33) para aumentar el monto. Ver E34 y E33.