Escríbenos
API de emisión · v1

Emite e-CF desde tu sistema con un solo POST

Tu sistema manda la factura en JSON. ncf.do asigna el e-NCF, la firma con el certificado de tu empresa, la entrega a la DGII y al comprador, y te devuelve al instante todo lo que necesitas para imprimirla.

Los 10 tipos de e-CFAmbiente de pruebas con la DGIISin certificados en tu servidor
POST https://ncf.do/api/v1/ecf → 201 Created
{
  "encf": "E310009100101",
  "codigoSeguridad": "TbtHcs",
  "fechaHoraFirma": "30-10-2026 21:04:00",
  "montoTotal": 2950.00,
  "urlTimbre": "https://ecf.dgii.gov.do/…",
  "dgii": { "estado": "Pendiente" },
  "comprador": { "estado": "Pendiente" }
}
01

Inicio rápido

En tres pasos emites tu primera factura en el ambiente de pruebas. Sin firmar XML, sin certificados en tu servidor y sin hablar con los servicios de la DGII.

1

Prepara tu empresa

En el portal sube tu certificado digital y carga los rangos de e-NCF en Emisión → Secuencias.

2

Crea una clave de API

En Emisión → Claves de API. Se muestra una sola vez: guárdala como secreto.

3

Envía tu factura

Un POST /api/v1/ecf con el JSON. La respuesta trae el e-NCF y el QR.

Tu primera factura en pruebas
curl -X POST https://test.ncf.do/api/v1/ecf \
  -H "Authorization: Bearer $NCF_CLAVE" \
  -H "Idempotency-Key: FACT-000123" \
  -H "Content-Type: application/json" \
  -d @factura.json
02

Cómo funciona

Tu sistema solo habla con ncf.do. ncf.do se encarga de la DGII y del comprador, y reintenta solo si algo falla.

  1. Asigna el e-NCF del rango que la DGII autorizó a tu empresa.
  2. Arma el XML, lo valida contra el formato oficial de la DGII y lo firma con el certificado digital de tu empresa.
  3. Lo entrega a la DGII y consulta el resultado. La factura de consumo menor de RD$250,000 se envía como resumen (RFCE), como pide la DGII.
  4. Lo entrega al comprador si es receptor electrónico y guarda el acuse de recibo que devuelve.
03

Ambientes

Integra primero en pruebas. Para pasar a producción cambias solo la URL base y la clave de API: el JSON y las respuestas son iguales.

AmbienteURL baseAmbiente de la DGIIValidez fiscal
Pruebashttps://test.ncf.doTesteCF (pre-certificación)No
Producciónhttps://ncf.doeCF (producción)Sí

En cada ambiente, un administrador de tu empresa deja listo en el portal (/portal):

  1. Certificado digital vigente, en Certificado digital. El titular debe estar delegado para tu empresa en la Oficina Virtual de la DGII, con permiso para emitir.
  2. Secuencias: los rangos de e-NCF que autorizó la DGII, en Emisión → Secuencias, escritos exactamente como los autorizó. Los tipos 32 y 34 no llevan fecha de vencimiento.
  3. Clave de API, en Emisión → Claves de API. Crea una por sistema para poder revocarlas por separado.

En pruebas, la DGII autoriza a cada empresa habilitada los e-NCF del 1 al 10,000,000 por tipo (la 32, hasta 50,000,000), con vencimiento 31-12-2028. Por ejemplo, E310000000001 a E310000001000. En producción, solo los rangos que la DGII autorizó a tu empresa.

04

Autenticación

Cada petición lleva la clave de API en el encabezado Authorization. La clave identifica a tu empresa, así que ninguna ruta lleva el RNC. También se acepta X-Api-Key.

Encabezado
Authorization: Bearer ncf_XXXXXXXXXXXX_YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
CódigoCuándo
401Falta la clave, no existe, fue revocada o la empresa está inactiva.
403«Servicio suspendido»: el servicio de tu empresa está suspendido. Comunícate con ncf.do para reactivarlo.
429Más de 300 peticiones por minuto con la misma clave. Espera lo que indica Retry-After.

Guarda la clave como secreto (variable de entorno o almacén de secretos), nunca en el código ni en el navegador. Si se expone, revócala en el portal: deja de funcionar al instante.

05

Emitir un e-CF

POST/api/v1/ecf

Un POST por factura. La respuesta llega al instante con el e-NCF asignado y los datos para imprimir. La entrega a la DGII sigue en segundo plano.

