Documentación

API de facturación electrónica (e-CF)

Esta API permite que cualquier sistema (ERP, POS, facturador propio) emita comprobantes fiscales electrónicos ante la DGII con una llamada HTTP. Nosotros nos encargamos de la firma digital XAdES con tu certificado .p12, la autenticación con la DGII, el envío, el seguimiento y los acuses.

1. Cómo funciona la integración

Tu sistema nunca habla directamente con la DGII ni maneja el certificado. Solo envía el documento en JSON (o XML sin firmar) a nuestro endpoint de emisión y consulta el estado con el trackId.

Tu sistema (ERP/POS)
   │  POST /api/v1/ecf/emit      → archivo/llamada para ENVIAR la factura
   ▼
Bridge DGII
   │  1. valida tu API key
   │  2. arma el XML del e-CF
   │  3. lo firma (XAdES) con tu certificado .p12 cifrado
   │  4. autentica contra la DGII (semilla → token)
   │  5. envía el e-CF y guarda el trackId
   ▼
DGII (TesteCF / CerteCF / Producción)
   │
   ▼
Tu sistema
      GET /api/v1/ecf/track/:trackId   → llamada para CONSULTAR EL ESTATUS
      (o recibes un webhook cuando cambia el estado)
Necesito…Endpoint
Probar que mi conexión y certificado funcionanGET /api/v1/status
Un ejemplo listo para enviarGET /api/v1/ecf/sample
ENVIAR una facturaPOST /api/v1/ecf/emit
CONSULTAR el estatus en la DGIIGET /api/v1/ecf/track/:trackId
Ver un documento que ya emitíGET /api/v1/ecf/:id
Verificar validez de un e-CFPOST /api/v1/ecf/inquiry
Anular secuencias no usadasPOST /api/v1/ecf/void

2. Ambientes y URL base

La URL base de la API es siempre la de esta plataforma. El ambiente de la DGII se define en el panel (tarjeta “Información de la empresa”), no en la petición.

Base URL (producción de la plataforma): https://ecf.grstech.com.do
Base URL (preview):                     https://ecf.grstech.com.do

Todas las rutas van bajo /api/v1
AmbienteValorHost DGII
Pruebas (TesteCF)devhttps://ecf.dgii.gov.do/testecf
Certificación (CerteCF)certhttps://ecf.dgii.gov.do/certecf
Producciónprodhttps://ecf.dgii.gov.do/ecf

3. Autenticación (API key)

Genera la key en el panel → API Keys. Se muestra una sola vez, empieza con bdgii_ y queda ligada a tu organización, su RNC y su certificado. Envíala en cada petición en el header x-api-key o como Authorization: Bearer.

x-api-key: bdgii_xxxxxxxxxxxxxxxxxxxx
content-type: application/json

# equivalente
Authorization: Bearer bdgii_xxxxxxxxxxxxxxxxxxxx

Sin key válida toda ruta responde 401. No expongas la key en el navegador ni en apps móviles: llama a la API desde tu servidor.

4. Flujo de emisión paso a paso

  1. 1En el panel crea tu organización con el RNC del emisor y elige el ambiente TesteCF — Pruebas.
  2. 2Sube tu certificado digital .p12 en Certificados (se guarda cifrado con AES-256-GCM).
  3. 3Pulsa “Probar conexión DGII”; debe autenticar correctamente contra el ambiente elegido.
  4. 4Genera una API key en API Keys y guárdala en tu sistema (solo se muestra una vez).
  5. 5Verifica desde tu sistema con GET /api/v1/status (debe devolver dgii.authenticated = true).
  6. 6Pide un ejemplo con GET /api/v1/ecf/sample y adáptalo con los datos reales de tu factura.
  7. 7Envía la factura con POST /api/v1/ecf/emit; guarda el id y el trackId de la respuesta.
  8. 8Consulta GET /api/v1/ecf/track/:trackId (o espera el webhook) hasta obtener Aceptado.

