Emite e-CF programáticamente en el ambiente de pruebas de DGII (TesteCF) usando tu API Key.
Todas las llamadas requieren el header X-API-Key con tu llave (se genera desde tu panel, próximamente disponible en el dashboard).
| POST | /v1/emitir-xml | Firma y envía un e-CF a DGII |
| POST | /v1/consultar | Consulta el resultado de un envío por track_id |
| GET | /v1/proximo-encf | Obtiene el siguiente número de secuencia (uso manual — no hace falta si dejas que /v1/emitir-xml lo genere solo) |
| GET | /v1/secuencias | Lista tus rangos de e-NCF registrados, con estadísticas de uso |
| POST | /v1/secuencias | Registra un rango de e-NCF autorizado por DGII |
| PUT | /v1/secuencias | Activa/desactiva un rango o ajusta su contador |
| DELETE | /v1/secuencias | Elimina un rango inactivo |
| GET | /v1/estado-certificado | Estado y vencimiento de tu certificado |
| GET | /v1/documentos | Lista los documentos emitidos |
Cada tipo de comprobante tiene campos y reglas específicas según DGII.
NCFModificado dentro de InformacionReferencia —
que va fuera de Encabezado, como hermano de
Encabezado y DetallesItems a nivel raíz de
ECF (confirmado probando en vivo contra DGII — colocarlo dentro de
Encabezado lo rechaza). Además, RazonModificacion
es obligatorio.
Los 11 formatos de esta guía (E32 en sus dos variantes) fueron probados en vivo
contra el sandbox TesteCF de DGII a través de nuestra propia API — todos pasaron la validación estructural
completa (XSD) y recibieron un trackId real. Ninguno llegó a "Aceptado" porque
se probaron con el tenant demo (RNC ficticio, sin delegación real en DGII) — con un tenant real y certificado
delegado, el mismo flujo termina en "Aceptado" tal como viste antes con la respuesta de ejemplo.
| Tipo | Estado | Detalle |
|---|---|---|
| E31 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E32 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 (2 variantes: < y ≥ RD$250K) |
| E33 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E34 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E41 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E43 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E44 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E45 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E46 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
| E47 | Estructura válida | HTTP 200, trackId real de DGII, verificado en vivo el 17-08-2026 |
Haga clic en cada tipo para ver el JSON completo que debe enviar (dentro de xml_sin_firmar ya convertido, o como referencia para construir su propio XML) a POST /v1/emitir-xml.
Ventas entre empresas con RNC. Permite deducción de ITBIS al comprador.
Pruebe la API directamente desde el navegador. Complete los campos y envíe el comprobante.
eNCF no hace falta incluirlo — se genera solo
desde el rango de secuencia que tengas registrado y activo
para ese tipo/entorno. FechaVencimientoSecuencia debe coincidir con la fecha
de vencimiento de ese rango (déjela como venga en la plantilla si usa el tenant demo).
Cómo conectar tu propio software de facturación (ERP, POS, sistema a medida) para que emita e-CF usando nuestra API como proveedor electrónico.
X-API-Key autentica cada llamada directamente, sin necesidad de intercambiar un usuario/contraseña por un token primero.
POST /v1/secuencias. Sin un rango activo para ese tipo/entorno,
/v1/emitir-xml devuelve un 400 ("Sin secuencia configurada").
Seleccione su lenguaje de programación para ver el ejemplo completo de emisión de un e-CF E31 (Crédito Fiscal).
#!/bin/bash
# ============================================
# e-CF SaaS API - Ejemplo completo con cURL
# ============================================
BASE_URL="https://cert.ocoasertec.com"
API_KEY="sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
XML='<ECF xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><Encabezado><Version>1.0</Version><IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia><IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc><Emisor><RNCEmisor>000000001</RNCEmisor><RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor><DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia><FechaEmision>'$(date +%d-%m-%Y)'</FechaEmision></Emisor><Comprador><RNCComprador>131703836</RNCComprador><RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador><DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador><ProvinciaComprador>10116</ProvinciaComprador></Comprador><Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1><ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1><MontoTotal>1770.00</MontoTotal></Totales></Encabezado><DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion><NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio><CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem><MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>'
# eNCF es opcional: si no se envia, el servidor lo genera automaticamente
# desde tu secuencia registrada en DGII (requiere jq instalado)
curl -s -X POST "$BASE_URL/v1/emitir-xml" \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d "$(jq -n --arg xml "$XML" '{xml_sin_firmar: $xml, entorno: "TesteCF"}')" \
| python3 -m json.tool
# Respuesta exitosa:
# {
# "exito": true,
# "encf": "E310000000012",
# "estado": "Aceptado",
# "track_id": "...",
# "ri_pdf_url": "/v1/documentos/E310000000012/ri",
# "xml_firmado_url": "/v1/documentos/E310000000012/xml"
# }
<?php
$baseUrl = "https://cert.ocoasertec.com";
$apiKey = "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8";
$fecha = date('d-m-Y');
$xml = '<ECF xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">'
. '<Encabezado><Version>1.0</Version>'
. '<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>'
. '<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>'
. '<Emisor><RNCEmisor>000000001</RNCEmisor>'
. '<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>'
. '<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>'
. "<FechaEmision>$fecha</FechaEmision></Emisor>"
. '<Comprador><RNCComprador>131703836</RNCComprador>'
. '<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>'
. '<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>'
. '<ProvinciaComprador>10116</ProvinciaComprador></Comprador>'
. '<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>'
. '<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>'
. '<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>'
. '<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>'
. '<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>'
. '<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>'
. '<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>';
// eNCF es opcional: si no se envia, el servidor lo genera automaticamente
$ch = curl_init("$baseUrl/v1/emitir-xml");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ["Content-Type: application/json", "X-API-Key: $apiKey"],
CURLOPT_POSTFIELDS => json_encode(["xml_sin_firmar" => $xml, "entorno" => "TesteCF"]),
]);
$respuesta = json_decode(curl_exec($ch), true);
print_r($respuesta);
// Respuesta esperada:
// [ "exito" => true, "encf" => "E310000000012", "estado" => "Aceptado", ... ]
import requests
from datetime import datetime
BASE_URL = "https://cert.ocoasertec.com"
API_KEY = "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
fecha = datetime.now().strftime("%d-%m-%Y")
xml = f"""<ECF xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Encabezado><Version>1.0</Version>
<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>
<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>
<Emisor><RNCEmisor>000000001</RNCEmisor>
<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>
<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>
<FechaEmision>{fecha}</FechaEmision></Emisor>
<Comprador><RNCComprador>131703836</RNCComprador>
<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>
<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>
<ProvinciaComprador>10116</ProvinciaComprador></Comprador>
<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>
<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>
<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>
<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>
<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>
<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>
<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>"""
# eNCF es opcional: si no se envia, el servidor lo genera automaticamente
resp = requests.post(
f"{BASE_URL}/v1/emitir-xml",
headers={"X-API-Key": API_KEY, "Content-Type": "application/json"},
json={"xml_sin_firmar": xml, "entorno": "TesteCF"},
)
print(resp.json())
# Respuesta esperada:
# {'exito': True, 'encf': 'E310000000012', 'estado': 'Aceptado', ...}
const BASE_URL = "https://cert.ocoasertec.com";
const API_KEY = "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8";
const pad = n => String(n).padStart(2, '0');
const hoy = new Date();
const fecha = `${pad(hoy.getDate())}-${pad(hoy.getMonth()+1)}-${hoy.getFullYear()}`;
const xml = `<ECF xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Encabezado><Version>1.0</Version>
<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>
<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>
<Emisor><RNCEmisor>000000001</RNCEmisor>
<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>
<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>
<FechaEmision>${fecha}</FechaEmision></Emisor>
<Comprador><RNCComprador>131703836</RNCComprador>
<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>
<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>
<ProvinciaComprador>10116</ProvinciaComprador></Comprador>
<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>
<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>
<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>
<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>
<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>
<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>
<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>`;
// eNCF es opcional: si no se envia, el servidor lo genera automaticamente
const resp = await fetch(`${BASE_URL}/v1/emitir-xml`, {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({ xml_sin_firmar: xml, entorno: 'TesteCF' }),
});
console.log(await resp.json());
// Respuesta esperada:
// { exito: true, encf: 'E310000000012', estado: 'Aceptado', ... }
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
var baseUrl = "https://cert.ocoasertec.com";
var apiKey = "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8";
var fecha = DateTime.Now.ToString("dd-MM-yyyy");
var xml = $@"<ECF xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">
<Encabezado><Version>1.0</Version>
<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>
<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>
<Emisor><RNCEmisor>000000001</RNCEmisor>
<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>
<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>
<FechaEmision>{fecha}</FechaEmision></Emisor>
<Comprador><RNCComprador>131703836</RNCComprador>
<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>
<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>
<ProvinciaComprador>10116</ProvinciaComprador></Comprador>
<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>
<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>
<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>
<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>
<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>
<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>
<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>";
// eNCF es opcional: si no se envia, el servidor lo genera automaticamente
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("X-API-Key", apiKey);
var body = JsonSerializer.Serialize(new { xml_sin_firmar = xml, entorno = "TesteCF" });
var resp = await http.PostAsync($"{baseUrl}/v1/emitir-xml",
new StringContent(body, Encoding.UTF8, "application/json"));
Console.WriteLine(await resp.Content.ReadAsStringAsync());
// Respuesta esperada:
// {"exito":true,"encf":"E310000000012","estado":"Aceptado", ...}
import java.net.URI;
import java.net.http.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import com.fasterxml.jackson.databind.ObjectMapper;
public class EmitirEcf {
public static void main(String[] args) throws Exception {
String baseUrl = "https://cert.ocoasertec.com";
String apiKey = "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8";
String fecha = LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy"));
String xml = "<ECF xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">"
+ "<Encabezado><Version>1.0</Version>"
+ "<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>"
+ "<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>"
+ "<Emisor><RNCEmisor>000000001</RNCEmisor>"
+ "<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>"
+ "<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>"
+ "<FechaEmision>" + fecha + "</FechaEmision></Emisor>"
+ "<Comprador><RNCComprador>131703836</RNCComprador>"
+ "<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>"
+ "<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>"
+ "<ProvinciaComprador>10116</ProvinciaComprador></Comprador>"
+ "<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>"
+ "<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>"
+ "<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>"
+ "<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>"
+ "<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>"
+ "<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>"
+ "<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>";
// eNCF es opcional: si no se envia, el servidor lo genera automaticamente
ObjectMapper mapper = new ObjectMapper();
var body = mapper.createObjectNode();
body.put("xml_sin_firmar", xml);
body.put("entorno", "TesteCF");
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(baseUrl + "/v1/emitir-xml"))
.header("Content-Type", "application/json")
.header("X-API-Key", apiKey)
.POST(HttpRequest.BodyPublishers.ofString(body.toString()))
.build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
// Respuesta esperada:
// {"exito":true,"encf":"E310000000012","estado":"Aceptado", ...}
}
}
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func main() {
baseUrl := "https://cert.ocoasertec.com"
apiKey := "sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
fecha := time.Now().Format("02-01-2006")
xml := fmt.Sprintf(`<ECF xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Encabezado><Version>1.0</Version>
<IdDoc><TipoeCF>31</TipoeCF><FechaVencimientoSecuencia>31-12-2028</FechaVencimientoSecuencia>
<IndicadorMontoGravado>0</IndicadorMontoGravado><TipoIngresos>01</TipoIngresos><TipoPago>1</TipoPago></IdDoc>
<Emisor><RNCEmisor>000000001</RNCEmisor>
<RazonSocialEmisor>EMPRESA DEMO (RNC de prueba, no registrado en DGII)</RazonSocialEmisor>
<DireccionEmisor>Santo Domingo</DireccionEmisor><Municipio>10116</Municipio><Provincia>10116</Provincia>
<FechaEmision>%s</FechaEmision></Emisor>
<Comprador><RNCComprador>131703836</RNCComprador>
<RazonSocialComprador>GRUPO MEDIA SOFT TECHNOLOGY GMS SRL</RazonSocialComprador>
<DireccionComprador>Santo Domingo</DireccionComprador><MunicipioComprador>10116</MunicipioComprador>
<ProvinciaComprador>10116</ProvinciaComprador></Comprador>
<Totales><MontoGravadoTotal>1500.00</MontoGravadoTotal><MontoGravadoI1>1500.00</MontoGravadoI1>
<ITBIS1>18</ITBIS1><TotalITBIS>270.00</TotalITBIS><TotalITBIS1>270.00</TotalITBIS1>
<MontoTotal>1770.00</MontoTotal></Totales></Encabezado>
<DetallesItems><Item><NumeroLinea>1</NumeroLinea><IndicadorFacturacion>1</IndicadorFacturacion>
<NombreItem>Servicio de consultoria</NombreItem><IndicadorBienoServicio>2</IndicadorBienoServicio>
<CantidadItem>1</CantidadItem><UnidadMedida>43</UnidadMedida><PrecioUnitarioItem>1500.00</PrecioUnitarioItem>
<MontoItem>1500.00</MontoItem></Item></DetallesItems></ECF>`, fecha)
// eNCF es opcional: si no se envia, el servidor lo genera automaticamente
payload, _ := json.Marshal(map[string]string{"xml_sin_firmar": xml, "entorno": "TesteCF"})
req, _ := http.NewRequest("POST", baseUrl+"/v1/emitir-xml", bytes.NewBuffer(payload))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-API-Key", apiKey)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
// Respuesta esperada:
// {"exito":true,"encf":"E310000000012","estado":"Aceptado", ...}
}
encf en el body de /v1/emitir-xml es opcional. Si no se envía (ni está presente dentro de <IdDoc> en el XML), el sistema lo genera automáticamente desde la secuencia registrada del tenant en DGII e inserta el elemento en la posición correcta del documento.
"exito": true de los ejemplos: es el formato de respuesta que verás con un tenant real, cuyo certificado sí está delegado ante DGII para su propio RNC. Con la API Key demo de arriba, el RNC (000000001) es ficticio, así que DGII siempre rechaza el envío con "estado": "Rechazado" y el mensaje "El RNC del certificado no está delegado para realizar transacciones para el RNC: 000000001" — no es un error de tu código ni de la API, es justamente lo esperado al usar credenciales de demo. La firma, el envío y la generación del eNCF sí ocurren de verdad; solo el paso de autorización final en DGII rechaza el RNC de prueba.
Antes de emitir comprobantes, cada empresa debe registrar los rangos de e-NCF autorizados por DGII. El sistema usa estos rangos para generar el eNCF automáticamente al procesar una factura.
estado:false) antes de crear o reactivar otro.
fecha_vencimiento.
Antes de registrar un rango nuevo, averigua en qué número terminó el anterior del mismo tipo.
# Antes de crear un rango nuevo, consulta en que numero termino el anterior
curl -s "https://cert.ocoasertec.com/v1/secuencias?prefijo=E31" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
# Respuesta:
# { "max_hasta": 1000000, "rango_activo": { "id": 3, "tipo": "E31", "desde": 1, "hasta": 1000000, ... } }
# El proximo rango debe comenzar en: max_hasta + 1 = 1000001
Registra un rango autorizado por DGII. Si ya existe un rango previo del mismo tipo/entorno, `desde` debe ser exactamente `max_hasta + 1`.
curl -s -X POST "https://cert.ocoasertec.com/v1/secuencias" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8" \
-H "Content-Type: application/json" \
-d '{
"tipo": "E31",
"entorno": "TesteCF",
"desde": 1,
"hasta": 50000,
"fecha_vencimiento": "31-12-2028"
}'
# Respuesta:
# {
# "id": 3, "tipo": "E31", "entorno": "TesteCF", "desde": 1, "hasta": 50000,
# "secuencia_actual": 1, "fecha_vencimiento": "31-12-2028", "estado": true
# }
# Nota E32: el tipo E32 (Consumo Electronico) no exige fecha_vencimiento -- puede omitirse.
Devuelve todos los rangos del tenant con estadísticas: usadas, disponibles, % de uso, días hasta vencimiento.
curl -s "https://cert.ocoasertec.com/v1/secuencias" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
# Respuesta (array de rangos con estadisticas):
# [{
# "id": 3, "tipo": "E31", "entorno": "TesteCF", "desde": 1, "hasta": 1000000,
# "secuencia_actual": 12, "total_secuencias": 1000000, "secuencias_usadas": 11,
# "secuencias_disponibles": 999989, "porcentaje_uso": 0.0,
# "fecha_vencimiento": "31-12-2028", "dias_vencimiento": 866, "estado": true
# }, ...]
# Filtrar por tipo y/o entorno:
curl -s "https://cert.ocoasertec.com/v1/secuencias?tipo=E31&entorno=TesteCF" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
Total de secuencias libres en el rango activo de un tipo — útil para alertar cuando quedan pocas.
curl -s "https://cert.ocoasertec.com/v1/secuencias?disponibilidad_prefijo=E31" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8"
# Respuesta:
# { "disponibilidad_total": 999989, "rangos_activos": 1 }
# Si disponibilidad_total < 1000, la respuesta incluye "advertencia"
Desactivar, reactivar o ajustar el contador de un rango existente.
# Desactivar un rango (requerido antes de poder eliminarlo)
curl -s -X PUT "https://cert.ocoasertec.com/v1/secuencias" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8" \
-H "Content-Type: application/json" \
-d '{ "id": 3, "estado": false }'
# Ajustar el contador actual (por ejemplo, al migrar desde otro sistema)
curl -s -X PUT "https://cert.ocoasertec.com/v1/secuencias" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8" \
-H "Content-Type: application/json" \
-d '{ "id": 3, "secuencia_actual": 4500 }'
Solo rangos inactivos pueden eliminarse — desactive primero con PUT.
# Solo se pueden eliminar rangos inactivos (estado:false)
curl -s -X DELETE "https://cert.ocoasertec.com/v1/secuencias" \
-H "X-API-Key: sk_6q2BM1UrfJekODVEf-TjjEtPIBWhXhGRudcELVjRcr8" \
-H "Content-Type: application/json" \
-d '{ "id": 3 }'
| Mensaje | Causa / Solución |
|---|---|
El número inicial debe ser X para continuar la secuencia | El `desde` no es consecutivo al rango anterior. Consulta `?prefijo=` para obtener el valor correcto. |
El rango se solapa con otro existente | Los números `desde`-`hasta` ya están cubiertos por otro rango del mismo tipo/entorno. |
Ya existe un rango activo para este tipo de comprobante | Desactive el rango actual (PUT `estado:false`) antes de crear o reactivar otro. |
La secuencia actual debe estar dentro del rango definido | `secuencia_actual` debe estar entre `desde` y `hasta`. |
fecha_vencimiento es obligatoria salvo para E32 | Todos los tipos exigen fecha de vencimiento excepto E32 (Consumo Electrónico). |
No se puede eliminar un rango activo | Primero desactívelo con PUT `estado:false`, luego ejecute DELETE. |
Rango no encontrado o no pertenece a su empresa | El `id` no existe o pertenece a otro tenant. |
Errores comunes al procesar comprobantes electrónicos y cómo resolverlos.
Ocurren antes de llegar a DGII — validaciones propias de ecf-saas.
| HTTP | Mensaje | Causa / Solución |
|---|---|---|
| 401 | Falta el header X-API-Key | Incluir el header X-API-Key en la petición (no usamos Bearer/JWT en /v1/*). |
| 401 | API key inválida o inactiva | Verificar que la llave esté bien copiada y no haya sido desactivada. |
| 403 | El tenant asociado a esta API key no está activo | La cuenta está pendiente de pago o suspendida. |
| 429 | Límite de la llave de demostración alcanzado (5/min) | La API Key demo pública tiene rate limit por IP. Regístrate para tu propia llave sin límite. |
| 400 | El tenant no tiene un certificado activo cargado | Sube tu certificado P12 desde el dashboard antes de emitir. |
| 400 | Sin secuencia configurada para {tipo} en entorno {entorno} | Registra el rango autorizado por DGII en Secuencias NCF o POST /v1/secuencias antes de emitir ese tipo. |
| 400 | Secuencia {tipo} agotada (máx X) | Se consumió todo el rango. Registra el siguiente rango que DGII te autorice. |
| 400 | XML inválido: no se encontró <IdDoc> | El xml_sin_firmar enviado no tiene la estructura mínima esperada. |
Estos ya llegaron a DGII — el XML se firmó y se envió, pero DGII lo rechazó. Los marcados verificado los reprodujimos nosotros mismos probando en vivo contra el sandbox de DGII durante el desarrollo de esta plataforma.
| Mensaje DGII | Causa / Solución | |
|---|---|---|
| verificado | invalid child element 'eNCF'. List of possible elements expected: 'eNCF' | eNCF no estaba inmediatamente después de TipoeCF dentro de IdDoc. Ya no debería pasarte — nuestra API lo posiciona automáticamente. |
| verificado | 'Encabezado' has invalid child element 'InformacionReferencia' | Se intentó colocar InformacionReferencia dentro de Encabezado. Va como hermano de Encabezado y DetallesItems, a nivel raíz de ECF — ver la nota de E33/E34 más abajo. |
| verificado | El RNC del certificado no está delegado para realizar transacciones para el RNC: X | El certificado firmante no corresponde al RNC declarado como RNCEmisor. Pasa siempre con la API Key demo (RNC ficticio) — no es un error de tu código. |
| típico | Fecha de vencimiento de secuencia inválida | El rango de secuencia venció en DGII. Debes solicitar/renovar la autorización en el portal de DGII y registrar el nuevo rango. |
| típico | eNCF no válido o ya utilizado | Ese eNCF ya fue enviado antes (duplicado) o está fuera del rango autorizado. No reenviar el mismo eNCF. |
| típico | RNC del comprador no encontrado | El RNCComprador no existe en el padrón de DGII. Verificar que esté bien escrito. |
| típico | Monto total no coincide con el detalle | La suma de MontoItem de todos los ítems no cuadra con Totales. Recalcular MontoGravadoTotal, TotalITBIS y MontoTotal. |
Las filas marcadas "típico" son patrones de rechazo documentados y ampliamente conocidos del e-CF dominicano — no los provocamos nosotros mismos en esta plataforma, así que el mensaje exacto puede variar ligeramente.
| Tipo | Restricción |
|---|---|
| E32 | No incluir FechaVencimientoSecuencia. Siempre incluir FechaLimitePago. Monto < RD$250,000: no enviar sección Comprador (solo Emisor, Items y Totales). Monto ≥ RD$250,000: datos reales del comprador obligatorios. |
| E33/E34 | InformacionReferencia va fuera de Encabezado, como hermano de Encabezado y DetallesItems a nivel raíz de ECF — confirmado probando en vivo contra DGII (colocarlo dentro de Encabezado lo rechaza). RazonModificacion es obligatorio. |
| E43 | No incluir sección Comprador. Solo usar MontoExento y MontoTotal. |
| E44 | No incluir IndicadorMontoGravado en IdDoc. DGII lo rechaza. |
| E47 | No incluir IndicadorMontoGravado. Usar IdentificadorExtranjero en vez de RNCComprador. |
Tablas de códigos oficiales utilizados en los comprobantes electrónicos.
| Código | Descripción | Uso común |
|---|---|---|
| 43 | Servicio / Unidad | Servicios, licencias, ítems unitarios |
| NIU | Unidad (alternativa) | Productos por unidad |
| 15 | Litro | Combustibles, líquidos |
| 21 | Kilogramo | Peso, productos agrícolas |
| 25 | Caja | Productos empacados |
| 27 | Metro cúbico | Volúmenes, embarques |
| MTR | Metro | Longitud, telas |
| DPC | Docena de piezas | Productos por docena |
| GLL | Galón | Combustibles, pinturas |
| LBR | Libra | Peso alternativo |
| YRD | Yarda | Telas, construcción |
| SET | Juego / Set | Kits, juegos de herramientas |
| Código | Descripción |
|---|---|
| 1 | Efectivo |
| 2 | Cheque / Transferencia / Depósito |
| 3 | Tarjeta de Crédito / Débito |
| 4 | Compra a Crédito |
| 5 | Permuta |
| 6 | Nota de Crédito |
| 7 | Mixto |
| Código | Descripción |
|---|---|
| 01 | Ingresos por operaciones (no financieros) |
| 02 | Ingresos financieros |
| 03 | Ingresos extraordinarios |
| 04 | Ingresos por arrendamientos |
| 05 | Ingresos por venta de activos depreciables |
| 06 | Otros ingresos |
| Código | Descripción | Tasa ITBIS |
|---|---|---|
| 1 | Gravado con ITBIS Tasa 1 | 18% |
| 2 | Gravado con ITBIS Tasa 2 | 16% |
| 3 | Gravado con ITBIS Tasa 3 | 0% (exportaciones) |
| 4 | Exento de ITBIS | N/A |
| Código | Descripción |
|---|---|
| 1 | Bien |
| 2 | Servicio |
| 3 | Bien y Servicio |
| 4 | Otros |
| Código | Descripción |
|---|---|
| 1 | Cancelación total de la factura |
| 2 | Corrección de texto / error de escritura |
| 3 | Devolución de bienes |
| 4 | Descuento / bonificación |
| 5 | Cambio de precio |
| Código | Provincia |
|---|---|
| 010000 | Distrito Nacional |
| 020000 | Azua |
| 030000 | Bahoruco |
| 040000 | Barahona |
| 050000 | Dajabón |
| 060000 | Duarte |
| 070000 | Elías Piña |
| 080000 | El Seibo |
| 090000 | Espaillat |
| 100000 | Independencia |
| 110000 | La Altagracia |
| 120000 | La Romana |
| 130000 | La Vega |
| 140000 | María Trinidad Sánchez |
| 150000 | Monte Cristi |
| 160000 | Pedernales |
| Código | Provincia |
|---|---|
| 170000 | Peravia |
| 180000 | Puerto Plata |
| 190000 | Hermanas Mirabal |
| 200000 | Samaná |
| 210000 | San Cristóbal |
| 220000 | San Juan |
| 230000 | San Pedro de Macorís |
| 240000 | Sánchez Ramírez |
| 250000 | Santiago |
| 260000 | Santiago Rodríguez |
| 270000 | Valverde |
| 280000 | Monseñor Nouel |
| 290000 | Monte Plata |
| 300000 | Hato Mayor |
| 310000 | San José de Ocoa |
| 320000 | Santo Domingo |
| Código | Municipio | Provincia |
|---|---|---|
| 010100 | Santo Domingo de Guzmán | Distrito Nacional |
| 320100 | Santo Domingo Este | Santo Domingo |
| 320200 | Santo Domingo Oeste | Santo Domingo |
| 320300 | Santo Domingo Norte | Santo Domingo |
| 320400 | Boca Chica | Santo Domingo |
| 320500 | San Antonio de Guerra | Santo Domingo |
| 320600 | Los Alcarrizos | Santo Domingo |
| 320700 | Pedro Brand | Santo Domingo |
| 250100 | Santiago de los Caballeros | Santiago |
| 230100 | San Pedro de Macorís | San Pedro de Macorís |
| 120100 | La Romana | La Romana |
| 180100 | San Felipe de Puerto Plata | Puerto Plata |
| 130100 | La Vega | La Vega |
| 210100 | San Cristóbal | San Cristóbal |
| 110100 | Higüey | La Altagracia |
| 060100 | San Francisco de Macorís | Duarte |
| Código | Moneda |
|---|---|
| DOP | Peso Dominicano |
| USD | Dólar Estadounidense |
| EUR | Euro |
| GBP | Libra Esterlina |
| CAD | Dólar Canadiense |
| CHF | Franco Suizo |
Fórmulas y formatos que se usan constantemente al construir tu comprobante.
IndicadorMontoGravado = "0"IndicadorMontoGravado = "1"