Saltar al contenido
Volver al inicio

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 por API key REST / JSON Custodia de certificados

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 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 Descripción del bien o servicio facturado.
items[].cantidad number Cantidad facturada de la línea.
items[].precioUnitario number 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.

31 Crédito Fiscal
32 Consumo
33 Nota de Débito
34 Nota de Crédito
41 Compras
43 Gastos Menores
44 Regímenes Especiales
45 Gubernamental
46 Exportaciones
47 Pagos al Exterior

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 eNCF y el servicio reserva el siguiente número de tu secuencia interna.
  • Deduplicación: un eNCF repetido por el mismo emisor devuelve 409 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.
En contado (tipoPago=1) la suma de los montos debe igualar el MontoTotal. En crédito (2) puedes añadir fechaLimitePago y terminoPago. En gratuito (3) no se envían formas de pago.

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 'D' descuento · 'R' recargo.
descuentosGlobales[].monto number 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.
Gross-up: tú siempre envías el NETO. Con indicadorMontoGravado=1 (precios CON ITBIS) el servicio grossea el descuento/recargo de ÍTEM (×(1+tasa)), porque viaja dentro de MontoItem, que es bruto. El GLOBAL NO se grossea: se emite neto en los dos modos. No aplican en los tipos 43 ni 47.

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 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 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.
Se calcula sobre la base SIN ITBIS de todos los ítems facturables — gravados 18%/16%, tasa 0 y exentos por igual (es una obligación laboral sobre el consumo, independiente del ITBIS) — con el precio ANTES de descuentos. Solo en los tipos 31, 32, 33, 34, 44 y 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.
Retención + percepción en 31, 33, 34 y 41; en 41 la retención es obligatoria en todo ítem. 47 solo admite retención de ISR (obligatoria, sin ITBIS ni percepción). El monto absoluto gana sobre la fracción.

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 Código ISO de la moneda, ej. 'USD'.
moneda.tipoCambio number 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 Ret+Per Completo Tipo nominativo completo.
32 Opc. Completo Consumidor final; sin retención/percepción.
33 RNC Ret+Per Completo Requiere referencia.fechaNCFModificado.
34 RNC Ret+Per Completo Sin formas de pago; requiere referencia.fechaNCFModificado.
41 RNC 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 Exento + imp. adic. Sin campos de ITBIS por tramo.
45 RNC Completo Sin retención/percepción (no la define su XSD).
46 Razón social / Ident. ext. Franja tasa-0 Exportación: solo tramo tasa-0.
47 Ident. ext. / RNC 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ó.
Presupuesto de llamadas: el límite es de 120 peticiones/minuto por API key. Una reconciliación cada 10 minutos con páginas de 200 consume una fracción mínima — no hay riesgo de agotarlo con este patrón.

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.