Recomendación: consulta el tracking con reintentos espaciados (por ejemplo a los 5s, 15s, 30s, 60s) en lugar de un bucle agresivo.

5. Referencia de endpoints

GET/api/v1/status

Valida tu API key, muestra la organización, el certificado activo y autentica en vivo contra la DGII del ambiente configurado.

¿Cuándo se usa? Al configurar la integración y como health-check antes de emitir en lote.

Respuesta

{
  "data": {
    "organization": { "id": "uuid", "name": "MI EMPRESA SRL", "rnc": "133511002", "environment": "dev" },
    "certificate": { "alias": "MI EMPRESA", "subject": "CN=...", "valid_to": "2027-05-01T00:00:00Z" },
    "dgii": { "authenticated": true, "error": null },
    "environmentLabel": "TesteCF (pruebas)"
  }
}
GET/api/v1/ecf/sample

Devuelve un body de emisión e-CF 31 completo y válido, con tu RNC y la próxima secuencia sugerida.

¿Cuándo se usa? Para arrancar la integración o generar pruebas rápidas. Parámetros opcionales: encf, rncEmisor, rncComprador, montoGravado.

Respuesta

{ "data": {
    "tipoEcf": "31",
    "encf": "E310000000001",
    "rncEmisor": "133511002",
    "rncComprador": "401506254",
    "montoTotal": 1180,
    "summary": false,
    "document": {
      "ECF": {
        "Encabezado": {
          "Version": "1.0",
          "IdDoc": {
            "TipoeCF": "31",
            "eNCF": "E310000000001",
            "FechaVencimientoSecuencia": "31-12-2028",
            "IndicadorMontoGravado": "0",
            "TipoIngresos": "01",
            "TipoPago": "1",
            "TotalPaginas": 1
          },
          "Emisor": {
            "RNCEmisor": "133511002",
            "RazonSocialEmisor": "EMPRESA DE PRUEBAS SRL",
            "DireccionEmisor": "Av. Principal #1, Santo Domingo",
            "FechaEmision": "15-01-2026"
          },
          "Comprador": {
            "RNCComprador": "401506254",
            "RazonSocialComprador": "DIRECCION GENERAL DE IMPUESTOS INTERNOS"
          },
          "Totales": {
            "MontoGravadoTotal": 1000,
            "MontoGravadoI1": 1000,
            "MontoExento": 0,
            "ITBIS1": 18,
            "TotalITBIS": 180,
            "TotalITBIS1": 180,
            "MontoTotal": 1180,
            "MontoNoFacturable": 0
          }
        },
        "DetallesItems": {
          "Item": {
            "NumeroLinea": "1",
            "IndicadorFacturacion": "1",
            "NombreItem": "Servicio de prueba",
            "IndicadorBienoServicio": "2",
            "CantidadItem": 1,
            "UnidadMedida": "43",
            "PrecioUnitarioItem": 1000,
            "MontoItem": 1000
          }
        },
        "Paginacion": {
          "Pagina": {
            "PaginaNo": 1,
            "NoLineaDesde": 1,
            "NoLineaHasta": 1,
            "SubtotalMontoGravadoPagina": 1000,
            "SubtotalMontoGravado1Pagina": 1000,
            "SubtotalExentoPagina": 0,
            "SubtotalItbisPagina": 180,
            "SubtotalItbis1Pagina": 180,
            "MontoSubtotalPagina": 1180,
            "SubtotalMontoNoFacturablePagina": 0
          }
        },
        "FechaHoraFirma": "15-01-2026 06:00:00"
      }
    }
  } }
POST/api/v1/ecf/emit

ESTE ES EL ENDPOINT PARA ENVIAR FACTURAS. Firma el documento con tu certificado y lo transmite a la DGII.

¿Cuándo se usa? Cada vez que tu sistema genera una factura fiscal electrónica.

