Verifica que el proceso Node.js esté vivo. Úsalo como liveness probe en Kubernetes/Docker.
Respuesta 200 OK
{ "status": "ok", "timestamp": "2026-05-22T23:00:00.000Z" }
Damaju · Colombia · UBL 2.1 · XAdES-EPES · Resolución 000085/2022
Este servicio implementa el ciclo completo de facturación electrónica colombiana conforme
al Anexo Técnico v1.9 de la DIAN (Resolución 000085/2022). Recibe datos de negocio vía REST, genera el
XML UBL 2.1, lo firma digitalmente con XAdES-EPES, lo empaqueta en ZIP
y lo transmite al web service SOAP de la DIAN (WcfDianCustomerServices).
El procesamiento es asíncrono: la API responde de inmediato con un id de
documento y un estado queued. Un worker BullMQ ejecuta el pipeline en background y actualiza
el estado en PostgreSQL. Usa GET /v1/documents/:id para consultar el resultado.
GET /documentation/openapi.json — úsalo para inspección programática de todos los endpoints,
parámetros y tipos. Esta página HTML contiene exactamente la misma información en formato legible.
Fastify (Node.js) → Zod validation → Prisma/PostgreSQL ↓ BullMQ worker (Redis) ├─ 1. Build UBL 2.1 XML ├─ 2. Sign XAdES-EPES (PKCS#12 certificate) ├─ 3. ZIP (nombre: NIT+PREFIX+CONSECUTIVE.zip) ├─ 4. SendBillAsync / SendTestSetAsync (DIAN SOAP) ├─ 5. Poll GetStatusZip ← retries exponenciales └─ 6. Store XML+PDF en S3/R2 · Update DB status
Guía para integrar este servicio desde cero, en orden cronológico:
En el panel admin (/admin/configuration) configura: NIT emisor, Software ID, Software PIN,
Test Set ID, y sube el certificado digital (.p12). El SoftwareSecurityCode se calcula
automáticamente como SHA384(softwareId + pin + nit).
Llama a POST /v1/dian/numbering-range/sync para obtener los rangos de numeración
autorizados (GetNumberingRange). Esto guarda en DB la clave técnica (ClTec),
prefijo, rangos y fechas de vigencia — necesarios para calcular el CUFE.
En /admin/integrations crea una API Key. Incluye los headers
X-API-Key y X-API-Secret en todas las llamadas de negocio.
Con POST /v1/invoices y el prefijo de habilitación (ej: SETT).
El servicio usará SendTestSetAsync en ambiente 2 (habilitación).
Con GET /v1/documents/:id. Espera estado validated (código DIAN 00).
Si hay error, el campo dianMessage describe el rechazo.
Cuando DIAN aprueba el set de pruebas, cambia DIAN_AMBIENTE=1 en el panel.
El servicio usará SendBillAsync y el WSDL de producción automáticamente.
Todos los endpoints /v1/* requieren dos headers:
| Header | Descripción | Ejemplo |
|---|---|---|
| X-API-Key | Identificador público de la API Key (generado en /admin/integrations) | dk_a1b2c3d4... |
| X-API-Secret | Secreto de la API Key. Solo se muestra una vez al crear. Se almacena como hash SHA256. | e5f6a7b8... |
curl -X POST https://fe.damaju.com.co/v1/invoices \
-H "Content-Type: application/json" \
-H "X-API-Key: dk_tu_api_key" \
-H "X-API-Secret: tu_api_secret" \
-d '{ ... }'
Verifica que el proceso Node.js esté vivo. Úsalo como liveness probe en Kubernetes/Docker.
{ "status": "ok", "timestamp": "2026-05-22T23:00:00.000Z" }
Verifica que PostgreSQL y Redis estén accesibles. Úsalo como readiness probe.
{
"status": "ready",
"checks": { "database": "ok", "redis": "ok" },
"timestamp": "2026-05-22T23:00:00.000Z"
}
Valida el payload con Zod (totales matemáticos, DV NIT, DANE codes), construye el XML UBL 2.1, firma con XAdES-EPES, empaqueta en ZIP y encola el envío a DIAN. La respuesta es inmediata; el procesamiento ocurre en background.
SHA384(NumFac + FecFac + HorFac + ValFac + CodImp1 + ValImp1 + ValImp3 + ValTot + NitOFE + NumAdq + ClTec + ambiente).
No es necesario enviarlo en el request.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| issuer | Party | ✓ | Emisor (Damaju). Ver esquema Party. |
| customer | Party | ✓ | Adquiriente/cliente. Ver esquema Party. |
| items | InvoiceLine[] | ✓ | Líneas de detalle. Mínimo 1, máximo 9999. lineTotal debe ser ≈ quantity × unitPrice. |
| taxes | TaxTotal[] | ✓ | Impuestos agrupados por código (IVA=01, INC=04, ICA=03). |
| totals | Totals | ✓ | Totales monetarios. Validación cruzada: taxInclusiveAmount = taxExclusiveAmount + Σtaxes. |
| prefix | string | ✓ | Prefijo autorizado por resolución DIAN. Ej: SETT |
| consecutive | number | Número específico. Si se omite, se auto-asigna el siguiente disponible. | |
| issueDate | string | Fecha emisión YYYY-MM-DD. Default: hoy. | |
| issueTime | string | Hora emisión HH:MM:SS-05:00. Default: ahora Colombia. | |
| currency | string | Código ISO 4217. Default: COP | |
| invoiceTypeCode | enum | 01=FV, 02=Exportación, 03=Contingencia, 04=Contingencia DIAN. Default: 01 | |
| payment | Payment | Medio de pago y fecha vencimiento. | |
| notes | string[] | Notas libres (máx 5000 chars c/u). | |
| orderReference | string | Referencia orden de compra. | |
| resolution | string | ✓ | Número de resolución DIAN. Ej: 18764086918727 |
curl -X POST https://fe.damaju.com.co/v1/invoices \
-H "Content-Type: application/json" \
-H "X-API-Key: dk_tu_api_key" \
-H "X-API-Secret: tu_api_secret" \
-d '{
"issuer": {
"documentType": "31",
"documentNumber": "900000000",
"dv": "1",
"name": "DAMAJU SAS",
"email": "facturacion@damaju.com.co",
"address": {
"street": "Calle 1 # 2-3",
"city": "Bogotá",
"cityCode": "11001",
"department": "Bogotá D.C.",
"departmentCode": "11",
"countryCode": "CO"
},
"taxScheme": "ZZ",
"taxLevelCode": "R-99-PN"
},
"customer": {
"documentType": "13",
"documentNumber": "1234567890",
"name": "CLIENTE EJEMPLO",
"email": "cliente@example.com",
"address": {
"street": "Carrera 10 # 20-30",
"city": "Medellín",
"cityCode": "05001",
"department": "Antioquia",
"departmentCode": "05",
"countryCode": "CO"
}
},
"items": [
{
"id": "1",
"description": "Servicio de consultoría",
"quantity": 1,
"unitCode": "EA",
"unitPrice": 200000,
"lineTotal": 200000,
"taxAmount": 38000,
"taxPercent": 19,
"taxCode": "01"
}
],
"taxes": [
{
"taxCode": "01",
"taxAmount": 38000,
"taxableAmount": 200000,
"percent": 19
}
],
"totals": {
"lineExtensionAmount": 200000,
"taxExclusiveAmount": 200000,
"taxInclusiveAmount": 238000,
"allowanceTotalAmount": 0,
"chargeTotalAmount": 0,
"payableAmount": 238000
},
"resolution": "18764086918727",
"prefix": "SETT",
"payment": {
"meansCode": "10",
"dueDate": "2026-06-22"
}
}'
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"prefix": "SETT",
"consecutive": 1,
"status": "queued",
"message": "Documento encolado para procesamiento"
}
id y consulta el estado con GET /v1/documents/:id.
El procesamiento típico tarda entre 3 y 30 segundos según la carga de DIAN.
Mismo payload que POST /v1/invoices más los campos adicionales de referencia
a la factura original. El CUDE se calcula como SHA384 con los mismos campos que el CUFE.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| billingReference | object | ✓ |
Referencia a la factura original:invoiceId: número completo (ej: SETT1)invoiceUuid: CUFE de la factura (96 chars hex)issueDate: fecha de la factura YYYY-MM-DD
|
| creditNoteReasonCode | enum | 1=Devolución parcial, 2=Anulación, 3=Rebaja, 4=Descuento, 5=Rescisión, 6=Otros. Default: 2 |
|
| creditNoteReason | string | Descripción libre de la razón. Default: Anulación de factura electrónica |
curl -X POST https://fe.damaju.com.co/v1/credit-notes \
-H "Content-Type: application/json" \
-H "X-API-Key: dk_tu_api_key" \
-H "X-API-Secret: tu_api_secret" \
-d '{
"billingReference": {
"invoiceId": "SETT1",
"invoiceUuid": "abc123...96hexchars",
"issueDate": "2026-05-20"
},
"creditNoteReasonCode": "1",
"creditNoteReason": "Devolución parcial de mercancía",
"issuer": { ... },
"customer": { ... },
"items": [ ... ],
"taxes": [ ... ],
"totals": { ... },
"resolution": "18764086918727",
"prefix": "NC"
}'
Igual que nota crédito, con campos de razón distintos.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| debitNoteReasonCode | enum | 1=Intereses, 2=Gastos por cobrar, 3=Cambio de valor. Default: 1 |
|
| debitNoteReason | string | Descripción libre. Default: Intereses |
Consulta el estado actual del pipeline para un documento creado previamente.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "INVOICE",
"prefix": "SETT",
"consecutive": 1,
"cufe": "a3b4c5...96hexchars",
"trackId": "uuid-retornado-por-dian",
"status": "validated",
"dianCode": "00",
"dianMessage": "Procesado correctamente",
"xmlPath": "documents/900000000/invoices/SETT/SETT1.xml",
"pdfPath": "documents/900000000/invoices/SETT/SETT1.pdf",
"createdAt": "2026-05-22T23:00:00.000Z",
"updatedAt": "2026-05-22T23:00:45.000Z"
}
Retorna el XML UBL 2.1 con firma XAdES-EPES tal como fue enviado a DIAN.
curl -H "X-API-Key: dk_..." -H "X-API-Secret: ..." \ https://fe.damaju.com.co/v1/documents/ID/xml > factura.xml
PDF generado con Puppeteer (HTML → PDF). Incluye cabecera, detalle de ítems, totales, CUFE y QR.
curl -H "X-API-Key: dk_..." -H "X-API-Secret: ..." \ https://fe.damaju.com.co/v1/documents/ID/pdf > factura.pdf
Invoca GetStatusZip en el web service DIAN para el trackId (ZipKey)
retornado por SendBillAsync o SendTestSetAsync.
{
"statusCode": "00",
"statusDescription": "Procesado correctamente",
"statusMessage": "Documento validado por la DIAN",
"isValid": true,
"errors": [],
"warnings": []
}
Llama a GetNumberingRange en DIAN y guarda en la base de datos las resoluciones
activas con: número resolución, prefijo, rango de numeración, clave técnica (ClTec),
y fechas de vigencia. Ejecutar antes de emitir cualquier documento.
{
"synced": 2,
"ranges": [
{
"resolutionNumber": "18764086918727",
"prefix": "SETT",
"fromNumber": 1,
"toNumber": 5000,
"technicalKey": "fc8eac422eba16e22ffd8c6f94b3f40a6e38162c...",
"validFrom": "2026-01-01",
"validTo": "2027-01-01"
}
]
}
Endpoints diseñados para ser llamados directamente desde un ERP, e-commerce o POS de Damaju. Cada webhook es un alias semántico de los endpoints principales.
| Webhook | Acción | Equivalente |
|---|---|---|
| POST /v1/webhooks/order-paid | Orden pagada → emitir factura electrónica | POST /v1/invoices |
| POST /v1/webhooks/order-cancelled | Orden cancelada → emitir nota crédito de anulación | POST /v1/credit-notes |
| POST /v1/webhooks/return-accepted | Devolución aceptada → emitir nota crédito de devolución | POST /v1/credit-notes |
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| documentType | enum | ✓ | 11=Registro civil, 12=Tarjeta identidad, 13=Cédula ciudadanía, 21=Tarjeta extranjería, 22=Cédula extranjería, 31=NIT, 41=Pasaporte, 42=Documento extranjero, 47=PEP, 50=NIT otro país, 91=NUIP |
| documentNumber | string | ✓ | Número del documento (máx 20 chars) |
| dv | string | Dígito de verificación NIT (1 char). Se valida contra el algoritmo DIAN si se provee. | |
| name | string | ✓ | Razón social o nombre completo (máx 450 chars) |
| string | ✓ | Email válido (máx 200 chars). Se usa para envío del PDF. | |
| phone | string | Teléfono de contacto | |
| address | Address | ✓ | Ver esquema Address |
| taxScheme | string | Régimen tributario DIAN. Default: ZZ | |
| taxLevelCode | string | Nivel de responsabilidad fiscal. Default: R-99-PN |
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| street | string | ✓ | Dirección completa (máx 200 chars) |
| city | string | ✓ | Nombre del municipio |
| cityCode | string | ✓ | Código municipio DANE — exactamente 5 dígitos. Ej: 11001 = Bogotá |
| department | string | ✓ | Nombre del departamento |
| departmentCode | string | ✓ | Código departamento DANE — exactamente 2 dígitos. Ej: 11 = Bogotá D.C. |
| countryCode | string | ISO 3166-1 alpha-2. Default: CO |
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| id | string | ✓ | Número de línea. Ej: "1" |
| description | string | ✓ | Descripción del bien o servicio (máx 1000 chars) |
| quantity | number | ✓ | Cantidad (positivo) |
| unitCode | string | Unidad de medida UN/CEFACT. Default: EA (Each) | |
| unitPrice | number | ✓ | Precio unitario sin impuesto |
| lineTotal | number | ✓ | Total línea sin impuesto. Debe ser ≈ quantity × unitPrice (tolerancia ±1 COP) |
| taxAmount | number | Valor impuesto de la línea. Debe ser ≈ lineTotal × taxPercent/100 (tolerancia ±1 COP) | |
| taxPercent | number | Porcentaje impuesto. Ej: 19 para IVA 19% | |
| taxCode | string | Código impuesto DIAN. 01=IVA, 03=ICA, 04=INC. Default: 01 |
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
| taxCode | enum | ✓ | Código tributo DIAN: 01=IVA, 02=INC bebidas azucaradas, 03=ICA, 04=INC, 05=INC bolsas plásticas, 06=Carbono, 07=Combustibles, 08=Tabaco, y más. |
| taxAmount | number | ✓ | Valor total del impuesto (suma de todas las líneas) |
| taxableAmount | number | ✓ | Base gravable total |
| percent | number | ✓ | Tarifa porcentual. Ej: 19 |
| Campo | Tipo | Req. | Regla de validación |
|---|---|---|---|
| lineExtensionAmount | number | ✓ | Debe ser = Σ(lineTotal de todos los items) ±1 COP |
| taxExclusiveAmount | number | ✓ | Subtotal sin impuestos (normalmente = lineExtensionAmount) |
| taxInclusiveAmount | number | ✓ | Debe ser = taxExclusiveAmount + Σ(taxAmount de taxes) ±1 COP |
| allowanceTotalAmount | number | Total descuentos. Default: 0 | |
| chargeTotalAmount | number | Total cargos adicionales. Default: 0 | |
| payableAmount | number | ✓ | Debe ser = taxInclusiveAmount - allowanceTotalAmount + chargeTotalAmount ±1 COP |
Un documento transita por estos estados desde la creación hasta la validación DIAN:
El campo dianCode refleja el código retornado por GetStatusZip:
Todos los errores siguen el mismo formato:
{
"error": "Validation failed",
"statusCode": 400,
"details": [
{
"path": ["totals", "payableAmount"],
"message": "payableAmount (238000) debe ser taxInclusiveAmount - descuentos + cargos (240000.00)"
}
]
}
| HTTP Status | Cuándo ocurre |
|---|---|
| 200 | Consulta exitosa (GET) |
| 201 | Documento creado y encolado (POST) |
| 400 | Error de validación Zod: campos faltantes, tipos incorrectos, totales matemáticos no cuadran, DV NIT inválido, código DANE incorrecto |
| 401 | API Key o Secret inválidos / ausentes |
| 404 | Documento no encontrado |
| 500 | Error interno — revisar logs en /admin/logs |
El esquema OpenAPI 3.1 completo está disponible como JSON para herramientas, agentes de IA y generadores de clientes:
curl https://fe.damaju.com.co/documentation/openapi.json
GET /documentation/openapi.json para obtener el contrato completo de la API
en formato estándar. Esta página HTML contiene la misma información con ejemplos más detallados.
Los esquemas Zod en src/types/invoice.ts son la fuente de verdad de validación.