Petición
POST /api/v1/ecf
Authorization: Bearer ncf_…
Idempotency-Key: FACT-000123
Content-Type: application/json

{ "ecf": { "Encabezado": { … }, "DetallesItems": { "Item": [ … ] } } }

El contenido de ecf

ecf es el elemento ECF del Formato e-CF v1.0 de la DGII, escrito en JSON. Sirve para los diez tipos: 31, 32, 33, 34, 41, 43, 44, 45, 46 y 47.

  • Mismos nombres de campo que el XML de la DGII (TipoeCF, RNCComprador, MontoGravadoI1…), sin distinguir mayúsculas. El orden no importa: lo pone ncf.do.
  • Mismos valores y códigos que el XML: fechas dd-MM-yyyy y códigos de la DGII (TipoIngresos: "01", TipoPago: 1). No hay true/false: van los códigos.
  • Montos como texto ("1180.00") para conservar los decimales exactos; también se aceptan números.
  • Lo que se repite va como lista: "Item": [ … ], "FormaDePago": [ … ]. Si hay uno solo, puede ir como objeto.
  • Lo que no aplica se omite: la DGII no admite etiquetas vacías. Un campo vacío o null cuenta como omitido.
  • Tú mandas los montos y totales. ncf.do no los recalcula; la DGII valida los cálculos y rechaza el e-CF si no cuadran.
  • Factura de consumo (32) desde RD$250,000: identifica al comprador con RNCComprador (o IdentificadorExtranjero si es extranjero sin RNC ni cédula) y RazonSocialComprador.

Campos que pone ncf.do y no debes enviar (si vienen, la respuesta es 400):

CampoLo pone ncf.do con
Encabezado.IdDoc.eNCFEl siguiente número del rango activo del tipo que vence primero.
Encabezado.IdDoc.FechaVencimientoSecuenciaLa fecha de vencimiento de ese rango (32 y 34 no la llevan).
FechaHoraFirmaEl momento de la firma, en hora de República Dominicana.

Encabezado.Version y Encabezado.Emisor.RNCEmisor son opcionales; si los envías, deben ser 1.0 y el RNC de tu empresa.

Idempotency-Key, obligatoria

Es el identificador único de la factura en tu sistema, por ejemplo su número interno: hasta 100 caracteres visibles, sin espacios.

  • Si repites la petición con la misma clave y el mismo contenido, recibes la misma factura (200 y el encabezado Idempotent-Replayed: true). No se gasta otro e-NCF.
  • La misma clave con otro contenido responde 409.
  • Ante un error de red o un tiempo de espera agotado, repite siempre con la misma clave. Nunca generes una nueva para reintentar.

La respuesta

201 Created, con el encabezado Location: /api/v1/ecf/{id}. Caso real del ambiente de pruebas:

201 Created
{
  "id": "aa81bdd3-6f78-4e09-9f9a-993b6d30170e",
  "encf": "E310009100101",
  "tipoEcf": 31,
  "tipoNombre": "Factura de Crédito Fiscal Electrónica",
  "fechaEmision": "2026-10-30",
  "fechaHoraFirma": "30-10-2026 21:04:00",
  "codigoSeguridad": "TbtHcs",
  "montoTotal": 2950.00,
  "totalItbis": 450.00,
  "rncComprador": "131880681",
  "razonSocialComprador": "DOCUMENTOS ELECTRONICOS DE 03",
  "resumen": false,
  "urlTimbre": "https://ecf.dgii.gov.do/testecf/consultatimbre?rncemisor=130964874&rnccomprador=131880681&encf=E310009100101&fechaemision=30-10-2026&montototal=2950.00&fechafirma=30-10-2026%2021%3A04%3A00&codigoseguridad=TbtHcs",
  "dgii": { "estado": "Pendiente", "trackId": null, "mensajes": [], "secuenciaUtilizada": null, "intentos": 0, "enviadoEn": null, "finalizadoEn": null },
  "comprador": { "estado": "Pendiente", "url": null, "intentos": 0, "error": null, "motivoNoRecibido": null, "entregadoEn": null },
  "aprobacionComercial": null,
  "creadoEn": "2026-10-30T21:04:00-04:00"
}
CampoPara qué te sirve
idConsultar la factura después. Guárdalo junto a tu factura.
encf, codigoSeguridad, fechaHoraFirmaVan en la factura impresa.
urlTimbreEl contenido del código QR.
resumentrue si es una factura de consumo menor de RD$250,000 (se envió como RFCE).
dgiiEl estado ante la DGII. En la respuesta del POST siempre viene Pendiente.
compradorLa entrega al receptor electrónico del comprador.
aprobacionComercialLa aprobación o el rechazo comercial del comprador, cuando llegue.
06