Petición (body JSON)

{
  "tipoEcf": "31",
  "encf": "E310000000001",
  "rncEmisor": "133511002",
  "rncComprador": "401506254",
  "montoTotal": 1180,
  "summary": false,
  "document": {
    "ECF": {
      "Encabezado": {
        "Version": "1.0",
        "IdDoc": {
          "TipoeCF": "31",
          "eNCF": "E310000000001",
          "FechaVencimientoSecuencia": "31-12-2028",
          "IndicadorMontoGravado": "0",
          "TipoIngresos": "01",
          "TipoPago": "1",
          "TotalPaginas": 1
        },
        "Emisor": {
          "RNCEmisor": "133511002",
          "RazonSocialEmisor": "EMPRESA DE PRUEBAS SRL",
          "DireccionEmisor": "Av. Principal #1, Santo Domingo",
          "FechaEmision": "15-01-2026"
        },
        "Comprador": {
          "RNCComprador": "401506254",
          "RazonSocialComprador": "DIRECCION GENERAL DE IMPUESTOS INTERNOS"
        },
        "Totales": {
          "MontoGravadoTotal": 1000,
          "MontoGravadoI1": 1000,
          "MontoExento": 0,
          "ITBIS1": 18,
          "TotalITBIS": 180,
          "TotalITBIS1": 180,
          "MontoTotal": 1180,
          "MontoNoFacturable": 0
        }
      },
      "DetallesItems": {
        "Item": {
          "NumeroLinea": "1",
          "IndicadorFacturacion": "1",
          "NombreItem": "Servicio de prueba",
          "IndicadorBienoServicio": "2",
          "CantidadItem": 1,
          "UnidadMedida": "43",
          "PrecioUnitarioItem": 1000,
          "MontoItem": 1000
        }
      },
      "Paginacion": {
        "Pagina": {
          "PaginaNo": 1,
          "NoLineaDesde": 1,
          "NoLineaHasta": 1,
          "SubtotalMontoGravadoPagina": 1000,
          "SubtotalMontoGravado1Pagina": 1000,
          "SubtotalExentoPagina": 0,
          "SubtotalItbisPagina": 180,
          "SubtotalItbis1Pagina": 180,
          "MontoSubtotalPagina": 1180,
          "SubtotalMontoNoFacturablePagina": 0
        }
      },
      "FechaHoraFirma": "15-01-2026 06:00:00"
    }
  }
}

Respuesta

HTTP 201
{
  "data": {
    "id": "9f0b...-uuid",             // identificador interno (úsalo en GET /api/v1/ecf/:id)
    "tipo_ecf": "31",
    "encf": "E310000000001",
    "rnc_emisor": "133511002",
    "rnc_comprador": "401506254",
    "monto_total": 1180,
    "track_id": "6f6b1f2e-...",       // úsalo en GET /api/v1/ecf/track/:trackId
    "status": "sent",
    "error_message": null,
    "dgii_response": { "trackId": "6f6b1f2e-..." },
    "created_at": "2026-08-12T15:00:00.000Z",
    "updated_at": "2026-08-12T15:00:02.000Z"
  }
}
GET/api/v1/ecf/track/:trackId

ESTE ES EL ENDPOINT PARA CONSULTAR EL ESTATUS en la DGII usando el trackId devuelto al emitir.

¿Cuándo se usa? Después de emitir, hasta que la DGII responda Aceptado o Rechazado.

Respuesta

{
  "data": {
    "trackId": "6f6b1f2e-...",
    "codigo": "1",
    "estado": "Aceptado",            // Aceptado | Aceptado Condicional | Rechazado | En Proceso
    "rnc": "133511002",
    "encf": "E310000000001",
    "secuenciaUtilizada": true,
    "fechaRecepcion": "12-08-2026 11:00:03",
    "mensajes": []                    // detalle de errores si fue rechazado
  }
}
GET/api/v1/ecf/:id

