Saltar al contenido

Guía de integración

Documentación de la API e-CF

Integra la emisión, firma y transmisión de Comprobantes Fiscales Electrónicos ante la DGII con una sola API. Custodiamos el certificado de cada emisor cifrado y firmamos los e-CF en su nombre, mientras tú te concentras en tu producto.

Autenticación Bearer Respuesta JSON uniforme Trazabilidad total

Empezar

Introducción

SynerCore e-CF, operado por SYNERX SRL — Emisor Electrónico certificado por la DGII y actualmente en proceso de autorización como Proveedor de Servicios de Facturación Electrónica. Operamos en modelo SaaS: tú integras una sola API y nosotros nos encargamos de firmar y transmitir los comprobantes de cada uno de tus emisores.

API producción

api.ecf.synercore.do

API staging

devapi.ecf.synercore.do

Panel web

app.ecf.synercore.do

pruebas: devapp.ecf.synercore.do

Todas las respuestas de la API siguen un mismo sobre (envelope). Una operación exitosa devuelve success: true junto al objeto data; un fallo devuelve success: false junto a un objeto error con message y statusCode. Mantén este contrato presente en toda tu integración.

Seguridad

Autenticación

La API se autentica con una clave de máquina (API key) enviada en la cabecera Authorization. El panel web usa JWT para usuarios humanos; tu integración siempre usa la API key.

Incluye tu clave en cada petición con el esquema Bearer. Toda solicitud que envíe o reciba JSON debe declarar también Content-Type: application/json.

Cabeceras

Authorization: Bearer sk_live_a1b2c3d4e5f6g7h8i9j0
Content-Type: application/json

Ejemplo con cURL

curl https://api.ecf.synercore.do/v1/comprobantes/abc123 \
  -H "Authorization: Bearer sk_live_a1b2c3d4e5f6g7h8i9j0"

Trata tu API key como una contraseña. Si se compromete, cualquiera podría emitir comprobantes en nombre de tus emisores. Guárdala en el servidor y rótala desde el panel ante cualquier sospecha.

Cabecera X-Emisor-RNC (requerida con la API key de integrador). Cuando autenticas con la API key de integrador, cada emisión (POST /v1/comprobantes) debe incluir esta cabecera con el RNC del emisor por el que emites. El servicio valida que ese emisor sea tuyo: responde 400 si falta la cabecera, 404 si el RNC no corresponde a ninguno de tus emisores y 403 si el emisor no pertenece a tu integrador. Con la API key de un emisor concreto la cabecera no es necesaria.

Cómo funciona

Flujo de integración

Como integrador das de alta a tus propios emisores (clientes), cargas el certificado de cada uno y emites e-CF en su nombre. Estos son los cuatro pasos del ciclo completo.

  1. 01

    Recibe tu API key

    Te entregamos una clave de integrador con la que autenticas todas tus llamadas a la API.

  2. 02

    Da de alta tus emisores y sube el certificado

    Registra a cada cliente (emisor) y carga su archivo .p12. Lo custodiamos cifrado con AES-256-GCM, con la clave maestra fuera de la base de datos; nunca queda expuesto.

  3. 03

    Emite e-CF en su nombre

    Envía el comprobante a POST /v1/comprobantes. Lo firmamos con el certificado del emisor y, si lo pides, lo transmitimos a la DGII.

  4. 04

    Consulta estado, XML y QR

    Verifica el estado en la DGII, descarga el XML firmado y obtén la bitácora de eventos para tu respaldo fiscal.

Emisión

Emitir un e-CF

Para emitir un comprobante envía un objeto ComprobanteInput a POST /v1/comprobantes. Con transmitir: true lo firmamos y lo enviamos a la DGII en la misma operación.

Solicitud

POST /v1/comprobantes HTTP/1.1
Host: api.ecf.synercore.do
Authorization: Bearer sk_live_a1b2c3d4e5f6g7h8i9j0
Content-Type: application/json

{
  "tipo": "31",
  "eNCF": "E310000000123",
  "fechaVencimientoSecuencia": "31-12-2026",
  "comprador": {
    "rnc": "131880681",
    "razonSocial": "CLIENTE SRL"
  },
  "items": [
    {
      "nombre": "Servicio de consultoría",
      "cantidad": 1,
      "precioUnitario": 10000,
      "itbis": 18
    }
  ],
  "tipoPago": 1,
  "fechaEmision": "27-06-2026",
  "transmitir": true
}