Consultar el resultado

La DGII suele responder en menos de 10 segundos. Consulta la factura con su id y mira dgii.estado; el formato es el mismo de la respuesta del POST.

RutaQué devuelve
GET /api/v1/ecf/{id}La factura con su estado actual.
GET /api/v1/ecf?encf=E310000000037La factura de ese e-NCF.
GET /api/v1/ecf?desde=2026-10-01&hasta=2026-10-31&tipo=31&estado=Rechazado&pagina=1&tamano=25Una página de facturas, las más recientes primero: { total, pagina, tamano, items }. Todos los filtros son opcionales; buscar filtra por comprador.

Si la factura no existe, la respuesta es 404.

Estado ante la DGII

dgii.estadoQué significaQué hace tu sistema
PendienteFirmada; ncf.do la está entregando a la DGII y reintenta si no responde.Esperar y volver a consultar.
EnProcesoLa DGII la recibió (trackId) y la está validando.Esperar y volver a consultar.
AceptadoVálida.Marcarla como aceptada.
AceptadoCondicionalVálida, con observaciones en mensajes.Marcarla como aceptada y revisar los mensajes.
RechazadoSin valor fiscal; el motivo está en mensajes.Corregir y emitir una factura nueva.
ErrorNo se pudo entregar ni confirmar en 72 horas, el plazo de contingencia de la DGII.Avisar a soporte.
  • Los mensajes de la DGII vienen como "código texto", por ejemplo "135 Número de secuencia no autorizada".
  • En un rechazo, secuenciaUtilizada: false significa que la DGII no dio por usado ese e-NCF.
  • La factura de consumo menor de RD$250,000 (resumen: true) pasa de Pendiente a su resultado sin trackId: la DGII valida el RFCE en el acto.

Entrega al comprador

comprador.estadoQué significa
PendienteSe envía cuando la DGII la acepta; si falla, ncf.do reintenta durante unas horas.
RecibidoEl comprador devolvió su acuse de recibo (descárgalo en /acuse).
NoRecibidoEl comprador respondió que no la recibió. Motivo en motivoNoRecibido: 1 especificación, 2 firma, 3 duplicado, 4 RNC no corresponde.
NoAplicaNo hay a quién enviarla: tipos 43 y 47, consumo menor de RD$250,000, o el comprador no es receptor electrónico.
ErrorNo se pudo entregar después de los reintentos.
CanceladaNo se envió porque la DGII no la aceptó.

Si tu empresa recibe sus e-CF en ncf.do, la aprobación comercial que mande el comprador aparece en aprobacionComercial: { estado: "Aprobado" | "Rechazado", fechaHora, motivo, recibidaEn }.

Cada cuánto consultar

No hay avisos automáticos (webhooks). Este ritmo basta:

  1. Consulta a los 5 segundos de emitir.
  2. Si sigue Pendiente o EnProceso, consulta cada 30 segundos durante 5 minutos.
  3. Después, revisa cada 15 minutos las que sigan sin resultado final, hasta 72 horas.
07

Representación impresa

Tu sistema imprime la factura con sus propios datos más cuatro que vienen en la respuesta del POST. No hace falta esperar a la DGII para imprimir.

En la factura impresaCampo de la respuesta
e-NCFencf
Código de seguridadcodigoSeguridad (6 caracteres)
Fecha de firma digitalfechaHoraFirma
Código QR, abajo a la izquierda, de al menos 22 × 22 mmurlTimbre, tal cual
  • Si resumen es true, urlTimbre ya apunta a la consulta del RFCE (consultatimbrefc), como pide la DGII.
  • El QR abre la verificación de la DGII; cuando la DGII acepta la factura, esa página muestra «Estado Aceptado».
  • La DGII indica la versión 8 del QR; la URL de un e-CF completo no cabe en ella, así que usa la menor versión en que quepa.
  • Si tu sistema no puede imprimir una factura, en el portal la imprimes con su QR: Emitidos → la factura → Imprimir.
08

Documentos y secuencias