Lee el documento tal como lo guardamos (estado interno, trackId y última respuesta de la DGII).

¿Cuándo se usa? Para reconciliar en tu sistema si perdiste el trackId o quieres auditar el envío.

Respuesta

{
  "data": {
    "id": "9f0b...-uuid",
    "tipo_ecf": "31",
    "encf": "E310000000001",
    "status": "accepted",
    "track_id": "6f6b1f2e-...",
    "error_message": null,
    "dgii_response": { }
  }
}
POST/api/v1/ecf/inquiry

Consulta ante la DGII la validez de un e-CF (propio o de un proveedor).

¿Cuándo se usa? Para verificar comprobantes recibidos o confirmar que un e-CF existe en la DGII.

Petición (body JSON)

{
  "rncEmisor": "133511002",
  "encf": "E310000000001",
  "rncComprador": "401506254",
  "codigoSeguridad": "aB3xY9"
}

Respuesta

{ "data": { "esValido": true, "mensajes": [] } }
POST/api/v1/ecf/void

Anula rangos de e-NCF no utilizados (ANECF). El XML se firma automáticamente.

¿Cuándo se usa? Cuando debes reportar secuencias autorizadas que no vas a usar.

Petición (body JSON)

{
  "xml": "<ANECF>…</ANECF>",
  "fileName": "133511002ANECF.xml"
}

Respuesta

{ "data": { "codigo": 1, "estado": "Aceptado" } }

6. Campos del documento e-CF

El body de /api/v1/ecf/emit tiene campos de control (para nuestro registro) y el documento en sí.

CampoTipoObligatorioDescripción
tipoEcfstring(2)SíTipo de e-CF: 31, 32, 33, 34, 41, 43, 44, 45, 46, 47.
encfstringSíSecuencia e-NCF, ej. E310000000001. Debe estar dentro de un rango autorizado.
rncEmisorstring(9-11)SíRNC del emisor; debe coincidir con el de tu organización y certificado.
rncCompradorstring(9-11)NoRNC/cédula del comprador. Obligatorio para tipos con crédito fiscal (31).
montoTotalnumberNoTotal del documento; se usa para reportes en el panel.
summarybooleanNotrue = envío por resumen (RFCE, consumo < RD$250,000).
documentobjectSí (o xml)Documento e-CF en JSON con la estructura ECF → Encabezado, DetallesItems, Paginacion.
xmlstringSí (o document)XML del e-CF SIN firmar, si prefieres generarlo tú. Nosotros lo firmamos.

Dentro de document.ECF: fechas en formato DD-MM-AAAA, FechaHoraFirma como DD-MM-AAAA HH:mm:ss, montos numéricos con 2 decimales, e ITBIS 18% en TotalITBIS. Los totales de Paginacion deben cuadrar con los de Totales, de lo contrario la DGII rechaza el documento.

7. Estados y ciclo de vida

  • sending — firmado y enviándose a la DGII.
  • sent — recibido por la DGII con trackId asignado; falta el resultado final.
  • accepted — aceptado (respuesta sin trackId o tracking confirmado).
  • error — fallo de firma, validación o comunicación; revisa error_message.

El estado definitivo lo da la DGII en el tracking: Aceptado, Aceptado Condicional, Rechazado o En Proceso. Un documento rechazado requiere corregir y emitir una secuencia nueva.

8. Errores y códigos HTTP

CódigoSignificadoQué hacer
200 / 201Operación correcta (201 al emitir).Guarda id y track_id.
400Payload inválido o faltan xml/document.Revisa el campo details del error.
401API key ausente, inválida o desactivada.Verifica el header x-api-key.
404Documento no encontrado para tu organización.Confirma el id.
500Error interno al leer datos.Reintenta; si persiste, contáctanos.
502Error de firma o de comunicación con la DGII.Revisa el certificado y el estado del servicio DGII; reintenta.

