Referencia
Referencia de la API
Una API REST simple y segura para emitir, firmar y transmitir Comprobantes Fiscales Electrónicos (e-CF) ante la DGII. Todas las peticiones se autentican con tu API key y devuelven una respuesta uniforme.
Autenticación
Las integraciones máquina-a-máquina se autentican con una API key en
la cabecera Authorization.
Los usuarios humanos del panel usan un token JWT; como integrador,
siempre utilizas tu API key.
Al emitir con tu API key de integrador debes añadir la cabecera X-Emisor-RNC con el RNC del emisor por el que emites. Es obligatoria con una API key de integrador: identifica a cuál de tus emisores
pertenece el comprobante y, si falta, la API responde 400.
Authorization: Bearer <API_KEY>
X-Emisor-RNC: <RNC_EMISOR> Ambientes y URLs base
- Producción — ambiente DGII: eCF
- https://api.ecf.synercore.do
- Pruebas / Certificación — ambientes DGII: TesteCF · CerteCF
- https://devapi.ecf.synercore.do
- Panel web — producción
- https://app.ecf.synercore.do
- Panel web — pruebas / certificación
- https://devapp.ecf.synercore.do
Las dos instancias están completamente separadas — bases de datos y
credenciales independientes — y la separación la garantiza el propio
servicio: la instancia de pruebas no puede
transmitir al ambiente real de la DGII, ni la de producción a los de
prueba. Cada respuesta incluye el campo
ambiente para confirmar dónde
se emitió el comprobante.
Endpoints
Endpoints
Todas las rutas son relativas a la URL base de producción y requieren tu API key.
| Método | Ruta | Descripción | Auth |
|---|---|---|---|
| Emisión | |||
| POST | /v1/comprobantes | Emite un e-CF: valida, firma con el certificado del emisor y, si transmitir=true, lo envía a la DGII. Con esperarDgiiMs devuelve el veredicto de la DGII en la misma respuesta cuando llega a tiempo (ver Consulta de estados). | API key |
| POST | /v1/comprobantes/validar | Valida un e-CF SIN firmarlo, sin consumir secuencia y sin transmitirlo a la DGII. Úsalo para verificar que tu comprobante pasará TODAS las validaciones ANTES de reservar tu e-NCF, así no 'quemas' un número si el input es inválido. Mismo cuerpo que POST /v1/comprobantes; el eNCF es opcional (se valida con un placeholder). Responde { valid: true } (200) o el motivo de la validación fallida (400). | API key |
| Consulta de estados | |||
| GET | /v1/comprobantes/:id | Recupera un comprobante con su estado de firma y su estado ante la DGII. Es la consulta puntual: lectura de la base del servicio, rápida y económica. | API key |
| GET | /v1/comprobantes | Lista los comprobantes del emisor con filtros (?estado, ?tipo, ?desde, ?hasta, ?q por e-NCF o trackId) y paginación (?limit hasta 200, ?offset). Es la base de la reconciliación periódica. | API key |
| POST | /v1/comprobantes/estados
Nuevo
| CONCILIACIÓN POR LOTE: envía hasta 500 e-NCF en un array y recibe el estado de cada uno + un resumen por estadoDgii. Un e-NCF inexistente se marca encontrado:false sin tumbar el lote. Pensado para el cierre contable. | API key |
| POST | /v1/comprobantes/:id/refresh | FUERZA una consulta a la DGII por TrackId en este momento y actualiza el registro. Útil cuando el usuario abre un documento pendiente y quiere el dato del instante. | API key |
| GET | /v1/comprobantes/resumen | Totales por estado sobre TODOS los registros que cumplen el filtro (no solo la página cargada). Acepta los mismos filtros que el listado. Para cuadre y cierre. | API key |
| GET | /v1/comprobantes/reporte | Reporte COMERCIAL del período: cantidad y monto facturado, desglose por tipo y por estado, serie diaria y comparación contra el período inmediatamente anterior (del mismo largo). Acepta los mismos filtros que el listado. Distinto de /v1/metrics, que es operativo (tiempos de la DGII). Los montos vienen como string para no perder precisión. | API key |
| Documentos del comprobante | |||
| GET | /v1/comprobantes/:id/xml | Devuelve el XML firmado del e-CF, listo para archivar o entregar. | API key |
| GET | /v1/comprobantes/:id/pdf | Representación impresa completa en PDF (A4, con el QR del timbre). Formato validado en la certificación DGII. | API key |
| GET | /v1/comprobantes/:id/qr.png | Solo el código QR del timbre DGII, en PNG. Alternativa: usa el campo qrUrl de la respuesta y genera el QR con tu propia librería. | API key |
| Auditoría | |||
| GET | /v1/comprobantes/:id/eventos | Bitácora cronológica de eventos del comprobante (firma, envío, respuestas DGII, reintentos). Append-only: nunca se modifica ni se borra. | API key |
| GET | /v1/comprobantes/:id/timeline | Línea de tiempo del comprobante con la duración de cada etapa (firma → envío → veredicto). | API key |
| GET | /v1/comprobantes/:id/input | Devuelve el JSON original que enviaste al emitir el comprobante (auditoría de 'qué se mandó'). | API key |
| GET | /v1/metrics | Métricas agregadas del emisor: tasas de aceptación/rechazo, tiempos de respuesta de la DGII y serie diaria. Rango opcional ?desde&?hasta. | API key |
| Anulación de secuencias (ANECF) | |||
| POST | /v1/anulaciones/preview | Vista previa: cuántas secuencias cubre el rango y cuáles ya fueron emitidas. No firma ni transmite. | API key |
| POST | /v1/anulaciones | Anula ante la DGII rangos de e-NCF autorizados pero NO utilizados. SÍNCRONO: la DGII responde en la misma llamada (ACEPTADA · PARCIAL · RECHAZADA · ERROR). Si alguna secuencia ya fue emitida, corta ANTES de llamar a la DGII con 409 (el camino correcto es la Nota de Crédito 34). | API key |
| GET | /v1/anulaciones | Lista las anulaciones del emisor, con paginación y filtros. | API key |
| GET | /v1/anulaciones/:id | Detalle de una anulación: los rangos enviados, su estado y el resultado de la DGII. | API key |
| GET | /v1/anulaciones/:id/xml | ANECF firmado transmitido — evidencia fiscal de la anulación. | API key |
| Certificado y credenciales — la vía habitual es el panel | |||
| POST | /v1/certificado | Carga el certificado digital del emisor autenticado (.p12 en base64 + clave). LA VÍA HABITUAL ES EL PANEL WEB de su ambiente: este endpoint existe para quien necesite automatizarlo, no es parte del flujo de integración. Se valida abriéndolo ANTES de guardar; se almacena cifrado (AES-256-GCM, clave maestra fuera de la base de datos). Renovar = volver a cargarlo. | API key |
| GET | /v1/certificado | Metadatos del certificado cargado: titular (subjectCN), huella (fingerprint) y vencimiento (validTo). Nunca expone la clave privada. El panel muestra esta misma información. | API key |
| POST | /v1/apikeys | Crea una API key del emisor autenticado. Gestión habitual desde el panel. El texto plano se muestra UNA sola vez; el servicio solo guarda su hash. | API key |
| GET | /v1/apikeys | Lista las API keys activas del emisor, enmascaradas, con su fecha de último uso. | API key |
| DELETE | /v1/apikeys/:id | Revoca una API key. Para rotar sin corte: crea la nueva desde el panel, despliégala y revoca la anterior. | API key |
| Directorio DGII | |||
| GET | /v1/directorio | Directorio de contribuyentes electrónicos autorizados por la DGII (cacheado). Con ?rnc= filtra uno: te dice si tu cliente es receptor electrónico. | API key |
| GET | /v1/integrator/rnc/:rnc | Consulta el padrón de la DGII por RNC para autollenar los datos del contribuyente. | API key |
| Gestión de emisores (integrador) | |||
| POST | /v1/integrator/emisores | Da de alta un emisor bajo tu cuenta de integrador (y, opcionalmente, su primer usuario de panel). Requiere rnc, razonSocial y direccion. | API key |
| GET | /v1/integrator/emisores | Lista los emisores que gestionas, con su estado de certificación y el vencimiento del certificado, paginado. | API key |
| POST | /v1/integrator/emisores/:id/certificado | Carga el certificado digital de uno de tus emisores (.p12 en base64 + clave). Cifrado en reposo (AES-256-GCM). | API key |
| POST | /v1/integrator/emisores/:id/apikeys | Emite una API key para uno de tus emisores. La key en texto plano se muestra una sola vez. | API key |
| GET | /v1/integrator/comprobantes | Lista los comprobantes de TODOS tus emisores, con paginación y filtros. | API key |
| Sistema | |||
| GET | /v1/openapi.json
Nuevo
| Especificación OpenAPI 3 de la superficie de integración, autenticada con tu API key: comprobantes, anulaciones, directorio, certificado, credenciales y las rutas de integrador. Úsala para generar tu cliente, cargarla en Postman/Insomnia o montar tu propio visor. Se genera desde el código: siempre refleja el comportamiento real. | API key |
| GET | /health | Salud del servicio. Sin autenticación. | — |
Petición
Esquema de ComprobanteInput
Cuerpo JSON que envías a POST /v1/comprobantes para emitir un e-CF.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
tipo | string | Sí | Tipo de e-CF (ver tabla). Ej.: "31" para Crédito Fiscal. |
eNCF
Nuevo
| string | No | e-NCF de TU secuencia: 'E' + tipo (2 díg.) + secuencial (10 díg.) = 13 chars. Ej.: "E310000000005". Si lo envías, el servicio lo usa, firma y registra (debe ser único por emisor: repetirlo devuelve 409). Si lo omites, el servicio lo genera (modo certificación). |
fechaVencimientoSecuencia
Nuevo
| string | Condicional | dd-MM-yyyy. Vencimiento de la secuencia/NCF que manejas: es la vigencia del rango autorizado por la DGII a tu emisor, y DEBE enviarla tu sistema (no hay valor por defecto — es un dato fiscal tuyo). Obligatoria en 31, 33, 41, 43, 44, 45, 46 y 47; NO aplica en 32 ni 34. Si el tipo la requiere y no viene → 400. |
comprador.rnc | string | Condicional | RNC o cédula del comprador. Requerido en Crédito Fiscal (31), Compras (41), Gubernamental (45) y Notas de Débito/Crédito (33/34); también en Consumo (32) cuando el MontoTotal ≥ RD$250,000. |
comprador.identificadorExtranjero | string | Condicional | Identificador del comprador extranjero. Alternativo al RNC en Exportación (46) y Pagos al Exterior (47). |
comprador.razonSocial | string | Condicional | Razón social o nombre del comprador. Requerido en Crédito Fiscal (31), Compras (41), Regímenes Especiales (44), Gubernamental (45) y Exportación (46); también en Consumo (32) cuando el MontoTotal ≥ RD$250,000. |
comprador.direccion | string | No | Dirección del comprador. Opcional en todos los tipos; si la envías se emite en DireccionComprador. |
items[].nombre | string | Sí | Descripción del bien o servicio facturado. |
items[].cantidad | number | Sí | Cantidad facturada de la línea. |
items[].precioUnitario | number | Sí | Precio unitario sin ITBIS, en pesos dominicanos (DOP). |
items[].itbis | number | string | No | Tasa de ITBIS de la línea: 18, 16, 0 o "exento". Por defecto 18. |
items[].indicadorBienoServicio | integer | No | 1 = bien · 2 = servicio. Por defecto 1. |
items[].descripcion | string | No | Descripción ampliada del ítem, adicional a nombre. |
items[].unidadMedida | integer | No | Código DGII de la unidad de medida del ítem. |
tipoPago | integer | No | 1 Contado, 2 Crédito, 3 Gratuito. Por defecto 1 (Contado). |
tipoIngresos | string | No | Código de tipo de ingresos de la DGII. Por defecto "01". |
indicadorMontoGravado | integer | No | 0 = los precios NO incluyen ITBIS · 1 = los precios ya incluyen ITBIS. Por defecto 0. |
fechaEmision | string | No | Fecha de emisión en formato dd-MM-yyyy. Por defecto la fecha de hoy. Ej.: "27-06-2026". |
numeroFacturaInterna
Nuevo
| string | No | TU número interno del documento (Emisor > NumeroFacturaInterna del XSD). Opcional y SIN efecto fiscal: no entra en ningún cálculo, no lo valida la DGII y no es la identidad del comprobante — esa es el e-NCF. Sirve para que el emisor reconozca SU documento: su consecutivo interno, su serie por sucursal. Máx. 20 caracteres (admite guiones); si envías uno más largo el campo se OMITE y el comprobante se emite igual — nunca se recorta ni se rechaza. No viaja en el RFCE de la Factura de Consumo (32) < RD$250,000, cuyo XSD no lo define. |
transmitir | boolean | No | Si es true, firma y ENCOLA la transmisión a la DGII (estado ENCOLADO / EN_COLA). Si es false, solo firma: el e-CF queda FIRMADO / NO_ENVIADO, sin transmitir. |
esperarDgiiMs
Nuevo
| integer | No | ESPERA ACOTADA (ms, máx. 20000): con transmitir=true, el servicio espera el veredicto de la DGII dentro de ESTA misma respuesta. Una Factura de Consumo (32) < RD$250,000 suele volver ACEPTADO/RECHAZADO en 1-2 s (canal síncrono). El resto de tipos suele volver EN_PROCESO con trackId; el veredicto llega por consulta. Si el tiempo se agota, el comprobante sigue su curso normal: la espera es un atajo, nunca un requisito. Recomendado para punto de venta. |
referencia | object | Condicional | Datos del e-NCF modificado: { ncfModificado, fechaNCFModificado, codigoModificacion, razonModificacion?, montoNCFModificado? }. Requerido en Nota de Débito (33) y Nota de Crédito (34); fechaNCFModificado es obligatoria en ambos. codigoModificacion es el MOTIVO (ECF-3x.xsd): 1 anula el NCF modificado · 2 corrige el texto · 3 corrige los montos · 4 reemplaza un NCF emitido en contingencia · 5 referencia a una Factura de Consumo Electrónica. Envía SIEMPRE montoNCFModificado cuando la nota modifique una Factura de Consumo (32): la DGII condiciona la identificación del comprador a que esa 32 alcance RD$250,000, y sin el dato el servicio asume que NO lo alcanza. |
indicadorNotaCredito | integer | No | Solo en Nota de Crédito (34). Es el PLAZO de rebaja del ITBIS, NO el motivo de la nota (el motivo va en referencia.codigoModificacion): 0 = la nota se emite dentro de los 30 días calendario del e-CF modificado · 1 = se emite después de 30 días y NO da derecho a rebajar el ITBIS (Reglamento 293-11). Si lo omites, el servicio lo DERIVA de fechaEmision y referencia.fechaNCFModificado; si lo envías, se respeta tal cual. |
formasPago | array | No | Detalle de las formas de pago (máx. 7). Ver la sección Formas de pago. |
descuentosGlobales | array | No | Descuentos o recargos a nivel documento (máx. 20). Ver la sección Descuentos y recargos. |
items[].impuestosAdicionales | array | No | Impuestos adicionales del ítem: ISC, CDT, Primera Placa (máx. 2). Ver la sección Impuestos adicionales. |
propina | object | No | Propina legal del 10% (impuesto adicional 001). Ver la sección Propina legal. |
moneda | object | No | Moneda extranjera: { tipo, tipoCambio }. Ver la sección Moneda extranjera. |
montoTotalObjetivo
Nuevo
| number | No | Total OBJETIVO de tu factura, en DOP. Opcional. Si el MontoTotal que calcula el servicio difiere del tuyo en ≤ 5 centavos, absorbe ese residuo en el ITBIS del tramo gravado mayor para que el MontoTotal del e-CF cuadre EXACTAMENTE con tu factura (y con los pagos y los libros 606/607). Fuera de ±5 centavos se ignora. |
Ejemplo de petición
POST /v1/comprobantes HTTP/1.1
Host: api.ecf.synercore.do
Authorization: Bearer <API_KEY>
X-Emisor-RNC: <RNC_EMISOR>
Content-Type: application/json
{
"tipo": "31",
"eNCF": "E310000000005",
"fechaVencimientoSecuencia": "31-12-2026",
"comprador": {
"rnc": "131880681",
"razonSocial": "CLIENTE SRL"
},
"items": [
{
"nombre": "Servicio de consultoría",
"cantidad": 1,
"precioUnitario": 10000
}
],
"transmitir": true
}
// X-Emisor-RNC: RNC del emisor por el que emites. OBLIGATORIO
// al usar una API key de INTEGRADOR (400 si falta); identifica
// a cuál de tus emisores pertenece este comprobante.
// eNCF: tu propia secuencia (modo producción). Omítelo
// para que el servicio lo genere (modo certificación).
// fechaVencimientoSecuencia es obligatoria en 31 (no en 32/34).
// itbis (18), tipoPago (1), fechaEmision (hoy), tipoIngresos
// ("01") e indicadorMontoGravado (0) usan valores por defecto. Catálogo
Tipos de e-CF
Valor admitido en el campo tipo según la clasificación de la DGII.
Lo que NO envías: el servicio lo calcula por ti
Solo envías los datos económicos (ítems, precios, descuentos, impuestos). El servicio calcula todos los totales y genera la identidad fiscal del comprobante. Nunca incluyas estos campos en la petición:
- MontoTotal y todos los totales (ITBIS por tramo, MontoGravado, MontoExento, etc.)
- codigoSeguridad y el código QR de la representación impresa
- El XML firmado con el certificado del emisor
Secuencia
Numeración (eNCF) y trazabilidad
Cada integrador maneja su PROPIA secuencia de e-NCF. Decide si la controlas tú (producción) o si dejas que el servicio la genere (certificación).
Formato del e-NCF
"E" +
tipo (2 dígitos) + secuencial (10 dígitos) = 13 caracteres. Ejemplo para un Crédito
Fiscal (31):
E31 0000000005
└┬┘ └────┬────┘
│ └─ secuencial (10 díg.)
└───────── "E" + tipo (31) Dos modos de numeración
- Producción: envías tu
eNCF. El servicio lo usa, FIRMA, TRANSMITE y lo REGISTRA tal cual. Debe ser único por emisor. - Certificación: omites
eNCFy el servicio reserva el siguiente número de tu secuencia interna. - Deduplicación: un
eNCFrepetido por el mismo emisor devuelve409 Conflict.
fechaVencimientoSecuencia
(dd-MM-yyyy) la manejas junto con tu secuencia: es la vigencia del rango de e-NCF que la DGII
autorizó a tu emisor, y debe enviarla tu sistema — es un dato
fiscal tuyo y no existe valor por defecto. Es obligatoria
en 31, 33, 41, 43, 44, 45, 46 y 47; no aplica en 32 ni 34.
Si el tipo la requiere y no viene, la API responde
400.
Pagos
Formas de pago
Detalla cómo se pagó el comprobante. tipoPago define la modalidad y formasPago el desglose (máx. 7 formas).
| Campo | Tipo | Descripción |
|---|---|---|
tipoPago | integer | 1 Contado · 2 Crédito · 3 Gratuito. Por defecto 1. |
formasPago[].forma | integer | Código de la forma de pago (1-8, ver tabla). Máx. 7 formas. |
formasPago[].monto | number | Monto pagado con esa forma, en DOP. |
fechaLimitePago | string | dd-MM-yyyy. Solo en crédito (tipoPago=2). |
terminoPago | string | Término/plazo de pago (máx. 15 caracteres). Solo en crédito. |
Enum forma (FormaPagoType)
1 | Efectivo |
2 | Cheque / Transferencia / Depósito |
3 | Tarjeta de Débito o 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": "31",
"comprador": { "rnc": "131880681", "razonSocial": "CLIENTE SRL" },
"items": [
{ "nombre": "Producto A", "cantidad": 1, "precioUnitario": 1000 }
],
"tipoPago": 1,
"formasPago": [
{ "forma": 1, "monto": 600 },
{ "forma": 3, "monto": 580 }
]
}
// Contado (tipoPago=1): la suma de los montos (1180)
// DEBE igualar el MontoTotal calculado por el servicio.
// En crédito (2) o gratuito (3) NO se envían formasPago. Ajustes
Descuentos y recargos
Por ítem (reducen/aumentan la base de la línea) o globales (afectan un tramo de facturación completo). Siempre se envían NETOS, sin ITBIS.
Por ítem
| Campo | Tipo | Descripción |
|---|---|---|
items[].descuentoMonto | number | Descuento de línea NETO (sin ITBIS), en DOP. Reduce la base gravable del ítem. |
items[].recargoMonto | number | Recargo de línea NETO (sin ITBIS), en DOP. Aumenta la base gravable del ítem. |
items[].subDescuentos | array | Sub-desglose informativo del descuento del ítem (máx. 12). No altera el neto. |
items[].subRecargos | array | Sub-desglose informativo del recargo del ítem (máx. 12). No altera el neto. |
Globales — descuentosGlobales[] (máx. 20)
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
descuentosGlobales[].tipoAjuste | string | Sí | 'D' descuento · 'R' recargo. |
descuentosGlobales[].monto | number | Sí | Monto NETO del ajuste (reducción/aumento real de la base). Se emite TAL CUAL, también con indicadorMontoGravado=1: el descuento GLOBAL nunca se grossea. |
descuentosGlobales[].tipoValor | string | No | '$' monto fijo · '%' porcentaje (informativo). Por defecto '$'. |
descuentosGlobales[].valor | number | No | Valor declarado (porcentaje o monto). Informativo. |
descuentosGlobales[].indicadorFacturacion | integer | No | Tramo afectado: 1 ITBIS 18% · 2 ITBIS 16% · 3 ITBIS 0% · 4 exento. Por defecto 1. |
descuentosGlobales[].descripcion | string | No | Descripción del ajuste (máx. 45 caracteres). |
descuentosGlobales[].indicadorNorma1007 | integer | No | Indicador de la Norma 10-07: 0 = no incluir · 1 = incluir. |
{
"tipo": "31",
"comprador": { "rnc": "131880681", "razonSocial": "CLIENTE SRL" },
"items": [
{
"nombre": "Producto A",
"cantidad": 2,
"precioUnitario": 1000,
"descuentoMonto": 200
}
],
"descuentosGlobales": [
{
"tipoAjuste": "D",
"tipoValor": "%",
"valor": 5,
"monto": 90,
"indicadorFacturacion": 1
}
]
}
// Los montos van NETOS (sin ITBIS), SIEMPRE.
// Con indicadorMontoGravado=1 (precios CON ITBIS) el servicio
// grossea el descuento de ÍTEM, porque va dentro de MontoItem,
// que es bruto. El descuento GLOBAL NO se grossea: la DGII
// divide el detalle entre (1+tasa) y resta el global DESPUÉS,
// así que lo espera neto. Grossearlo restaría el ITBIS dos veces. Impuestos
Impuestos adicionales (ISC, CDT, Primera Placa)
Se declaran por ítem con el código del catálogo DGII (máx. 2 por ítem). El servicio calcula el monto desde el catálogo y lo suma al MontoTotal.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
items[].impuestosAdicionales[].tipoImpuesto | string | Sí | Código DGII de 3 dígitos del catálogo (002..039). El 001 (propina) NO va aquí: usa el campo propina. En Regímenes Especiales (44) solo se admiten los de campo OTROS (002-005). |
items[].impuestosAdicionales[].tipoCalculo | string | No | 'PORCENTAJE' (base × tasa) o 'ESPECIFICO' (RD$/unidad × cantidad). Por defecto el del catálogo. |
items[].impuestosAdicionales[].tasa | number | No | Override de la tasa: fracción para PORCENTAJE (0.10), RD$/unidad para ESPECIFICO. Por defecto el del catálogo. |
items[].impuestosAdicionales[].montoEspecifico | number | No | Override del monto específico RD$/unidad (alias de tasa para ESPECIFICO). |
{
"tipo": "31",
"comprador": { "rnc": "131880681", "razonSocial": "CLIENTE SRL" },
"items": [
{
"nombre": "Whisky importado",
"cantidad": 1,
"precioUnitario": 2500,
"impuestosAdicionales": [
{ "tipoImpuesto": "013" },
{ "tipoImpuesto": "030" }
]
}
]
}
// 013 = ISC Específico Whisky (RD$/unidad)
// 030 = ISC AdValorem Whisky (% sobre la base)
// Máx. 2 impuestos adicionales por ítem. El servicio calcula
// el monto desde el catálogo y lo suma al MontoTotal. Generales (001-005) — porcentaje
| Código | Tipo | Cálculo | Nota |
|---|---|---|---|
001 | Propina Legal | % (10%) | Se declara con el campo propina, no por ítem. |
002 | CDT (Telecomunicaciones Ley 153-98) | % (2%) | |
003 | ISC Servicios de Seguros | % (16%) | |
004 | ISC Servicios de Telecomunicaciones | % (10%) | |
005 | Primera Placa (Primer Registro de Vehículos) | % (17%) |
ISC Específico (006-022) — RD$/unidad
006 | Cerveza |
007 | Vinos de uva |
008 | Vermut |
009 | Demás bebidas fermentadas |
010 | Alcohol Etílico ≥ 80% |
011 | Alcohol Etílico < 80% |
012 | Aguardientes de uva |
013 | Whisky |
014 | Ron y aguardientes de caña |
015 | Gin y Ginebra |
016 | Vodka |
017 | Licores |
018 | Los demás (Bebidas y Alcoholes) |
019 | Cigarrillos tabaco cajetilla 20u |
020 | Demás Cigarrillos 20u |
021 | Cigarrillos 10u |
022 | Demás Cigarrillos 10u |
ISC AdValorem (023-039) — porcentaje
023 | Cerveza |
024 | Vinos de uva |
025 | Vermut |
026 | Demás bebidas fermentadas |
027 | Alcohol Etílico ≥ 80% |
028 | Alcohol Etílico < 80% |
029 | Aguardientes de uva |
030 | Whisky |
031 | Ron y aguardientes de caña |
032 | Gin y Ginebra |
033 | Vodka |
034 | Licores |
035 | Los demás (Bebidas y Alcoholes) |
036 | Cigarrillos tabaco cajetilla 20u |
037 | Demás Cigarrillos 20u |
038 | Cigarrillos 10u |
039 | Demás Cigarrillos 10u |
Solo aplican en los tipos que su XSD los define: 31, 32, 33, 34, 44 y 45. Enviarlos en 41, 43, 46 o 47 devuelve un error 400.
Propina
Propina legal
Propina del 10% por defecto. Se reporta como impuesto adicional código 001 y suma al MontoTotal.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
propina.habilitada | boolean | Sí | true para aplicar la propina legal. |
propina.tasa | number | No | Fracción en (0, 1]. Por defecto 0.10 (10%). |
propina.monto | number | No | Se acepta por compatibilidad, pero el servicio lo IGNORA. e-CF SIEMPRE recomputa la propina desde el detalle firmado (10% de la base SIN ITBIS de todos los ítems facturables, precio ANTES de descuentos), porque la DGII la deriva del comprobante y observaría un monto externo que no reconcilie (observación 11094). Para variar el porcentaje usa propina.tasa. |
{
"tipo": "32",
"items": [
{ "nombre": "Plato del día", "cantidad": 2, "precioUnitario": 450 }
],
"propina": { "habilitada": true }
}
// La propina (10% por defecto) se calcula sobre la base SIN
// ITBIS de TODOS los ítems facturables (gravados 18%/16%,
// tasa 0 y exentos por igual), con el precio ANTES de
// descuentos, y se reporta como impuesto adicional 001.
// Solo en tipos 31, 32, 33, 34, 44, 45. Retención
Retenciones y percepciones
Se declaran por ítem. Retención por fracción (rate) o por monto absoluto. La percepción se activa con indicadorAgente=2.
| Campo | Tipo | Descripción |
|---|---|---|
items[].itbisRetenidoRate | number | Fracción 0..1 del ITBIS del ítem a retener. |
items[].isrRetenidoRate | number | Fracción 0..1 de la base del ítem a retener como ISR. |
items[].itbisRetenidoMonto | number | Monto absoluto de ITBIS retenido (DOP). Gana sobre itbisRetenidoRate. |
items[].isrRetenidoMonto | number | Monto absoluto de ISR retenido (DOP). Gana sobre isrRetenidoRate. |
items[].indicadorAgente | integer | 1 = agente de retención (por defecto) · 2 = agente de percepción. |
items[].percepcion | object | Datos de percepción (con indicadorAgente=2): itbisPercibidoRate/isrPercibidoRate o sus montos absolutos. |
{
"tipo": "41",
"comprador": { "rnc": "131880681", "razonSocial": "PROVEEDOR SRL" },
"items": [
{
"nombre": "Servicio profesional",
"cantidad": 1,
"precioUnitario": 10000,
"itbisRetenidoRate": 1,
"isrRetenidoRate": 0.10
}
]
}
// Retención por fracción (1 = 100% del ITBIS, 0.10 = 10% ISR)
// o por monto absoluto (...Monto, que gana sobre la tasa).
// Percepción: indicadorAgente=2 + objeto percepcion.
// En 41/47 la retención es obligatoria en TODO ítem;
// 47 solo admite retención de ISR. Divisa
Moneda extranjera
Emite el bloque OtraMoneda como representación del documento en divisa. NO altera los Totales en DOP.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
moneda.tipo | string | Sí | Código ISO de la moneda, ej. 'USD'. |
moneda.tipoCambio | number | Sí | Tasa de cambio a DOP (debe ser > 0). Cada monto en moneda extranjera = monto DOP / tipoCambio. |
{
"tipo": "46",
"comprador": { "razonSocial": "FOREIGN BUYER INC" },
"items": [
{ "nombre": "Producto exportado", "cantidad": 1, "precioUnitario": 5900, "itbis": 0 }
],
"moneda": { "tipo": "USD", "tipoCambio": 59.00 }
}
// El servicio emite el bloque OtraMoneda (cada monto =
// monto DOP / tipoCambio). Es solo representación: NO altera
// los Totales en DOP ni el MontoTotal. Matriz
Soporte de funciones por tipo
Qué conceptos admite cada tipo de e-CF. El servicio rechaza (400) los datos que el XSD del tipo no define.
| Tipo | Comprador | Descuentos | Formas pago | Imp. adic. | Propina | Ret./Perc. | Moneda | Notas |
|---|---|---|---|---|---|---|---|---|
31 | RNC | Sí | Sí | Sí | Sí | Ret+Per | Completo | Tipo nominativo completo. |
32 | Opc. | Sí | Sí | Sí | Sí | — | Completo | Consumidor final; sin retención/percepción. |
33 | RNC | Sí | Sí | Sí | Sí | Ret+Per | Completo | Requiere referencia.fechaNCFModificado. |
34 | RNC | Sí | — | Sí | Sí | Ret+Per | Completo | Sin formas de pago; requiere referencia.fechaNCFModificado. |
41 | RNC | Sí | Sí | — | — | Ret+Per (oblig.) | Sin imp. adic. | Retención obligatoria en todo ítem. |
43 | — | — | — | — | — | — | Solo exento | Mínimo: solo ítems; sin descuentos ni pagos. |
44 | Razón social | Sí | Sí | Sí | Sí | — | Exento + imp. adic. | Sin campos de ITBIS por tramo. |
45 | RNC | Sí | Sí | Sí | Sí | — | Completo | Sin retención/percepción (no la define su XSD). |
46 | Razón social / Ident. ext. | Sí | Sí | — | — | — | Franja tasa-0 | Exportación: solo tramo tasa-0. |
47 | Ident. ext. / RNC | — | Sí | — | — | Solo ISR (oblig.) | Solo exento | Solo retención de ISR; sin ITBIS ni percepción. |
Validaciones
Validaciones del servicio
Antes de firmar, el servicio aplica estas validaciones. Si alguna falla, responde 400 con el motivo (y, en e-NCF duplicado, 409). Ningún comprobante inválido se firma ni consume secuencia.
| Regla | Detalle |
|---|---|
| Tipo soportado | Solo los 10 tipos: 31, 32, 33, 34, 41, 43, 44, 45, 46, 47. |
| Ítems | Al menos 1 ítem; cada uno con nombre, cantidad > 0 y precioUnitario ≥ 0. |
| Cuadratura de pagos (Contado) | En tipoPago=1, la suma de formasPago debe igualar el MontoTotal (tolerancia 0.01). |
| Totales no negativos | Los descuentos no pueden exceder la base: si un tramo o el total resultan negativos → 400. |
| Descuentos de línea | Si un ítem lleva descuentoMonto o recargoMonto, el servicio genera automáticamente la TablaSubDescuento/TablaSubRecargo que la DGII exige (aunque no envíes el sub-desglose); el monto del sub siempre cuadra con el descuento de línea. |
| Comprador por tipo | RNC en 31, 41, 45 y notas 33/34; identificadorExtranjero o RNC en 46, 47; razonSocial en 31, 41, 44, 45, 46. En Consumo (32) con MontoTotal ≥ RD$250,000 se exige identificación del comprador (RNC/Cédula + razonSocial). |
| Notas (33/34) | Referencia obligatoria: ncfModificado, fechaNCFModificado y codigoModificacion. |
| Retenciones / percepciones | Solo en tipos con capacidad (31, 33, 34, 41, 47); en 41 y 47 la retención es obligatoria por ítem; el 47 admite SOLO retención de ISR (no ITBIS). |
| Impuestos adicionales y propina | Solo en 31, 32, 33, 34, 44, 45; máximo 2 impuestos adicionales por ítem; el 001 (propina) se declara con el campo propina, no por ítem. Regímenes Especiales (44) solo admite impuestos de campo OTROS (códigos 002-005); no acepta ISC específico/ad-valorem (006-039). |
| Moneda extranjera | Si envías moneda, tipoCambio debe ser > 0. |
| e-NCF aportado | Formato 'E' + tipo (2 díg.) + 10 díg.; único por emisor (repetirlo → 409). |
| Vencimiento de secuencia | fechaVencimientoSecuencia requerida según el tipo (todos salvo 32 y 34). |
| XSD oficial DGII | El e-CF firmado se valida contra el esquema XSD oficial de la DGII antes de aceptarlo. |
Recomendado para quien maneja su propia secuencia de e-NCF: llama primero a POST /v1/comprobantes/validar. Valida sin consumir ni firmar, así no reservas un número que luego se
rechazaría — y solo asignas tu e-NCF cuando ya sabes que el comprobante
es válido.
Respuesta
Esquema de respuesta del comprobante
Estructura del objeto data devuelto al consultar o emitir un comprobante.
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador interno del comprobante en SynerCore. |
eNCF | string | Número de Comprobante Fiscal Electrónico asignado. |
codigoSeguridad | string | Código de seguridad del e-CF (usado en el QR). |
estado | string | Estado interno del comprobante en la plataforma. |
firmado | boolean | Indica si el XML ya fue firmado con el certificado del emisor. |
estadoDgii | string | Estado del comprobante ante la DGII (ver tabla de estados). |
trackId | string | Identificador de seguimiento devuelto por la DGII. |
montoTotal | string | Monto total del comprobante (incluido el ITBIS), como string con 2 decimales. Ej.: "11800.00". |
ambiente | string | null | Ambiente DGII de la transmisión: CERTECF (certificación) o ECF (producción). Es null mientras el comprobante no se ha transmitido (estado ENCOLADO / FIRMADO); el worker lo fija al transmitir. |
fechaFirma
Nuevo
| string | FechaHoraFirma del XML firmado (dd-MM-yyyy HH:mm:ss). Úsala como parámetro 'fechafirma' del QR del timbre. |
qrUrl
Nuevo
| string | null | URL del timbre DGII lista para el QR de la representación impresa (ConsultaTimbre o ConsultaTimbreFC según tipo/monto). null si el comprobante aún no tiene los datos del timbre. |
mensajeDgii
Nuevo
| string | null | Mensaje/motivo de la DGII, sobre todo en RECHAZADO o ACEPTADO_CONDICIONAL con observaciones. Texto legible listo para mostrar a tu usuario. null si no hay mensaje. |
avisos
Nuevo
| string[] | Advertencias NO bloqueantes de la emisión (p. ej. un texto recortado para respetar el largo máximo del esquema DGII). El comprobante SE EMITIÓ correctamente; el aviso es informativo. Solo aparece cuando hay algo que avisar. |
createdAt | string | Fecha y hora de creación del comprobante (ISO 8601). |
Respuesta exitosa
{
"success": true,
"data": {
"id": "cmp_9f3a2b6e",
"eNCF": "E310000000123",
"codigoSeguridad": "aB3xZ9",
"estado": "ACEPTADO",
"firmado": true,
"estadoDgii": "ACEPTADO",
"trackId": "5d8f1c4a-...",
"montoTotal": "11800.00",
"ambiente": "ECF",
"fechaFirma": "27-06-2026 14:22:08",
"qrUrl": "https://ecf.dgii.gov.do/ecf/consultatimbre?...",
"mensajeDgii": null,
"createdAt": "2026-06-27T14:22:08.512Z"
}
} Respuesta con error
{
"success": false,
"error": {
"message": "El campo 'tipo' es requerido.",
"statusCode": 400
}
} Sincronización
Consulta de estados: cómo sincronizar tu sistema
El servicio NO envía webhooks ni callbacks: tu sistema consulta el estado contra la API. Esta tabla resume qué devuelve la emisión según el escenario, y qué URL usar para obtener el veredicto final.
| Escenario al emitir | La API devuelve | Qué significa para tu sistema |
|---|---|---|
| transmitir: false | 201 · estadoDgii: NO_ENVIADO | El e-CF queda firmado y válido, sin transmitir. Transmítelo después o consérvalo así. |
| transmitir: true (sin espera) | 201 · estadoDgii: EN_COLA · trackId: null | Comportamiento asíncrono clásico: el servicio transmite y consulta a la DGII por ti. Obtén el veredicto por reconciliación o consulta puntual. |
| + esperarDgiiMs — Factura de Consumo (32) < RD$250,000 | 201 · estadoDgii: ACEPTADO | RECHAZADO | El canal de resumen (RFCE) es SÍNCRONO: el veredicto definitivo suele llegar en 1-2 s, dentro de la misma respuesta. No necesitas consultar nada más. |
| + esperarDgiiMs — resto de tipos (31, 33, 34, …) | 201 · estadoDgii: EN_PROCESO · trackId: presente | La DGII ya lo recibió y lo está procesando. El veredicto final llega en segundos o minutos: recógelo con la reconciliación o con GET /v1/comprobantes/:id. |
| DGII caída o en mantenimiento | 201 · estadoDgii: EN_COLA | TU FACTURACIÓN NO SE DETIENE. El e-CF queda firmado y el servicio reintenta con espera incremental (30 s → … → 15 min) durante ~74 horas hasta transmitirlo. Tu sistema no tiene que hacer nada. |
Reconciliación periódica — el mecanismo principal
Un proceso recurrente en tu sistema que barre los comprobantes sin veredicto definitivo:
GET /v1/comprobantes?estado=EN_PROCESO&desde=04-08-2026&hasta=09-08-2026&limit=200 - Intervalo: cada 10 minutos es suficiente. Ventana: 5 días hacia atrás. Máximo: 500 documentos por ciclo.
- La regla más importante: consulta SOLO los que no tienen veredicto terminal. Un ACEPTADO, ACEPTADO_CONDICIONAL o RECHAZADO no vuelve a cambiar nunca — deja de consultarlo. Sin ese filtro repetirás cientos de llamadas sobre datos inmutables.
- Escritura: actualiza tu base solo si algo cambió.
Conciliación por lote (cierre contable)
POST /v1/comprobantes/estados
Authorization: Bearer <API_KEY>
{ "eNCF": ["E310000000001", "E310000000002", "E320000000007"] }
// Respuesta:
{
"success": true,
"data": {
"solicitados": 3,
"encontrados": 3,
"noEncontrados": 0,
"porEstadoDgii": { "ACEPTADO": 2, "EN_PROCESO": 1 },
"items": [
{
"eNCF": "E310000000001",
"encontrado": true,
"estadoDgii": "ACEPTADO",
"trackId": "5f8b...",
"montoTotal": "17700.00",
"mensajeDgii": null
}
// ...
]
}
}
// Hasta 500 e-NCF por llamada. Un e-NCF inexistente
// se marca encontrado:false SIN tumbar el lote. Estados
Estados ante la DGII
El comprobante tiene dos estados separados: la firma (firmado: true/false) y el estado de procesamiento ante la DGII (estadoDgii).
| Estado | Descripción |
|---|---|
NO_ENVIADO | El comprobante está firmado pero aún no se ha transmitido a la DGII. |
EN_COLA | El comprobante está en cola para ser transmitido. |
ENVIADO | El comprobante fue transmitido a la DGII y espera procesamiento. |
EN_PROCESO | La DGII recibió el comprobante y lo está procesando. |
ACEPTADO | La DGII aceptó el comprobante sin observaciones. |
ACEPTADO_CONDICIONAL | La DGII aceptó el comprobante con observaciones que debes revisar. |
RECHAZADO | La DGII rechazó el comprobante. Consulta los eventos para ver el motivo. |
Errores
Códigos de estado HTTP
Respuestas estándar que devuelve la API. Los errores siempre incluyen un objeto error con message y statusCode.
| Código | Significado | Cuándo ocurre | ¿Reintentar? |
|---|---|---|---|
200 | OK | La consulta se resolvió correctamente. | — |
201 | Created | El comprobante se creó y se aceptó para firma/transmisión. El campo estadoDgii de la respuesta te dice en qué punto quedó (ver Consulta de estados). | — |
400 | Bad Request | El cuerpo es inválido, faltan campos requeridos, el tipo no admite un concepto, o el e-CF generado no cumple el XSD oficial de la DGII. El mensaje indica el campo exacto. | No — corrige el JSON |
401 | Unauthorized | La API key falta, es inválida o fue revocada. | No — revisa la credencial |
403 | Forbidden | El emisor del header X-Emisor-RNC existe pero NO pertenece a tu cuenta de integrador. | No — revisa X-Emisor-RNC |
404 | Not Found | El recurso solicitado no existe o no pertenece a tu cuenta. | No — verifica el id |
409 | Conflict | Ya existe un comprobante con ese e-NCF para el emisor en ese ambiente (e-NCF duplicado), o en una anulación alguna secuencia del rango ya fue emitida. EL DOCUMENTO YA EXISTE: recupéralo con GET /v1/comprobantes?q=<eNCF> en vez de reemitirlo. | No — consúltalo |
429 | Too Many Requests | Excediste el límite de 120 peticiones/minuto de tu API key. El mensaje incluye cuánto esperar ('Reintenta en X'). | Sí — con la espera indicada |
502 | Bad Gateway | No se pudo consultar un servicio de la DGII (p. ej. el directorio). El detalle viene en error.message. | Sí — con espera |
500 | Internal Server Error | Error inesperado del servidor. Si persiste, contacta a soporte con el id del comprobante. | Sí — si persiste, soporte |
Validación XSD: antes de transmitir, el servicio
valida el e-CF firmado contra el XSD oficial de la DGII para ese tipo.
Si no cumple, responde 400 con
el detalle del incumplimiento en
error.message, de modo que el
fallo se detecta localmente y no en la DGII.
Rate limiting
Límites de uso
Límites concretos del servicio. Al superar el de peticiones, la API responde 429 indicando cuánto esperar; el límite se aplica por API key, así que la actividad de un integrador no afecta a otro.
| Límite | Valor |
|---|---|
| Peticiones por API key | 120 por minuto |
| Tamaño máximo de la petición | 15 MB |
| e-NCF por lote de conciliación (POST /v1/comprobantes/estados) | 500 |
| Registros por página en listados (?limit) | 200 (25 por defecto) |
| Espera del veredicto en la emisión (esperarDgiiMs) | 20 segundos |
| Formas de pago por comprobante | 7 |
| Descuentos o recargos globales | 20 |
| Impuestos adicionales por ítem | 2 (1 si hay propina) |
429: el mensaje de error incluye el
tiempo de espera ("Reintenta en X"). Respeta esa espera y reintenta.
Ante 5xx o
502, usa retroceso exponencial.
Nunca reintentes un 4xx de validación sin
corregir los datos.