Factura electrónica desde tu sistema, con una sola llamada
La API de Nodiq e-CF crea, firma y envía tus comprobantes a la DGII, te devuelve el resultado y te avisa por webhook cuando algo cambia. Todo en JSON, con ejemplos en cURL, PHP y Node.js.
Primeros pasos y autenticación
La URL base de la API es https://ecf.nodiqtech.com/api/v1. Todas las peticiones van por HTTPS, con cuerpo y respuesta en JSON (UTF-8).
- Crea tu cuenta y registra tu empresa.
- En API y webhooks, dentro del panel, crea una clave. Se muestra completa una sola vez: guárdala en un lugar seguro.
- Envía la clave en la cabecera
Authorizationde cada petición.
Authorizationcabecera · obligatorio | Bearer nq_test_… para práctica y pruebas, Bearer nq_live_… para producción. |
Content-Typecabecera | application/json en las peticiones con cuerpo. |
Idempotency-Keycabecera | Texto único (hasta 100 caracteres) para repetir un POST sin duplicar. Ver idempotencia. |
curl https://ecf.nodiqtech.com/api/v1/estado \
-H "Authorization: Bearer nq_test_xxxxxxxxxxxxxxxx"$ch = curl_init('https://ecf.nodiqtech.com/api/v1/estado');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NODIQ_CLAVE')],
CURLOPT_RETURNTRANSFER => true,
]);
$estado = json_decode(curl_exec($ch), true);const r = await fetch('https://ecf.nodiqtech.com/api/v1/estado', {
headers: { Authorization: `Bearer ${process.env.NODIQ_CLAVE}` },
});
const estado = await r.json();Ambientes y modo prueba
El ambiente lo decide la clave, no la URL:
nq_test_…clave de prueba | Emite en el ambiente de práctica (simulado, sin conexión con la DGII) o de pre-certificación de la DGII, según la etapa de tu empresa. Los comprobantes no tienen valor fiscal y no cuentan para tu plan. |
nq_live_…clave de producción | Emite comprobantes reales ante la DGII. Requiere que tu empresa esté autorizada como emisor electrónico y tenga certificado y secuencias vigentes. |
Cada comprobante indica su ambiente en el campo ambiente: Simulado, TesteCF, CerteCF o eCF (producción).
{
"id": "01JBZ8Q3W6Y7N2K4R5T8V9X0AB",
"encf": "E320000000001",
"ambiente": "Simulado",
"estado": "aceptado"
}Idempotencia
Las redes fallan. Si tu sistema reintenta un POST porque no recibió respuesta, envía la misma cabecera Idempotency-Key: te devolveremos el comprobante que ya se creó en lugar de emitir otro y gastar otro e-NCF.
- Usa un valor que identifique la operación en tu sistema, por ejemplo
pedido-1001. - La clave es única por empresa. Si la reutilizas con un cuerpo distinto, recibirás
409con el códigoidempotencia. - También puedes enviar
referencia_externapara buscar luego el comprobante por tu propio identificador.
curl -X POST https://ecf.nodiqtech.com/api/v1/comprobantes \
-H "Authorization: Bearer nq_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1001" \
-d @comprobante.json
# Repetir la misma petición devuelve el mismo
# comprobante (mismo id y mismo e-NCF).Errores
Los errores usan códigos HTTP estándar y siempre el mismo formato. mensaje está redactado para mostrarlo a una persona; detalles señala cada campo con problema.
400 | Petición mal formada (JSON inválido). |
401 | Falta la clave, es inválida o fue revocada. |
403 | La clave no tiene permiso para esta acción o la empresa está suspendida. |
404 | El recurso no existe o pertenece a otra empresa. |
409 | Conflicto: idempotencia, estado que no permite la acción o secuencia agotada. |
422 | Error de validación en los datos. |
429 | Se superó el límite de peticiones. |
500 | Error nuestro. El mensaje incluye una referencia para soporte. |
Un rechazo de la DGII no es un error HTTP: el comprobante se crea con estado: "rechazado" y los motivos en mensajes_dgii.
{
"error": {
"codigo": "validacion",
"mensaje": "Revisa los datos del comprobante.",
"detalles": [
{ "campo": "comprador.rnc", "mensaje": "El RNC 131000000 no es válido." },
{ "campo": "lineas.0.precio", "mensaje": "El precio debe ser mayor que cero." }
]
}
}Límites y paginación
Cada clave admite 120 peticiones por minuto. Si las superas recibirás 429: espera unos segundos y reintenta con espera creciente.
Los listados se paginan con pagina (desde 1) y por_pagina (de 1 a 100, 25 por defecto), y devuelven los resultados en datos junto con la información de paginación.
{
"datos": [ { "id": "01JBZ8…", "encf": "E310000000149" } ],
"paginacion": { "pagina": 1, "por_pagina": 25, "total": 134, "paginas": 6 }
}Crear y emitir un comprobante
Valida los datos, calcula impuestos y totales, asigna el siguiente e-NCF de tu secuencia, firma el XML con tu certificado y lo envía a la DGII. La respuesta llega en segundos con el estado; si la DGII tarda, recibirás en_proceso y un webhook cuando se resuelva.
Cuerpo
tipoentero · obligatorio | Tipo de e-CF: 31, 32, 33, 34, 41, 43, 44, 45, 46 o 47. Ver catálogos. |
fecha_emisionfecha | AAAA-MM-DD. Por defecto, hoy. |
cliente_identero | Un cliente guardado. Alternativa a comprador. |
compradorobjeto | Obligatorio en 31, 33, 34, 41, 44, 45 y en 32 de RD$250,000 o más. Campos: rnc (RNC o cédula) o extranjero (identificador), razon_social, correo, direccion, municipio, provincia (códigos DGII), pais. |
lineasarreglo · obligatorio | De 1 a 1,000 líneas. Ver abajo. |
tipo_pagoentero · obligatorio | 1 contado, 2 crédito, 3 gratuito. |
fecha_limite_pagofecha | Obligatoria si tipo_pago es 2. |
formas_pagoarreglo | [{"forma": 1, "monto": 1180.00}]. Códigos en catálogos. |
monedatexto | Código ISO (DOP por defecto, USD, EUR…). |
tasa_cambiodecimal | Obligatoria si la moneda no es DOP. |
tipo_ingresostexto | 01 a 06. Por defecto 01 (operaciones). |
precios_con_itbisbooleano | Si es true, los precios de las líneas ya incluyen el ITBIS y se desglosa. |
referenciaobjeto | Obligatoria en 33 y 34: encf modificado, fecha, codigo de modificación (1 a 5) y razon. |
referencia_externatexto | Tu identificador (hasta 100 caracteres). |
notastexto | Nota visible en la representación impresa. |
Cada línea
nombretexto · obligatorio | Hasta 80 caracteres. |
descripciontexto | Hasta 1,000 caracteres. |
cantidaddecimal · obligatorio | Mayor que cero. |
preciodecimal · obligatorio | Precio unitario (sin ITBIS, salvo precios_con_itbis). |
indicadorentero · obligatorio | 0 no facturable, 1 ITBIS 18 %, 2 ITBIS 16 %, 3 ITBIS 0 %, 4 exento. |
tipoentero · obligatorio | 1 bien, 2 servicio. |
descuentodecimal | Monto de descuento de la línea. |
codigotexto | Tu código de producto (hasta 35 caracteres). |
unidadentero | Código de unidad de medida de la DGII (por ejemplo, 43 = unidad). |
retencion_itbis_pctdecimal | Porcentaje de ITBIS retenido por el comprador (por ejemplo, 30 o 100). |
retencion_isr_pctdecimal | Porcentaje de ISR retenido (por ejemplo, 2, 10 o 27). |
Parámetros de consulta
validarbooleano | ?validar=true hace una prueba en seco: valida y calcula sin consumir e-NCF ni enviar a la DGII. Ver validar. |
curl -X POST https://ecf.nodiqtech.com/api/v1/comprobantes \
-H "Authorization: Bearer nq_test_…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido-1001" \
-d '{
"tipo": 31,
"comprador": {
"rnc": "101010101",
"razon_social": "Ferretería Ozama, SRL",
"correo": "compras@ozama.com.do"
},
"tipo_pago": 2,
"fecha_limite_pago": "2026-12-05",
"lineas": [
{ "nombre": "Cemento gris 42.5 kg", "cantidad": 40,
"precio": 545.00, "indicador": 1, "tipo": 1, "unidad": 43 },
{ "nombre": "Transporte", "cantidad": 1,
"precio": 2500.00, "indicador": 1, "tipo": 2 }
],
"referencia_externa": "PED-1001"
}'$cuerpo = [
'tipo' => 31,
'comprador' => [
'rnc' => '101010101',
'razon_social' => 'Ferretería Ozama, SRL',
'correo' => 'compras@ozama.com.do',
],
'tipo_pago' => 2,
'fecha_limite_pago' => '2026-12-05',
'lineas' => [
['nombre' => 'Cemento gris 42.5 kg', 'cantidad' => 40, 'precio' => 545.00,
'indicador' => 1, 'tipo' => 1, 'unidad' => 43],
['nombre' => 'Transporte', 'cantidad' => 1, 'precio' => 2500.00,
'indicador' => 1, 'tipo' => 2],
],
'referencia_externa' => 'PED-1001',
];
$ch = curl_init('https://ecf.nodiqtech.com/api/v1/comprobantes');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NODIQ_CLAVE'),
'Content-Type: application/json',
'Idempotency-Key: pedido-1001',
],
CURLOPT_POSTFIELDS => json_encode($cuerpo),
]);
$r = json_decode(curl_exec($ch), true);
$http = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($http >= 400) {
throw new RuntimeException($r['error']['mensaje']);
}
echo $r['encf'], ' ', $r['estado']; // E310000000149 aceptadoconst r = await fetch('https://ecf.nodiqtech.com/api/v1/comprobantes', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NODIQ_CLAVE}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'pedido-1001',
},
body: JSON.stringify({
tipo: 31,
comprador: {
rnc: '101010101',
razon_social: 'Ferretería Ozama, SRL',
correo: 'compras@ozama.com.do',
},
tipo_pago: 2,
fecha_limite_pago: '2026-12-05',
lineas: [
{ nombre: 'Cemento gris 42.5 kg', cantidad: 40, precio: 545,
indicador: 1, tipo: 1, unidad: 43 },
{ nombre: 'Transporte', cantidad: 1, precio: 2500,
indicador: 1, tipo: 2 },
],
referencia_externa: 'PED-1001',
}),
});
const doc = await r.json();
if (!r.ok) throw new Error(doc.error.mensaje);
console.log(doc.encf, doc.estado); // E310000000149 aceptado{
"id": "01JBZ8Q3W6Y7N2K4R5T8V9X0AB",
"encf": "E310000000149",
"tipo": 31,
"estado": "aceptado",
"ambiente": "TesteCF",
"fecha_emision": "2026-11-05",
"comprador": { "rnc": "101010101", "razon_social": "Ferretería Ozama, SRL" },
"moneda": "DOP",
"totales": {
"gravado": 24300.00, "exento": 0.00, "itbis": 4374.00,
"total": 28674.00, "itbis_retenido": 0.00, "isr_retenido": 0.00
},
"codigo_seguridad": "a7Xk2P",
"fecha_firma": "2026-11-05 10:42:18",
"track_id": "4f3c9a1e-…",
"mensajes_dgii": [],
"url_pdf": "https://ecf.nodiqtech.com/c/9f2b…/pdf",
"url_xml": "https://ecf.nodiqtech.com/c/9f2b…/xml",
"url_publica": "https://ecf.nodiqtech.com/c/9f2b…",
"url_qr": "https://ecf.dgii.gov.do/testecf/consultatimbre?…",
"creado": "2026-11-05T10:42:17-04:00"
}Validar en seco
Recibe el mismo cuerpo que la creación, aplica todas las validaciones (incluido el esquema XSD de la DGII) y devuelve los totales calculados, sin asignar e-NCF, sin firmar y sin enviar. Úsalo para probar tu integración o para mostrar el total antes de confirmar una venta.
Si hay errores, responde 422 igual que la creación.
{
"valido": true,
"totales": {
"gravado": 24300.00, "exento": 0.00, "itbis": 4374.00,
"total": 28674.00, "itbis_retenido": 0.00, "isr_retenido": 0.00
},
"advertencias": []
}Listar comprobantes
estadotexto | Filtra por estado (ver objeto comprobante). |
tipoentero | Filtra por tipo de e-CF. |
desdefecha | Fecha de emisión inicial (incluida). |
hastafecha | Fecha de emisión final (incluida). |
paginaentero | Desde 1. |
por_paginaentero | De 1 a 100. Por defecto 25. |
Ordenado del más reciente al más antiguo.
curl "https://ecf.nodiqtech.com/api/v1/comprobantes?estado=rechazado&desde=2026-11-01&hasta=2026-11-30" \
-H "Authorization: Bearer nq_test_…"$q = http_build_query(['estado' => 'rechazado', 'desde' => '2026-11-01', 'hasta' => '2026-11-30']);
$ch = curl_init("https://ecf.nodiqtech.com/api/v1/comprobantes?$q");
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NODIQ_CLAVE')],
CURLOPT_RETURNTRANSFER => true,
]);
$lista = json_decode(curl_exec($ch), true)['datos'];const q = new URLSearchParams({ estado: 'rechazado', desde: '2026-11-01', hasta: '2026-11-30' });
const r = await fetch(`https://ecf.nodiqtech.com/api/v1/comprobantes?${q}`, {
headers: { Authorization: `Bearer ${process.env.NODIQ_CLAVE}` },
});
const { datos, paginacion } = await r.json();Obtener un comprobante
{id} es el identificador que devolvió la creación (no el e-NCF). Devuelve el objeto comprobante completo, con sus líneas.
Si recibiste en_proceso, puedes consultar aquí cada pocos segundos, aunque es mejor esperar el webhook documento.aceptado o documento.rechazado.
curl https://ecf.nodiqtech.com/api/v1/comprobantes/01JBZ8Q3W6Y7N2K4R5T8V9X0AB \
-H "Authorization: Bearer nq_test_…"Descargar PDF y XML
El PDF es la representación impresa con el código QR y el código de seguridad (application/pdf). El XML es el documento firmado tal como se envió a la DGII (application/xml). El nombre del archivo es el RNC del emisor seguido del e-NCF.
Para compartir con tu cliente sin exponer tu clave, usa url_publica, url_pdf o url_xml del objeto comprobante: son enlaces públicos y difíciles de adivinar.
curl https://ecf.nodiqtech.com/api/v1/comprobantes/01JBZ8Q3W6Y7N2K4R5T8V9X0AB/pdf \
-H "Authorization: Bearer nq_test_…" \
-o 101010101E310000000149.pdf$ch = curl_init('https://ecf.nodiqtech.com/api/v1/comprobantes/' . $id . '/pdf');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('NODIQ_CLAVE')],
CURLOPT_RETURNTRANSFER => true,
]);
file_put_contents('comprobante.pdf', curl_exec($ch));import { writeFile } from 'node:fs/promises';
const r = await fetch(`https://ecf.nodiqtech.com/api/v1/comprobantes/${id}/pdf`, {
headers: { Authorization: `Bearer ${process.env.NODIQ_CLAVE}` },
});
await writeFile('comprobante.pdf', Buffer.from(await r.arrayBuffer()));Reenviar a la DGII
Fuerza un nuevo intento de envío de un comprobante en contingencia o en_proceso, o un nuevo intento de firma de uno en error. No genera un e-NCF nuevo. No es necesario llamarlo: la plataforma reintenta sola.
Para corregir un comprobante rechazado emite uno nuevo; para anular uno aceptado, emite una nota de crédito (34) con referencia.codigo = 1.
curl -X POST https://ecf.nodiqtech.com/api/v1/comprobantes/01JBZ8Q3W6Y7N2K4R5T8V9X0AB/reenviar \
-H "Authorization: Bearer nq_test_…"Enviar por correo
paratexto · obligatorio | Uno o varios correos separados por coma. |
Envía al comprador un correo con el PDF y el XML adjuntos y el enlace público. Úsalo para reenviarlo o para mandarlo a otra dirección.
curl -X POST https://ecf.nodiqtech.com/api/v1/comprobantes/01JBZ8Q3W6Y7N2K4R5T8V9X0AB/correo \
-H "Authorization: Bearer nq_test_…" \
-H "Content-Type: application/json" \
-d '{"para": "compras@ozama.com.do"}'El objeto comprobante
idtexto | Identificador único (26 caracteres). |
encftexto | e-NCF asignado (13 caracteres). |
tipoentero | Tipo de e-CF. |
estadotexto | aceptado, aceptado_condicional, rechazado, en_proceso, contingencia, firmado, error o anulado. |
ambientetexto | Simulado, TesteCF, CerteCF o eCF. |
fecha_emisionfecha | |
compradorobjeto | Datos del comprador tal como se emitieron. |
monedatexto | Código ISO. |
totalesobjeto | gravado, exento, itbis, total, itbis_retenido, isr_retenido. |
lineasarreglo | Detalle con monto e ITBIS por línea. |
codigo_seguridadtexto | 6 caracteres de la firma; aparece en la representación impresa. |
fecha_firmafecha y hora | |
track_idtexto | Identificador de la DGII para el envío. |
mensajes_dgiiarreglo | Observaciones o motivos de rechazo de la DGII. |
url_pdfURL | Representación impresa pública. |
url_xmlURL | XML firmado público. |
url_publicaURL | Página del comprobante para tu cliente. |
url_qrURL | Consulta del timbre en la DGII (contenido del código QR). |
creadofecha y hora | ISO 8601. |
| Aceptado | Válido. La DGII lo aceptó. |
| Válido, con observaciones menores de la DGII. | |
| En proceso | Enviado; esperando la respuesta de la DGII. |
| Firmado y válido; la DGII no respondió y se reenvía solo. | |
| Rechazado | No válido. Corrige y emite uno nuevo. |
| Error | No se pudo firmar (por ejemplo, certificado vencido). No consumió validez fiscal. |
{
"tipo": 34,
"comprador": { "rnc": "101010101", "razon_social": "Ferretería Ozama, SRL" },
"tipo_pago": 1,
"lineas": [
{ "nombre": "Devolución total", "cantidad": 1,
"precio": 24300.00, "indicador": 1, "tipo": 1 }
],
"referencia": {
"encf": "E310000000149",
"fecha": "2026-11-05",
"codigo": 1,
"razon": "Devolución de mercancía"
}
}Catálogos y códigos
Tipos de e-CF
31 | Factura de Crédito Fiscal Electrónica |
32 | Factura de Consumo Electrónica |
33 | Nota de Débito Electrónica |
34 | Nota de Crédito Electrónica |
41 | Compras Electrónico |
43 | Gastos Menores Electrónico |
44 | Regímenes Especiales Electrónico |
45 | Gubernamental Electrónico |
46 | Comprobante para Exportaciones Electrónico |
47 | Comprobante para Pagos al Exterior Electrónico |
Formas de pago
1 | Efectivo |
2 | Cheque, transferencia o 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 |
Códigos de modificación (referencia)
1 | Anula el comprobante modificado |
2 | Corrige texto |
3 | Corrige montos |
4 | Reemplazo de NCF emitido en contingencia |
5 | Referencia a factura de consumo electrónica |
Tipo de ingresos
01 | Ingresos por operaciones (no financieros) |
02 | Ingresos financieros |
03 | Ingresos extraordinarios |
04 | Ingresos por arrendamientos |
05 | Ingresos por venta de activo depreciable |
06 | Otros ingresos |
Facturas de consumo y RFCE
Las facturas de consumo (32) menores de RD$250,000 se informan a la DGII mediante el resumen (RFCE) de forma automática. Para ti no cambia nada: llamas a la API igual y recibes el mismo objeto, con rfce: true.
Comprobantes recibidos
Los e-CF que tus proveedores te envían a tu dirección de recepción. Al recibirlos validamos la firma y los datos y enviamos el acuse de recibo automáticamente. Filtros: aprobacion (pendiente, aprobado, rechazado), desde, hasta, rnc_emisor, pagina, por_pagina.
Escucha el webhook recibido.nuevo para enterarte al instante.
curl "https://ecf.nodiqtech.com/api/v1/recibidos?aprobacion=pendiente" \
-H "Authorization: Bearer nq_live_…"{
"id": 8812,
"encf": "E310000004410",
"tipo": 31,
"emisor": { "rnc": "130000001", "razon_social": "Suplidora Caribe, SRL" },
"fecha_emision": "2026-11-04",
"totales": { "itbis": 1620.00, "total": 10620.00 },
"aprobacion": "pendiente",
"url_xml": "https://ecf.nodiqtech.com/api/v1/recibidos/8812?formato=xml",
"recibido": "2026-11-04T16:21:09-04:00"
}Aprobar o rechazar
motivotexto | Obligatorio al rechazar (hasta 250 caracteres). |
Envía la aprobación comercial al emisor y a la DGII. Solo se puede responder una vez por comprobante.
curl -X POST https://ecf.nodiqtech.com/api/v1/recibidos/8812/rechazar \
-H "Authorization: Bearer nq_live_…" \
-H "Content-Type: application/json" \
-d '{"motivo": "La mercancía no fue entregada."}'await fetch('https://ecf.nodiqtech.com/api/v1/recibidos/8812/rechazar', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.NODIQ_CLAVE}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ motivo: 'La mercancía no fue entregada.' }),
});Clientes
Guarda a tus compradores para emitir luego con cliente_id. El listado acepta q (busca por nombre o RNC), pagina y por_pagina. Al crear se aceptan los mismos campos que comprador; si envías un RNC, completamos la razón social desde el padrón de la DGII.
curl -X POST https://ecf.nodiqtech.com/api/v1/clientes \
-H "Authorization: Bearer nq_test_…" \
-H "Content-Type: application/json" \
-d '{"rnc": "101010101", "correo": "compras@ozama.com.do"}'Secuencias
Los rangos de e-NCF autorizados por la DGII para tu empresa, por tipo: desde, hasta, siguiente número, disponibles y fecha de vencimiento. Recibirás el webhook secuencia.por_agotarse cuando queden pocas.
{
"datos": [
{ "tipo": 31, "desde": 1, "hasta": 5000, "siguiente": 150,
"disponibles": 4851, "vence": "2027-12-31", "activa": true }
]
}Consultar un contribuyente
Busca un RNC o cédula en el padrón de contribuyentes de la DGII y te dice si es emisor electrónico (para saber si debes enviarle el e-CF por la vía electrónica).
{
"rnc": "101010101",
"razon_social": "FERRETERIA OZAMA SRL",
"nombre_comercial": "FERRETERIA OZAMA",
"estado": "ACTIVO",
"emisor_electronico": true
}Estado
Comprueba tu clave y la salud del servicio: ambiente de la clave, empresa, y estado de la plataforma y de la DGII. El mismo estado es público, sin clave, en /estado.json.
{
"ok": true,
"empresa": { "rnc": "131000001", "razon_social": "TU EMPRESA SRL" },
"ambiente": "TesteCF",
"dgii": "operativo"
}Webhooks
Registra una URL HTTPS en API y webhooks y elige los eventos. Te enviaremos un POST con JSON cada vez que ocurra uno. Disponibles en todos los planes.
documento.creado | Se creó y firmó un comprobante. |
documento.aceptado | La DGII lo aceptó. |
documento.aceptado_condicional | La DGII lo aceptó con observaciones. |
documento.rechazado | La DGII lo rechazó; los motivos van en mensajes_dgii. |
documento.anulado | Se anuló el comprobante o su secuencia. |
documento.aprobacion_comercial | Tu cliente aprobó o rechazó comercialmente tu comprobante. |
recibido.nuevo | Llegó un e-CF de un proveedor. |
secuencia.por_agotarse | Quedan pocos e-NCF de un tipo o la secuencia está por vencer. |
certificado.por_vencer | Tu certificado digital vence en menos de 30 días. |
Cabeceras
Nodiq-Eventocabecera | Nombre del evento, por ejemplo documento.aceptado. |
Nodiq-Entregacabecera | Identificador único de la entrega. Úsalo para ignorar duplicados. |
Nodiq-Firmacabecera | t=<unix>,v1=<hex>. Ver verificar la firma. |
Entrega y reintentos
Responde con un código 2xx en menos de 10 segundos (cortamos la conexión a los 15); procesa el trabajo pesado después. Si no, reintentamos 9 veces con espera creciente durante unas 72 horas. Puedes ver cada entrega y reintentarla desde el panel. Los eventos pueden llegar más de una vez o fuera de orden: usa Nodiq-Entrega y el estado del objeto.
POST /webhooks/nodiq HTTP/1.1
Content-Type: application/json
Nodiq-Evento: documento.aceptado
Nodiq-Entrega: 48213
Nodiq-Firma: t=1793896938,v1=5f1c0e…9ab2
{
"id": "evt_01JBZ9A1C2D3E4F5G6H7J8K9LM",
"evento": "documento.aceptado",
"creado": "2026-11-05T10:42:19-04:00",
"datos": {
"id": "01JBZ8Q3W6Y7N2K4R5T8V9X0AB",
"encf": "E310000000149",
"estado": "aceptado",
"referencia_externa": "PED-1001",
"totales": { "itbis": 4374.00, "total": 28674.00 }
}
}Verificar la firma
Cada webhook va firmado con el secreto que te mostramos al crearlo (empieza por whsec_). Verifícalo siempre antes de confiar en el contenido:
- Lee
tyv1de la cabeceraNodiq-Firma. - Calcula
HMAC-SHA256(secreto, t + "." + cuerpo)en hexadecimal, usando el cuerpo crudo, sin volver a codificar el JSON. - Compáralo con
v1en tiempo constante. - Rechaza si
tdifiere de la hora actual en más de 5 minutos, para evitar reenvíos maliciosos.
$cuerpo = file_get_contents('php://input');
$firma = $_SERVER['HTTP_NODIQ_FIRMA'] ?? '';
$secreto = getenv('NODIQ_WEBHOOK_SECRETO');
parse_str(str_replace(',', '&', $firma), $p); // ['t' => …, 'v1' => …]
$esperada = hash_hmac('sha256', ($p['t'] ?? '') . '.' . $cuerpo, $secreto);
if (empty($p['v1']) || !hash_equals($esperada, $p['v1'])
|| abs(time() - (int) $p['t']) > 300) {
http_response_code(400);
exit('Firma inválida');
}
$evento = json_decode($cuerpo, true);
// Encola el trabajo y responde rápido.
http_response_code(200);import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post('/webhooks/nodiq', express.raw({ type: 'application/json' }), (req, res) => {
const p = Object.fromEntries(
(req.get('Nodiq-Firma') || '').split(',').map((x) => x.split('=')),
);
const esperada = crypto
.createHmac('sha256', process.env.NODIQ_WEBHOOK_SECRETO)
.update(`${p.t}.${req.body}`)
.digest('hex');
const ok = p.v1 && p.v1.length === esperada.length &&
crypto.timingSafeEqual(Buffer.from(p.v1), Buffer.from(esperada)) &&
Math.abs(Date.now() / 1000 - Number(p.t)) <= 300;
if (!ok) return res.status(400).send('Firma inválida');
const evento = JSON.parse(req.body);
res.sendStatus(200); // procesa después
});Servicios de recepción para la DGII
Para ser emisor electrónico, la DGII te pide declarar las direcciones donde recibirás e-CF de otros emisores y sus aprobaciones comerciales. Con Nodiq e-CF declaras estas direcciones; son compartidas por todos nuestros clientes y la plataforma enruta cada documento por el RNC del comprador:
| Recepción | https://ecf.nodiqtech.com/fe/recepcion/api/ecf |
| Aprobación comercial | https://ecf.nodiqtech.com/fe/aprobacioncomercial/api/ecf |
| Autenticación (semilla) | https://ecf.nodiqtech.com/fe/autenticacion/api/semilla |
| Validación de certificado | https://ecf.nodiqtech.com/fe/autenticacion/api/validacioncertificado |
Estas direcciones las consumen la DGII y otros emisores siguiendo el protocolo de la DGII (semilla firmada y token); no usan tu clave de API. En el panel, en Puesta en marcha, encontrarás también una variante con un identificador propio de tu empresa por si la necesitas.
Emisor externo ──► /fe/autenticacion/api/semilla
──► /fe/autenticacion/api/validacioncertificado (token)
──► /fe/recepcion/api/ecf (e-CF firmado)
│
├─ valida firma y RNC del comprador
├─ responde el acuse de recibo (ARECF)
└─ webhook recibido.nuevo ──► tu sistema
Tu aprobación ──► POST /api/v1/recibidos/{id}/aprobar
──► ACECF al emisor y a la DGIIOpenAPI, llms.txt y soporte
La especificación completa está en /openapi.json (OpenAPI 3.1): impórtala en Postman, Insomnia o tu generador de clientes favorito para crear tu propio SDK. Para asistentes de IA y agentes, hay un resumen en texto en /llms.txt.
¿Dudas con la integración? Escríbenos a soporte@nodiqtech.com o por WhatsApp; te ayudamos a revisar tus peticiones.