Forma del error

{ "error": "Payload inválido / Invalid payload", "details": "..." }

Importante: no reenvíes el mismo eNCF tras un 502 sin antes consultar GET /api/v1/ecf/:id; la secuencia podría haberse consumido.

9. Ejemplos de código

cURL

BASE=https://ecf.grstech.com.do
KEY=bdgii_xxxxxxxxxxxxxxxx

# 1) Verificar conexión con la DGII
curl "$BASE/api/v1/status" -H "x-api-key: $KEY"

# 2) Obtener un payload de prueba
curl "$BASE/api/v1/ecf/sample" -H "x-api-key: $KEY" -o payload.json

# 3) ENVIAR la factura (usa el objeto data del paso 2 como body)
curl -X POST "$BASE/api/v1/ecf/emit" \
  -H "x-api-key: $KEY" \
  -H "content-type: application/json" \
  -d @payload.json

# 4) CONSULTAR EL ESTATUS
curl "$BASE/api/v1/ecf/track/TRACK_ID" -H "x-api-key: $KEY"

Node.js / TypeScript

const BASE = "https://ecf.grstech.com.do";
const KEY = process.env.BRIDGE_API_KEY!;

async function emitir(factura: unknown) {
  const res = await fetch(`${BASE}/api/v1/ecf/emit`, {
    method: "POST",
    headers: { "x-api-key": KEY, "content-type": "application/json" },
    body: JSON.stringify(factura),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(body.error);
  return body.data; // { id, track_id, status, ... }
}

async function estatus(trackId: string) {
  const res = await fetch(`${BASE}/api/v1/ecf/track/${trackId}`, {
    headers: { "x-api-key": KEY },
  });
  return (await res.json()).data;
}

PHP

<?php
$base = "https://ecf.grstech.com.do";
$key  = getenv("BRIDGE_API_KEY");

$ch = curl_init("$base/api/v1/ecf/emit");
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_HTTPHEADER => ["x-api-key: $key", "content-type: application/json"],
  CURLOPT_POSTFIELDS => json_encode($factura),
]);
$resp = json_decode(curl_exec($ch), true);
$trackId = $resp["data"]["track_id"] ?? null;

Python

import os, requests

BASE = "https://ecf.grstech.com.do"
H = {"x-api-key": os.environ["BRIDGE_API_KEY"]}

doc = requests.post(f"{BASE}/api/v1/ecf/emit", json=factura, headers=H).json()["data"]
estado = requests.get(f"{BASE}/api/v1/ecf/track/{doc['track_id']}", headers=H).json()["data"]
print(estado["estado"])

10. Webhooks

Alternativa al polling: configúralos en el panel → Webhooks. Cuando cambia el estado de un documento enviamos un POST JSON a tu URL con la firma HMAC-SHA256 del cuerpo en el header x-bridge-signature. Verifícala con el secreto mostrado al crear el webhook y responde 2xx.

// Cuerpo recibido
{
  "event": "ecf.status_changed",
  "data": { "id": "uuid", "encf": "E310000000001", "status": "sent", "track_id": "..." }
}

// Verificación (Node.js)
import crypto from "node:crypto";
const esperado = crypto.createHmac("sha256", SECRET).update(rawBody).digest("hex");
const valido = crypto.timingSafeEqual(Buffer.from(firma), Buffer.from(esperado));

11. Buzón de recibidos

Los e-CF que te envían tus proveedores y las aprobaciones comerciales entran por las URLs registradas en la DGII y quedan guardados en tu buzón. Tu sistema (Trillonic) los lee con estos dos endpoints, usando la misma x-api-key.

Importante: el buzón está separado por ambiente. Si no ves documentos, agrega ?environment=prod (o dev / cert); sin ese parámetro se devuelve solo el ambiente activo de la empresa. Para empresas asociadas envía el header x-org-id.