RutaQué devuelve
GET /api/v1/ecf/{id}/xmlEl e-CF firmado: lo que recibieron la DGII y el comprador.
GET /api/v1/ecf/{id}/rfceEl resumen RFCE firmado (solo consumo menor de RD$250,000).
GET /api/v1/ecf/{id}/acuseEl acuse de recibo (ARECF) que devolvió el comprador.
GET /api/v1/secuenciasLos rangos cargados con el siguiente e-NCF, cuántos quedan, el vencimiento y el estado.

ncf.do guarda estos documentos por ti; descárgalos si tu sistema también debe conservarlos. Usa /api/v1/secuencias para avisar antes de que un rango se agote o venza: sin un rango activo y vigente del tipo, POST /api/v1/ecf responde 409.

GET /api/v1/secuencias
[
  { "id": 1, "tipoEcf": 31, "desde": "E310000000001", "hasta": "E310000001000", "siguiente": "E310000000038",
    "usados": 37, "disponibles": 963, "fechaVencimiento": "2028-12-31", "estado": "Activa" }
]
09

Anular e-NCF que no usarás

POST/api/v1/anulaciones

Anula ante la DGII los números que no vas a usar: un rango vencido o el sobrante de uno que dejas de usar. ncf.do arma el documento de anulación (ANECF), lo firma, lo envía y responde el resultado de la DGII.

POST /api/v1/anulaciones
{ "rangos": [ { "desde": "E310000000038", "hasta": "E310000000500" } ] }
  • estado: Aceptada; Rechazada, cuando la DGII dijo que no (motivo en mensajes); o Error, cuando no se pudo entregar y hay que repetir la petición.
  • No se puede anular un e-NCF ya emitido (400).
  • De un rango cargado en ncf.do solo se anula lo que falta por asignar completo, desde el siguiente hasta el final. Si la DGII acepta, ese rango deja de asignar esos números.
10

Errores

Todo error llega como application/problem+json con title, status y detail. En los 400, errors dice qué campo falla con su ruta en tu JSON, para mostrarlo junto al dato que lo causó. Un error en los datos no gasta ningún e-NCF.

400 Bad Request
{
  "title": "Revisa los datos",
  "status": 400,
  "detail": "Es obligatorio.",
  "errors": {
    "ecf.Encabezado.IdDoc.TipoPago": ["Es obligatorio."],
    "ecf.Encabezado.IdDoc.TipoIngresos": ["«99» no es un valor válido para este campo. Valores permitidos: 01, 02, 03, 04, 05, 06."],
    "ecf.DetallesItems.Item[1].CantidadItem": ["«-1» no es un valor válido para este campo (formato Decimal18D1or2ValidationTypeMayorCero del XSD de la DGII)."],
    "ecf.Encabezado.Emisor.Telefono": ["Este campo no existe en el formato de la DGII para este documento."]
  }
}
CódigoCuándoQué hacer
200Repetiste la misma Idempotency-Key con el mismo contenido.Usar la factura que devuelve.
400JSON inválido, falta Idempotency-Key, un campo no cumple el formato de la DGII o enviaste un campo que pone ncf.do.Corregir los campos de errors y reenviar.
401Clave de API ausente, revocada o inválida.Revisar la clave.
403El servicio de la empresa está suspendido («Servicio suspendido»).Comunicarse con ncf.do.
404El id o el e-NCF no existe.Revisar el identificador.
409No quedan e-NCF del tipo, el certificado no está vigente o la Idempotency-Key ya se usó con otro contenido.Ver detail; el administrador carga un rango o el certificado en el portal.
429Más de 300 peticiones por minuto.Esperar Retry-After y reintentar.
5xx o sin respuestaFalla temporal.Reintentar con la misma Idempotency-Key.
11

Ejemplos por tipo

Estos cuatro casos fueron aceptados por la DGII en el ambiente de pruebas. Cambia los datos del emisor por los de tu empresa. El comprador 131880681 es el contribuyente de prueba de la DGII y solo sirve en pruebas.

