Invoices
Tres endpoints alrededor del recurso invoice:
| Método | Path | Para qué |
|---|---|---|
POST | /invoices | Enviar un nuevo e-CF |
GET | /invoices/:trackOrId | Consultar estado |
GET | /invoices | Listar historial paginado |
Todos requieren Authorization: Bearer <JWT>.
"Invoices" cubre TODA la familia de e-CFs DGII, no solo facturas.
El tipo de documento se determina por el campo TipoeCF dentro del
XML/JSON que mandas — no por el endpoint o un parámetro de URL.
Tipos de e-CF soportados
POST /invoices acepta cualquiera de los siguientes tipos vía el campo
TipoeCF del header (Encabezado.IdDoc.TipoeCF en JSON Digimart):
| Código | Tipo de documento | Uso típico |
|---|---|---|
31 | Factura de Crédito Fiscal | B2B con derecho a crédito ITBIS |
32 | Factura de Consumo | B2C — consumidor final |
33 | Nota de Débito Electrónica | Aumento de un e-CF previamente emitido |
34 | Nota de Crédito Electrónica | Devolución / descuento sobre un e-CF previo |
41 | Compras | Registro de compras a no-emisores (gastos informales) |
43 | Gastos Menores | Operaciones < umbral DGII |
44 | Régimen Especial | Sectores con tratamiento fiscal específico |
45 | Gubernamental | Operaciones con el Estado |
46 | Comprobante Pago Exterior | Pagos a proveedores fuera del país |
47 | Régimen Especial Pago Exterior | Combinación de 44 y 46 |
Otros documentos también soportados
POST /invoices también acepta Resumen de Facturas de Consumo (RFCE)
y Aprobaciones Comerciales (ACECF / ANECF). El endpoint del cliente
es el mismo — el worker auto-detecta el root element del XML/JSON y
rutea al endpoint DGII correcto bajo el capó:
| Root element | Tipo de documento | Endpoint DGII destino |
|---|---|---|
<ECF> | e-CF estándar (tipos 31-47) | ecf.dgii.gov.do/{env}/recepcion/api/FacturasElectronicas |
<RFCE> | Resumen Facturas Consumo | fc.dgii.gov.do/{env}/recepcionfc/api/recepcion/ecf |
<ACECF> | Aprobación Comercial (emisor → receptor) | ecf.dgii.gov.do/{env}/aprobacionComercial/api/AprobacionComercial |
<ANECF> | Aprobación de Notas | ecf.dgii.gov.do/{env}/aprobacionComercial/api/AprobacionComercial |
Si mandas format: 'digimart' con un JSON cuyo root es RFCE, el worker
lo convierte a XML con el tag correcto, lo firma, y lo manda al host
CF de DGII. Tú no tienes que hacer nada distinto — un solo POST,
el routing es invisible.
POST /invoices
Envía un comprobante para procesamiento async. Responde 202 con un identificador que puedes usar para consultar estado.
POST /api/v1/fe/invoices
Authorization: Bearer <JWT>
Content-Type: application/jsonScope requerido: invoices:write
Request body
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
format | "xml" | "digimart" | ✅ | Forma del payload |
xml | string | Si format="xml" | XML completo del e-CF |
payload | object | Si format="digimart" | JSON estructurado |
clientRequestId | string | ❌ | Idempotency key opcional |
format: "xml"
Le pasas el XML del e-CF tal cual lo construiste. Lo firmamos y enviamos.
{
"format": "xml",
"clientRequestId": "factura-001-2026-06-15",
"xml": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ECF xmlns:xsi=\"...\">...</ECF>"
}format: "digimart"
Le pasas un JSON estructurado que sigue el esquema interno de Digimart. Nosotros lo convertimos a XML, firmamos, y enviamos.
{
"format": "digimart",
"clientRequestId": "factura-001-2026-06-15",
"payload": {
"Encabezado": {
"Version": "1.0",
"IdDoc": {
"TipoeCF": "31",
"eNCF": "E310000000001",
"FechaVencimientoSecuencia": "31-12-2026"
},
"Emisor": { /* ... */ },
"Comprador": { /* ... */ },
"Totales": { /* ... */ }
},
"DetallesItems": { /* ... */ }
}
}Si necesitas el esquema completo del JSON Digimart, contáctanos — publicamos la definición en formato JSON Schema.
clientRequestId (idempotency)
Si tu sistema reintenta la misma factura (timeout de red, retry interno,
etc.), envía el mismo clientRequestId en cada intento. Detectamos
la duplicación y devolvemos el envío original en vez de procesar dos veces.
- Recomendación: usa un id determinístico desde TU sistema
(ej.
tu_factura_id+ fecha). - Longitud: 1-120 caracteres.
- Si lo omites, no hay protección contra retries duplicados.
Response — 202 Accepted
{
"dgiiLogId": "a3f7b8c9-d1e2-3f4g-5h6i-7j8k9l0m1n2o",
"status": "queued",
"environment": "ecf",
"billable": true,
"message": "Invoice queued for processing"
}| Campo | Descripción |
|---|---|
dgiiLogId | UUID del registro. Úsalo para consultar estado (ver abajo). |
status | Siempre "queued" inicialmente. Cambia a "Aceptado", "Rechazado", etc. después de la respuesta de DGII. |
environment | Ambiente al que se envía (determinado por la key). |
billable | true solo si environment === "ecf". Pruebas no facturan. |
Response — 400 Bad Request
{
"message": "xml is required when format=\"xml\"",
"statusCode": 400
}Otros errores 400 comunes:
payload is required when format="digimart"format must be one of: xml, digimart
Idempotency response
Si reutilizas un clientRequestId:
{
"dgiiLogId": "a3f...",
"status": "queued",
"deduped": true,
"message": "Existing submission returned (idempotency key matched)"
}El deduped: true te dice que NO procesamos de nuevo — te devolvimos el
envío original.
GET /invoices/:trackOrId
Consulta el estado de un comprobante. Acepta tanto el dgiiLogId (que
te dimos al hacer POST) como el trackId (que DGII asigna después).
GET /api/v1/fe/invoices/{trackOrId}
Authorization: Bearer <JWT>Scope requerido: invoices:read
Response — 200 OK (procesado)
{
"dgiiLogId": "a3f7b8c9-...",
"trackId": "DGII-2026-06-15-12345",
"environment": "ecf",
"status": "Aceptado",
"qrCodeUrl": "https://ecf.dgii.gov.do/ConsultaTimbre?...",
"securityCode": "abc123",
"ncfExpiration": "31-12-2026",
"fechaHoraFirma": "2026-06-15T14:23:11Z",
"response": { /* respuesta completa de DGII */ },
"submittedAt": "2026-06-15T14:23:10Z",
"updatedAt": "2026-06-15T14:23:13Z",
"clientRequestId": "factura-001-2026-06-15"
}| Campo | Descripción |
|---|---|
status | "Aceptado", "Aceptado Condicional", "Rechazado", "En Proceso", o null (todavía no procesado) |
qrCodeUrl | URL del QR oficial para incluir en tu PDF de factura |
securityCode | Código de seguridad asignado por DGII |
ncfExpiration | Fecha en que expira la secuencia NCF |
response | Respuesta cruda de DGII (para debugging) |
Estados posibles
| Estado | Significado | ¿Es facturable? |
|---|---|---|
"Aceptado" | DGII aceptó el e-CF sin observaciones. | ✅ Sí |
"Aceptado Condicional" | DGII aceptó con observaciones (revisa mensajes). | ✅ Sí |
"Rechazado" | DGII rechazó. Mira response.mensajes para detalles. | ❌ No |
"En Proceso" | DGII todavía procesando. Vuelve a consultar en ~30s. | ❌ Todavía no |
null o "queued" | Aún no enviado a DGII. Vuelve a consultar en ~5s. | ❌ Todavía no |
Response — 200 OK (no encontrado)
{
"found": false
}Significa que el trackOrId no corresponde a ningún envío tuyo.
GET /invoices
Lista paginada del historial de envíos del tenant. Solo retorna envíos
hechos a través de esta API pública (source = 'fe-api').
GET /api/v1/fe/invoices?page=1&limit=50&from=2026-06-01&to=2026-06-30
Authorization: Bearer <JWT>Scope requerido: invoices:read
Query params
| Param | Tipo | Default | Descripción |
|---|---|---|---|
page | number | 1 | Página (1-indexed) |
limit | number | 50 | Items por página (max 200) |
from | ISO date | — | Filtro: createdAt >= from |
to | ISO date | — | Filtro: createdAt <= to |
status | string | — | (reservado para uso futuro) |
Response — 200 OK
{
"page": 1,
"limit": 50,
"total": 247,
"items": [
{
"dgiiLogId": "a3f...",
"trackId": "DGII-...",
"environment": "ecf",
"status": "Aceptado",
"qrCodeUrl": "...",
"submittedAt": "2026-06-15T14:23:10Z",
"updatedAt": "2026-06-15T14:23:13Z",
"clientRequestId": "factura-001-2026-06-15"
}
/* ... más items ... */
]
}Ordenado por submittedAt DESC (más recientes primero).