GET/api/v1/inbound

Lista los documentos recibidos (e-CF de proveedores y aprobaciones comerciales).

¿Cuándo se usa? Cada pocos minutos desde Trillonic para sincronizar el buzón de entrada.

Respuesta

// GET /api/v1/inbound?environment=prod&limit=50
{
  "environment": "prod",
  "count": 1,
  "data": [
    {
      "id": "9f0b...-uuid",
      "kind": "ecf",                 // ecf | commercial_approval
      "encf": "E310000016244",
      "rnc_emisor": "133511002",
      "rnc_comprador": "401506254",
      "status": "received",          // received | rejected | approved
      "reject_code": null,
      "environment": "prod",
      "created_at": "2026-09-06T12:00:00.000Z"
    }
  ]
}
GET/api/v1/inbound/:id

Devuelve un documento recibido con su XML completo tal como llegó de la DGII.

¿Cuándo se usa? Cuando Trillonic necesita el detalle (items, montos) para registrar la compra.

Respuesta

{
  "data": {
    "id": "9f0b...-uuid",
    "kind": "ecf",
    "encf": "E310000016244",
    "rnc_emisor": "133511002",
    "status": "received",
    "environment": "prod",
    "xml": "<ECF>…</ECF>",
    "created_at": "2026-09-06T12:00:00.000Z"
  }
}
GET/api/v1/inbound/:idOe-NCF/xml

Descarga el XML crudo del documento recibido (acepta el id o el e-NCF).

¿Cuándo se usa? Cuando solo necesitas el archivo XML para archivarlo o procesarlo.

Respuesta

<?xml version="1.0" encoding="utf-8"?>
<ECF>…</ECF>
GET/api/v1/ecf/:idOe-NCF/xml

Descarga el XML firmado de un comprobante que tú emitiste (acepta el id o el e-NCF).

¿Cuándo se usa? Para recuperar el archivo firmado desde cualquier servidor: se lee de la base de datos.

Respuesta

<?xml version="1.0" encoding="utf-8"?>
<ECF>…<Signature>…</Signature></ECF>

Filtros disponibles en la lista: environment, status, rncEmisor, encf, since (fecha ISO) y limit (1–200).

// Sincronizar el buzón en Trillonic (Node.js)
const BASE = "https://ecf.grstech.com.do";
const H = { "x-api-key": process.env.BRIDGE_API_KEY };

const desde = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
const r = await fetch(
  `${BASE}/api/v1/inbound?environment=prod&since=${desde}&limit=200`,
  { headers: H },
);
const { data } = await r.json();

for (const doc of data) {
  const det = await fetch(`${BASE}/api/v1/inbound/${doc.id}`, { headers: H });
  const { data: completo } = await det.json();
  // completo.xml trae el e-CF recibido; regístralo como compra en Trillonic.
}

// Bajar solo el archivo XML (emitido o recibido), por id o por e-NCF:
const xmlRecibido = await (await fetch(
  `${BASE}/api/v1/inbound/E310000016244/xml?environment=prod`, { headers: H },
)).text();
const xmlEmitido = await (await fetch(
  `${BASE}/api/v1/ecf/E310000000005/xml?environment=prod`, { headers: H },
)).text();

12. URLs a registrar en la DGII

Para recibir e-CF de tus proveedores y aprobaciones comerciales, registra estas URLs en el portal de la DGII:

  • https://ecf.grstech.com.do/fe/autenticacion/api/semilla
  • https://ecf.grstech.com.do/fe/autenticacion/api/validacioncertificado
  • https://ecf.grstech.com.do/fe/recepcion/api/ecf
  • https://ecf.grstech.com.do/fe/aprobacioncomercial/api/ecf

Si manejas varios RNC, añade ?rnc=RNC a cada URL. Los documentos recibidos aparecen en Documentos → Recibidos y en GET /api/v1/inbound.

13. Preguntas frecuentes