tipo

Código del tipo de e-CF (ej. "31"). Ver la tabla de tipos más abajo.

comprador

Datos del receptor: rnc y razonSocial.

items

Líneas del comprobante con nombre, cantidad, precioUnitario e itbis.

tipoPago / fechaEmision / transmitir

Forma de pago, fecha en formato DD-MM-AAAA y si se transmite a la DGII de inmediato.

Respuesta

{
  "success": true,
  "data": {
    "id": "cmp_8f3k29ad",
    "eNCF": "E310000000123",
    "codigoSeguridad": "aB3xK9",
    "estado": "ENCOLADO",
    "firmado": true,
    "estadoDgii": "EN_COLA",
    "trackId": null,
    "montoTotal": "11800.00",
    "ambiente": null,
    "createdAt": "2026-06-27T14:32:08.000Z"
  }
}

La validación de la DGII es asíncrona. La respuesta inicial puede devolver estadoDgii: "EN_COLA"; consulta el comprobante más tarde para conocer el resultado definitivo.

Integración

Ejemplos por lenguaje

El mismo request — emitir un Crédito Fiscal (tipo 31) — implementado en seis lenguajes. Todos apuntan a la misma URL, envían el mismo cuerpo JSON, leen eNCF, estado y codigoSeguridad, y manejan el error cuando el status es 400 o superior.

Cada snippet lee la API key desde la variable de entorno ECF_API_KEY. Nunca incrustes la clave en el código fuente. Cambia api.ecf.synercore.do por devapi.ecf.synercore.do para probar en staging.

cURL

curl -X POST https://api.ecf.synercore.do/v1/comprobantes \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Emisor-RNC: 130862346" \
  -H "Content-Type: application/json" \
  -d '{
    "tipo": "31",
    "eNCF": "E310000000123",
    "fechaVencimientoSecuencia": "31-12-2026",
    "comprador": { "rnc": "131880681", "razonSocial": "CLIENTE SRL" },
    "items": [
      { "nombre": "Servicio de consultoría", "cantidad": 1, "precioUnitario": 10000, "itbis": 18 }
    ],
    "tipoPago": 1,
    "transmitir": true
  }'

Todos reciben la misma respuesta 201: { success: true, data: { eNCF, estado, codigoSeguridad, ... } }. Consulta la sección Ejemplos de uso para ver respuestas completas por escenario.

Cálculo

Lo que calcula el servicio

Solo envías los datos económicos del comprobante (ítems, precios, descuentos, impuestos). El servicio calcula todos los totales y genera la identidad fiscal. Estos campos NUNCA se envían en la petición.

  • MontoTotal y todos los totales (ITBIS por tramo, MontoGravado, MontoExento).
  • codigoSeguridad y el código QR de la representación impresa.
  • El XML firmado con el certificado del emisor.

Funciones

Conceptos avanzados

Más allá de los ítems básicos, el ComprobanteInput admite formas de pago, descuentos, impuestos adicionales, propina, retenciones y moneda extranjera. Consulta la Referencia de la API para los campos exactos de cada uno.

Numeración (eNCF) y secuencia

eNCF = 'E' + tipo (2 díg.) + secuencial (10 díg.) = 13 chars (ej. E310000000005). Cada integrador maneja su PROPIA secuencia: si ENVÍAS eNCF, el servicio lo usa, firma y registra (único por emisor; repetirlo → 409). Si lo OMITES, lo genera el servicio (modo certificación). fechaVencimientoSecuencia (dd-MM-yyyy) es obligatoria en 31, 33, 41, 43, 44, 45, 46 y 47; no aplica en 32 ni 34. Aparte y SIN efecto fiscal, numeroFacturaInterna lleva TU consecutivo interno (máx. 20 chars): si lo mandas más largo se omite, el comprobante se emite igual.

Formas de pago

tipoPago (1 Contado · 2 Crédito · 3 Gratuito) y formasPago[] { forma 1-8, monto } (máx. 7). En contado la suma de los montos debe igualar el MontoTotal; en crédito añade fechaLimitePago y terminoPago; en gratuito no se envían formas de pago.