factura-31.json
{
  "ecf": {
    "Encabezado": {
      "IdDoc": { "TipoeCF": 31, "IndicadorMontoGravado": 0, "TipoIngresos": "01", "TipoPago": 2, "FechaLimitePago": "29-11-2026", "TerminoPago": "30 días" },
      "Emisor": {
        "RazonSocialEmisor": "Tu Empresa SRL",
        "NombreComercial": "Tu Empresa",
        "DireccionEmisor": "Av. Winston Churchill 1099, Santo Domingo",
        "NumeroFacturaInterna": "FACT-000123",
        "FechaEmision": "30-10-2026"
      },
      "Comprador": { "RNCComprador": "131880681", "RazonSocialComprador": "DOCUMENTOS ELECTRONICOS DE 03" },
      "Totales": { "MontoGravadoTotal": "2500.00", "MontoGravadoI1": "2500.00", "ITBIS1": 18, "TotalITBIS": "450.00", "TotalITBIS1": "450.00", "MontoTotal": "2950.00" }
    },
    "DetallesItems": {
      "Item": [
        { "NumeroLinea": 1, "IndicadorFacturacion": 1, "NombreItem": "Servicio de mantenimiento preventivo", "IndicadorBienoServicio": 2, "CantidadItem": 1, "PrecioUnitarioItem": "1500.00", "MontoItem": "1500.00" },
        { "NumeroLinea": 2, "IndicadorFacturacion": 1, "NombreItem": "Filtro de aire", "IndicadorBienoServicio": 1, "CantidadItem": 2, "PrecioUnitarioItem": "500.00", "MontoItem": "1000.00" }
      ]
    }
  }
}
12

Código

Emitir y leer la respuesta en los lenguajes más comunes. Los nombres de campo no distinguen mayúsculas, así que el camelCase que generan muchas librerías también sirve.

.NET 6 o posterior
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

var http = new HttpClient { BaseAddress = new Uri("https://test.ncf.do") };
http.DefaultRequestHeaders.Authorization =
    new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("NCF_CLAVE"));

var factura = new
{
    ecf = new
    {
        Encabezado = new
        {
            IdDoc = new { TipoeCF = 31, IndicadorMontoGravado = 0, TipoIngresos = "01", TipoPago = 1 },
            Emisor = new { RazonSocialEmisor = "Tu Empresa SRL", DireccionEmisor = "Av. Winston Churchill 1099, Santo Domingo", FechaEmision = "30-10-2026" },
            Comprador = new { RNCComprador = "131880681", RazonSocialComprador = "DOCUMENTOS ELECTRONICOS DE 03" },
            Totales = new { MontoGravadoTotal = "1000.00", MontoGravadoI1 = "1000.00", ITBIS1 = 18, TotalITBIS = "180.00", TotalITBIS1 = "180.00", MontoTotal = "1180.00" },
        },
        DetallesItems = new
        {
            Item = new[] { new { NumeroLinea = 1, IndicadorFacturacion = 1, NombreItem = "Servicio", IndicadorBienoServicio = 2, CantidadItem = 1, PrecioUnitarioItem = "1000.00", MontoItem = "1000.00" } },
        },
    },
};

using var pedido = new HttpRequestMessage(HttpMethod.Post, "/api/v1/ecf") { Content = JsonContent.Create(factura) };
pedido.Headers.Add("Idempotency-Key", "FACT-000123"); // el número de la factura en tu sistema
using var respuesta = await http.SendAsync(pedido);
var cuerpo = await respuesta.Content.ReadAsStringAsync();
if (!respuesta.IsSuccessStatusCode)
    throw new InvalidOperationException($"ncf.do respondió {(int)respuesta.StatusCode}: {cuerpo}");

using var json = JsonDocument.Parse(cuerpo);
var id = json.RootElement.GetProperty("id").GetString();         // guárdalo para consultar
var encf = json.RootElement.GetProperty("encf").GetString();     // a la factura impresa
var qr = json.RootElement.GetProperty("urlTimbre").GetString();  // contenido del QR
13

Lista para pasar a producción

Cuando todo esto funcione en pruebas, cambia la URL base a https://ncf.do y usa una clave de API de producción.

  • Emitiste en pruebas los tipos que usa tu negocio y la DGII los aceptó.
  • Tu sistema guarda el id y el encf de cada factura.
  • La factura impresa lleva el e-NCF, el código de seguridad, la fecha de firma y el QR con urlTimbre.
  • Muestras los errores de errors junto al campo que los causó.
  • Ante errores de red o respuestas 5xx, reintentas con la misma Idempotency-Key.
  • Consultas el resultado de la DGII y avisas cuando una factura queda Rechazado o Error.
  • Revisas las secuencias y avisas antes de que se agoten o venzan.
  • La clave de API de producción está guardada como secreto, fuera del código.
  • En el portal de producción están el certificado vigente y los rangos que autorizó la DGII.

¿Listo para integrar?

Escríbenos y te damos acceso al ambiente de pruebas. Para dudas de una factura, incluye su id o su e-NCF.

Escríbenos