¿Cuál es el “archivo” para enviar facturas?
No se sube archivo: se hace un POST JSON a /api/v1/ecf/emit. Si tu sistema ya genera XML, mándalo sin firmar en el campo xml y nosotros lo firmamos.
¿Y para consultar el estatus?
GET /api/v1/ecf/track/:trackId con el trackId que devuelve la emisión. También puedes usar GET /api/v1/ecf/:id para ver el registro interno.
¿Debo firmar el XML en mi sistema?
No. La firma XAdES se hace aquí con tu certificado .p12 cifrado; tu sistema nunca maneja la llave privada.
¿Quién controla la secuencia e-NCF?
Tu sistema. Debes enviar un eNCF único dentro de un rango autorizado por la DGII; no reutilices secuencias.
¿Cómo paso de pruebas a producción?
Cambia el ambiente de la organización a Producción en el panel y vuelve a probar la conexión. La API key y los endpoints siguen siendo los mismos.
¿Hay límite de tamaño?
Los e-CF de consumo menores a RD$250,000 pueden enviarse por resumen (summary: true, RFCE).

14. Multi-cliente (empresas asociadas)

Si eres integrador (por ejemplo un ERP con muchos clientes), usas una sola API key (la de tu empresa principal) y creas una empresa asociada por cada cliente final. Cada empresa asociada tiene su propio RNC, certificado .p12, secuencias, documentos y ambiente DGII. Tus clientes no necesitan API key propia.

Tu sistema (integrador)  ── x-api-key: bdgii_xxx (tu key)
   │
   ├─ POST /api/v1/orgs                        → crea la empresa asociada (cliente)
   ├─ POST /api/v1/orgs/:orgId/certificate     → sube su .p12 (cifrado AES-256-GCM)
   │
   └─ POST /api/v1/ecf/emit  + x-org-id: :orgId → emite a nombre de ese cliente

1. Crear la empresa asociada

curl -X POST "$BASE/api/v1/orgs" \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{
    "name": "Cliente Ejemplo SRL",
    "rnc": "131234567",
    "environment": "dev"
  }'

# Respuesta
{
  "success": true,
  "data": {
    "org": { "id": "uuid-del-cliente", "name": "Cliente Ejemplo SRL", "rnc": "131234567" },
    "apiKey": "bdgii_..."   // opcional: puedes ignorarla y usar siempre tu key
  }
}

Guarda el org.id en tu base de datos junto al cliente. Requiere que tu key tenga el scope orgs:manage.

2. Subir el certificado del cliente

curl -X POST "$BASE/api/v1/orgs/ORG_ID/certificate" \
  -H "x-api-key: $KEY" -H "content-type: application/json" \
  -d '{
    "alias": "Certificado 2026",
    "p12Base64": "<contenido del .p12 en base64>",
    "password": "clave-del-certificado"
  }'

El .p12 se cifra con AES-256-GCM y se archiva en almacenamiento privado. Desde tu sistema puedes ofrecer al cliente un formulario de carga y reenviarnos el archivo en base64.

3. Emitir a nombre del cliente

curl -X POST "$BASE/api/v1/ecf/emit" \
  -H "x-api-key: $KEY" \
  -H "x-org-id: ORG_ID" \
  -H "content-type: application/json" \
  -d @factura.json

El header x-org-id también funciona en /api/v1/ecf/track/:trackId, /api/v1/ecf/inquiry, /api/v1/ecf/void, /api/v1/ecf/:id, /api/v1/ecf/sample y /api/v1/status. En los GET puedes usar además ?orgId=ORG_ID.

Seguridad

  • x-org-id solo se acepta si esa empresa fue creada bajo tu organización; cualquier otro id devuelve 403.
  • Sin el header, todo se emite con tu propia organización y certificado.
  • Cada cliente puede tener su propio ambiente (dev/cert/prod) sin afectar a los demás.