Descuentos y recargos

Por ítem (descuentoMonto / recargoMonto, con subDescuentos / subRecargos) o globales (descuentosGlobales[] { tipoAjuste D/R, tipoValor $/%, valor, monto, indicadorFacturacion, indicadorNorma1007 }). Siempre se envían NETOS. Con indicadorMontoGravado=1 el servicio grossea el de ÍTEM (viaja dentro de MontoItem, que es bruto); el GLOBAL NO se grossea: la DGII lo resta DESPUÉS de dividir el detalle entre (1+tasa), así que lo espera neto.

Impuestos adicionales

items[].impuestosAdicionales[] { tipoImpuesto } con el código del catálogo DGII (ISC específico 006-022, ISC ad-valorem 023-039, CDT, Primera Placa). Máx. 2 por ítem. El servicio calcula el monto y lo suma al MontoTotal.

Propina legal

propina { habilitada: true, tasa? } — 10% por defecto. Se reporta como impuesto adicional código 001 y suma al MontoTotal. Solo en tipos 31, 32, 33, 34, 44 y 45.

Retenciones y percepciones

Por ítem: itbisRetenidoRate / isrRetenidoRate (fracción) o ...Monto (absoluto, gana sobre la tasa). La percepción se activa con indicadorAgente=2 y el objeto percepcion. Obligatoria en 41 y 47; 47 solo admite retención de ISR.

Moneda extranjera

moneda { tipo, tipoCambio }. El servicio emite el bloque OtraMoneda (cada monto = monto DOP / tipoCambio). Es solo representación: NO altera los Totales en DOP ni el MontoTotal.

Los montos de descuentos, recargos y retenciones se envían siempre NETOS (sin ITBIS). Si tus precios ya incluyen ITBIS, indica indicadorMontoGravado: 1 y el servicio grossea el descuento/recargo de ítem al emitir (viaja dentro de MontoItem, que es bruto). El descuento/recargo global NO se grossea: se emite neto en los dos modos, porque la DGII lo resta después de dividir el detalle entre (1+tasa).

Matriz

Soporte de funciones por tipo

No todos los tipos admiten todas las funciones. El servicio rechaza con 400 los datos que el XSD del tipo no define. Esta matriz resume qué aplica a cada uno.

Tipo Descuentos Formas pago Imp. adic. Propina Ret./Perc. Moneda
31 Ret + Perc. Completo
32 Completo
33 Ret + Perc. Completo
34 Ret + Perc. Completo
41 Ret + Perc. (oblig.) Sin imp. adic.
43 Solo exento
44 Exento + imp. adic.
45 Completo
46 Franja tasa-0
47 Solo ISR (oblig.) Solo exento

Exclusiones clave: 43 (Gastos Menores) es mínimo — solo ítems, sin descuentos ni formas de pago. 44 no lleva ITBIS por tramo. 46 (Exportación) opera en franja tasa-0. 47 solo admite retención de ISR. El comprador es por RNC en 31/33/34/41/45, con razón social en 31/41/44/45/46, y por identificador extranjero en 46/47. En Consumo (32) con MontoTotal ≥ RD$250,000 también se exige comprador identificado (RNC/Cédula + razón social).

Sincronización

Consultar estados: cómo sincronizar tu sistema

El servicio NO envía webhooks ni callbacks: tu sistema consulta el estado contra la API. Diseña la integración con este modelo desde el inicio — son tres niveles y cubren desde el punto de venta hasta el cierre contable.

Nivel 1 — Veredicto en la misma respuesta (esperarDgiiMs)

POST /v1/comprobantes
{ "tipo": "32", "transmitir": true, "esperarDgiiMs": 8000, ... }

// Factura de Consumo (32) < RD$250,000 → canal SÍNCRONO:
// → 201 { "estadoDgii": "ACEPTADO", ... }   en 1-2 segundos.
// No necesitas consultar nada más.

// Resto de tipos (31, 33, 34, …) → asíncrono con TrackId:
// → 201 { "estadoDgii": "EN_PROCESO", "trackId": "5f8b...", ... }
// El veredicto final llega en segundos o minutos:
// recógelo con la reconciliación o GET /v1/comprobantes/:id.

// Máximo 20000 ms. Si se agota, el comprobante sigue su curso
// normal: la espera es un atajo, nunca un requisito.

Nivel 2 — Reconciliación periódica (el mecanismo principal)

Un proceso recurrente en tu ERP que barre los comprobantes sin veredicto definitivo. Es la pieza que garantiza que ningún comprobante quede sin su estado final, y te recomendamos implementarla sin excepción:

GET /v1/comprobantes?estado=EN_PROCESO&desde=04-08-2026&hasta=09-08-2026&limit=200
  • Parámetros de referencia (los mismos que usamos en producción): intervalo de 10 minutos, ventana de 5 días hacia atrás, máximo 500 documentos por ciclo, y escribir en tu base solo si algo cambió.
  • La regla más importante: consulta SOLO los comprobantes sin veredicto terminal. Un ACEPTADO, ACEPTADO_CONDICIONAL o RECHAZADO no vuelve a cambiar nunca — márcalo en tu base y deja de consultarlo. Sin ese filtro repetirás cientos de llamadas sobre datos inmutables.
  • Presupuesto: con el límite de 120 peticiones/minuto, este patrón consume una fracción mínima. No hay riesgo de agotarlo.

Nivel 3 — Conciliación por lote (cierre contable)

POST /v1/comprobantes/estados
{ "eNCF": ["E310000000001", "E310000000002", "E320000000007"] }

// → 200
{
  "success": true,
  "data": {
    "solicitados": 3, "encontrados": 3, "noEncontrados": 0,
    "porEstadoDgii": { "ACEPTADO": 2, "EN_PROCESO": 1 },
    "items": [
      { "eNCF": "E310000000001", "encontrado": true,
        "estadoDgii": "ACEPTADO", "montoTotal": "17700.00",
        "trackId": "5f8b...", "mensajeDgii": null }
    ]
  }
}

// Hasta 500 e-NCF por llamada. Un e-NCF inexistente se marca
// encontrado:false SIN tumbar el lote — y que aparezca ya es
// información útil: tu ERP lo dio por emitido y no lo está.

Respuesta de GET /v1/comprobantes/:id

{
  "success": true,
  "data": {
    "id": "cmp_8f3k29ad",
    "eNCF": "E310000000123",
    "codigoSeguridad": "aB3xK9",
    "estado": "ACEPTADO",
    "firmado": true,
    "estadoDgii": "ACEPTADO",
    "trackId": "9d2e1f7c-0a44-4b8e-9c3a-77f1e2b9c001",
    "montoTotal": "11800.00",
    "ambiente": "ECF",
    "createdAt": "2026-06-27T14:32:08.000Z"
  }
}

Todos los recursos del comprobante

# ─ Estado ────────────────────────────────────────────────────
GET  /v1/comprobantes/:id           # Estado actual (consulta puntual, barata)
GET  /v1/comprobantes?estado=&q=    # Listado con filtros (base de la reconciliación)
POST /v1/comprobantes/estados       # Conciliación por LOTE (hasta 500 e-NCF)
POST /v1/comprobantes/:id/refresh   # FUERZA una consulta a la DGII ahora mismo
GET  /v1/comprobantes/resumen       # Totales por estado (cuadre/cierre)
GET  /v1/comprobantes/reporte       # Reporte comercial (por tipo, serie, vs. período anterior)

# ─ Documentos ────────────────────────────────────────────────
GET  /v1/comprobantes/:id/xml       # XML firmado (representación fiscal)
GET  /v1/comprobantes/:id/pdf       # Representación impresa (A4 con QR)
GET  /v1/comprobantes/:id/qr.png    # Solo el QR del timbre DGII

# ─ Auditoría ─────────────────────────────────────────────────
GET  /v1/comprobantes/:id/eventos   # Bitácora de eventos (trazabilidad)
GET  /v1/comprobantes/:id/timeline  # Duración de cada etapa
GET  /v1/comprobantes/:id/input     # JSON original enviado al emitir

¿Y si la DGII se cae? Tu facturación no se detiene: el e-CF se firma igual, queda en EN_COLA y el servicio reintenta con espera incremental (30 s → … → 15 min) durante ~74 horas hasta transmitirlo. Tu reconciliación lo verá pasar a estado terminal por sí solo. No reenvíes, no reintentes, no cambies de modo. Detalle en la Política de Contingencia.

Catálogo

Tipos de e-CF

La DGII reconoce diez tipos de Comprobante Fiscal Electrónico. Indica el código correspondiente en el campo tipo al emitir.

Código Tipo Uso
31 Crédito Fiscal Ventas a contribuyentes que sustentan crédito de ITBIS y costos.
32 Consumo Ventas a consumidores finales que no requieren crédito fiscal.
33 Nota de Débito Aumenta el valor de un comprobante emitido previamente.
34 Nota de Crédito Disminuye o anula el valor de un comprobante emitido previamente.
41 Compras Registra compras a personas no obligadas a emitir comprobantes.
43 Gastos Menores Sustenta gastos menores sin comprobante formal del proveedor.
44 Regímenes Especiales Operaciones bajo regímenes tributarios especiales.
45 Gubernamental Ventas a instituciones del Estado dominicano.
46 Exportaciones Ventas de bienes o servicios hacia el exterior.
47 Pagos al Exterior Pagos realizados a beneficiarios fuera del país.

Ciclo de vida

Estados: firma vs. DGII

Un comprobante tiene dos estados independientes. firmado indica si el XML ya se firmó con el certificado del emisor; estadoDgii refleja en qué punto del ciclo de validación de la DGII se encuentra.

Estado de firma — campo firmado

false El comprobante aún no ha sido firmado digitalmente con el certificado del emisor.
true El XML fue firmado con el certificado .p12 del emisor y está listo para transmitirse.

Estado DGII — campo estadoDgii

NO_ENVIADO El e-CF existe en SynerCore pero todavía no se ha transmitido a la DGII.
EN_COLA El comprobante está encolado para su envío a la DGII.
ENVIADO El e-CF fue transmitido a la DGII y se generó un trackId.
EN_PROCESO La DGII recibió el comprobante y está evaluando su validez.
ACEPTADO La DGII aceptó el comprobante. Es fiscalmente válido.
ACEPTADO_CONDICIONAL Aceptado con observaciones que conviene revisar, pero válido.
RECHAZADO La DGII rechazó el comprobante. Revisa los eventos para conocer el motivo.

Padrón

Búsqueda de RNC

Consulta el padrón de la DGII para autollenar la razón social del comprador a partir de su RNC antes de emitir.

Usa el endpoint GET /v1/integrator/rnc/:rnc para validar un RNC y obtener los datos del contribuyente. Esto te permite reducir errores de captura y precargar la razón social en tu formulario.

Respuesta

{
  "success": true,
  "data": {
    "rnc": "131880681",
    "razonSocial": "CLIENTE SRL",
    "nombreComercial": "CLIENTE SRL",
    "estado": "ACTIVO"
  }
}

Errores

Manejo de errores

Cuando una operación falla, la API devuelve success: false junto a un objeto error con un mensaje legible y el código HTTP. Procesa siempre este sobre antes de asumir éxito.

Sobre de error

{
  "success": false,
  "error": {
    "message": "El campo 'comprador.rnc' es obligatorio.",
    "statusCode": 400
  }
}

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. Así el fallo se detecta de inmediato y no en la DGII.

Código Significado Cuándo ocurre
400 Solicitud inválida Faltan campos obligatorios, el JSON es incorrecto, 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: corrígelo antes de reintentar.
401 No autenticado La cabecera Authorization no se envió o la API key es inválida o fue revocada.
403 Prohibido El emisor del header X-Emisor-RNC existe pero no pertenece a tu cuenta de integrador.
404 No encontrado El comprobante consultado no existe o no pertenece a tu cuenta, o el RNC del header X-Emisor-RNC no existe como emisor (si existe pero pertenece a otro integrador, la respuesta es 403). La búsqueda de RNC en el padrón (GET /v1/integrator/rnc/:rnc) NO devuelve 404: responde 200 con data: null si el RNC no existe.
409 Conflicto Ya existe un comprobante con ese e-NCF para el emisor en ese ambiente. EL DOCUMENTO YA EXISTE: recupéralo con GET /v1/comprobantes?q=<eNCF> en vez de reemitirlo — es tu señal de idempotencia tras un timeout.
429 Demasiadas solicitudes Se superó el límite de 120 peticiones/minuto de la API key. El mensaje indica cuánto esperar; respétalo y reintenta.
502 Error de la DGII No se pudo consultar un servicio de la DGII (p. ej. el directorio). Reintenta con espera.
500 Error interno Fallo inesperado del servidor. Reintenta y, si persiste, contacta soporte con el id del comprobante.

Observaciones y rechazos de la DGII

Distintos de los errores HTTP: estos códigos los emite la DGII al procesar el comprobante y llegan en el campo mensajeDgii y en la bitácora de eventos. Documentamos los que vemos en operación real; cualquier otro código viaja íntegro para que soporte pueda ayudarte a interpretarlo.

Código Observación Resultado Qué hacer
1100 Falta la Fecha Límite de Pago RECHAZADO Factura a crédito (tipoPago=2) sin fechaLimitePago. El servicio lo valida ANTES de firmar, así que este rechazo no debería llegar a ocurrir: si lo ves como 400 local, envía fechaLimitePago (dd-MM-yyyy).
1385 RNC del comprador no válido ACEPTADO_CONDICIONAL El RNC/cédula cumple el formato pero no figura en el padrón de la DGII. Es una OBSERVACIÓN, no un rechazo: el e-CF es válido. Verifica el identificador del cliente en tu maestro. En notas 33/34 el servicio omite automáticamente identificadores no exigidos para prevenirla.
2020 Falta el desglose del descuento Prevenido Una línea con DescuentoMonto/RecargoMonto exige su tabla de desglose. El servicio la genera automáticamente si no la envías — no tienes que hacer nada.
11094 Totales no reconcilian con el detalle Prevenido La DGII recalcula los totales desde el detalle y los compara. El servicio liquida por tramo y recomputa la propina desde el detalle firmado, precisamente para que siempre reconcilien. Si tu ERP redondea distinto, usa montoTotalObjetivo.

ACEPTADO_CONDICIONAL no es un rechazo. El comprobante es fiscalmente válido; la DGII solo dejó una observación. Muéstrala a tu usuario desde mensajeDgii y trátalo como emitido.

Recomendaciones

Buenas prácticas

Recomendaciones para una integración robusta y segura: protege tus credenciales, evita comprobantes duplicados y maneja la naturaleza asíncrona de la validación fiscal.

Protege la API key

Guarda la clave en variables de entorno o en un gestor de secretos. Nunca la incluyas en el código del cliente, repositorios públicos ni logs. Rota la clave si sospechas que fue expuesta.

Reintentos con espera

Ante errores 429 o 5xx, reintenta con backoff exponencial (1s, 2s, 4s...). No reintentes en respuestas 4xx de validación: corrige los datos antes de volver a enviar.

Idempotencia: envía TU eNCF

Aporta siempre tu propio eNCF al emitir: se convierte en la clave de idempotencia exacta. Si un timeout te deja sin respuesta, reintenta con el MISMO eNCF — si ya se registró recibes 409 y lo recuperas con GET /v1/comprobantes?q=<eNCF>; si no llegó, se emite normal. Nunca se duplica.

Consulta, no esperes webhooks

El servicio no envía notificaciones a tu infraestructura: tu sistema consulta el estado. Usa esperarDgiiMs para el veredicto inmediato en punto de venta, y una reconciliación periódica (cada 10 min, solo los SIN veredicto terminal) para el resto. Un ACEPTADO o RECHAZADO ya no cambia: deja de consultarlo.

Valida en staging

Prueba toda la integración contra el ambiente de pruebas antes de producción. Revisa el campo ambiente en cada respuesta para confirmar dónde se emitió cada comprobante.

Conserva el XML firmado

Descarga y resguarda el XML firmado y la bitácora de eventos de cada comprobante. Son tu respaldo fiscal ante cualquier verificación posterior.

Siguiente paso

¿Listo para integrar la facturación electrónica?

Solicita tu API key y empieza a emitir e-CF válidos ante la DGII en tu propia plataforma, con custodia de certificados y trazabilidad completa.