Crear factura
Emite una factura de consumidor final, la firma y la transmite a Hacienda en una sola llamada. Úsalo desde tu POS, Saas, ERP o checkout cuando el cliente paga y necesitas el documento tributario listo.
En lugar de armar el JSON del DTE, calcular IVA, firmar con certificado y llamar a recepción del MH, solo envías ítems y pagos. Lumen reserva el correlativo, firma, transmite y te devuelve PDF/JSON con URLs prefirmadas.
Método: POST
Endpoint
Section titled “Endpoint”https://api-sandbox.lumendte.com/v1/invoiceshttps://api.lumendte.com/v1/invoicesHeaders
Section titled “Headers”| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
Authorization | string | Sí | Bearer <access_token> (Client Credentials) |
x-company-tax-number | string | Sí | NRC de la empresa emisora (con o sin guiones) |
x-establishment-code | string | Sí | Código dado por MH para el establecimiento (ej. M001) |
Payload mínimo
Section titled “Payload mínimo”Para operaciones menores a $200 USD, la normativa permite omitir los datos del cliente. Este es el payload mínimo para emitir una factura de contado:
{ "items": [ { "description": "Servicio de consultoría", "quantity": 1, "unitPrice": 113.0 } ], "payments": [ { "paymentCode": "01", "amount": 113.0 } ]}Respuesta exitosa
Section titled “Respuesta exitosa”Status: 201 Created
Guarda generationCode y controlNumber. Las URLs pdfUrl y jsonUrl expiran en 15 minutos: descárgalas o reenvíalas al cliente de inmediato desde tu backend. No las persistas en tu base de datos para uso posterior.
{ "status": "TRANSMITTED", "generationCode": "FF84E5DB-79C5-42CE-B415-EC510C53EFB5", "controlNumber": "DTE-01-M001P001-000000000000001", "receivedStamp": "20219E9D4DC0292F4681AD759B0B0F5CA99DC23G", "environment": "SANDBOX", "pdfUrl": "https://files.example.com/invoices/1/FF84E5DB-79C5-42CE-B415-EC510C53EFB5.pdf?X-Amz-Expires=900", "jsonUrl": "https://files.example.com/invoices/1/FF84E5DB-79C5-42CE-B415-EC510C53EFB5.json?X-Amz-Expires=900"}environment: SANDBOX o PRODUCTION.
Estructura completa del payload
Section titled “Estructura completa del payload”Si la venta es de $200 USD o más, o necesitas informar datos del cliente, descuentos, productos exentos o pagos a crédito/mixtos, envía el objeto completo:
{ "customer": { "name": "Juan Perez", "email": "juan.perez@example.com", "phone": "7777-7777", "document": { "type": "DUI", "number": "01010101-1" }, "address": { "complement": "Col. Escalón, San Salvador", "departmentCode": "06", "districtCode": "13" } }, "items": [ { "sku": "PROD-001", "description": "Servicio de consultoría", "quantity": 1, "unitPrice": 113.0, "taxType": "TAXABLE", "itemType": "SERVICE", "unitOfMeasure": "59", "discount": { "amount": 0, "percentage": 0 } } ], "payments": [ { "paymentCode": "01", "amount": 56.5 }, { "paymentCode": "03", "amount": 56.5, "reference": "AUTH-998877" } ]}Líneas del documento. Mínimo 1, máximo 50.
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
description | string | Sí | Descripción del ítem |
quantity | number | Sí | Cantidad (> 0) |
unitPrice | number | Sí | Precio unitario con IVA incluido (> 0) |
sku | string | null | No | Código interno del producto |
taxType | string | No | TAXABLE (default), EXEMPT, NOT_SUBJECT |
itemType | string | No | GOODS, SERVICE (default), BOTH |
unitOfMeasure | string | No | Unidad de medida del producto. Default: "59" (Unidad). Ver Unidades de medida |
discount.amount | number | No | Descuento monetario de la línea (≥ 0). Default: 0 |
discount.percentage | number | No | Porcentaje de descuento (0–100). Default: 0 |
Significados de taxType:
Indica si el producto o servicio a facturar es gravado, exento o no sujeto a IVA.
| Valor | Significado |
|---|---|
TAXABLE | Venta gravada |
EXEMPT | Venta exenta |
NOT_SUBJECT | Venta no sujeta |
Significados de itemType:
Indica si el ítem es un producto, un servicio o ambos.
| Valor | Significado |
|---|---|
GOODS | Productos o bienes |
SERVICE | Servicio |
BOTH | Implica tanto servicio como producto a la vez |
payments
Section titled “payments”Formas de pago. Mínimo 1. Puedes enviar más de una si la transacción se pagó con varios medios (por ejemplo 50 % con tarjeta y 50 % en efectivo).
Lumen establece la condición de la operación (CAT-016) automáticamente según los pagos enviados:
| Pagos enviados | Condición |
|---|---|
Solo contado (cualquier código ≠ 13) | Contado |
Solo crédito (13) | Crédito |
Crédito (13) + contado | Mixto |
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
paymentCode | string | Sí | Código de forma de pago. Ver Formas de pago |
amount | number | Sí | Monto del pago. Envíalo siempre con el valor real de esa forma de pago. |
reference | string | No | Referencia o código de autorización. Se omite en efectivo (01) y en crédito puro |
creditTerm | string | null | Condicional | Tipo de plazo CAT-018. Requerido si paymentCode es 13 (ej. 01 = Días) |
creditPeriod | number | null | Condicional | Cantidad de días/meses/años según creditTerm (1–999). Requerido si paymentCode es 13 |
methodDetail | string | null | Condicional | Requerido si paymentCode es 99 (Otros) |
Cuando paymentCode es 13 (cuentas por pagar), debes indicar plazo y periodo. Ejemplo para un pago a crédito de 30 días:
{ "payments": [ { "paymentCode": "13", "amount": 150.0, "creditTerm": "01", "creditPeriod": 30 } ]}creditTerm (01) = días; creditPeriod (30) = 30 días de plazo.
customer
Section titled “customer”Datos del cliente. Requerido para operaciones de $200 USD o más. Si se omite (solo montos menores a $200), Lumen envía el cliente como null a Hacienda (cliente general).
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
name | string | Sí | Nombre completo del cliente |
document.type | string | Sí | DUI, NIT, PASSPORT, RESIDENT_CARD, OTHER |
document.number | string | Sí | Número de documento con o sin guiones |
email | string | No | Correo del cliente |
phone | string | No | Teléfono nacional o internacional (ej. 7777-7777, +14155552671) |
address.complement | string | No | Complemento de dirección (calle, colonia, etc.) |
address.departmentCode | string | No | Código de departamento. Ver Departamentos |
address.districtCode | string | No | Código de distrito. Ver Distritos |
Ejemplo de Solicitud
Section titled “Ejemplo de Solicitud”curl -X POST https://api-sandbox.lumendte.com/v1/invoices \ -H "Accept: application/json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer tu_access_token" \ -H "x-company-tax-number: 123456-7" \ -H "x-establishment-code: M001" \ -d '{ "items": [ { "description": "Servicio de consultoría", "quantity": 1, "unitPrice": 113.0 } ], "payments": [ { "paymentCode": "01", "amount": 113.0 } ] }'const axios = require("axios");
const config = { method: "post", url: "https://api-sandbox.lumendte.com/v1/invoices", headers: { Accept: "application/json", "Content-Type": "application/json", Authorization: "Bearer tu_access_token", "x-company-tax-number": "123456-7", "x-establishment-code": "M001", }, data: { items: [ { description: "Servicio de consultoría", quantity: 1, unitPrice: 113.0, }, ], payments: [ { paymentCode: "01", amount: 113.0, }, ], },};
axios(config) .then((response) => { console.log( "Factura aceptada:", response.data.controlNumber, response.data.pdfUrl, ); }) .catch((error) => { console.error( "Error en la solicitud:", error.response ? error.response.data : error.message, ); });import requests
response = requests.post( 'https://api-sandbox.lumendte.com/v1/invoices', headers={ 'Accept': 'application/json', 'Content-Type': 'application/json', 'Authorization': 'Bearer tu_access_token', 'x-company-tax-number': '123456-7', 'x-establishment-code': 'M001', }, json={ 'items': [ { 'description': 'Servicio de consultoría', 'quantity': 1, 'unitPrice': 113.0, }, ], 'payments': [ { 'paymentCode': '01', 'amount': 113.0, }, ], },)
if response.ok: data = response.json() print('Factura aceptada:', data['controlNumber'], data['pdfUrl'])else: print('Error en la solicitud:', response.text)Errores frecuentes
Section titled “Errores frecuentes”400 — Crédito sin plazo o periodo
Section titled “400 — Crédito sin plazo o periodo”Enviaste paymentCode 13 sin creditTerm o creditPeriod.
{ "errorCode": "INVALID_PARAMETERS", "message": [ "creditTerm is required when paymentCode is 13", "creditPeriod is required when paymentCode is 13" ], "error": "Bad Request", "statusCode": 400}Fix: Incluye creditTerm (CAT-018, ej. 01 = Días) y creditPeriod (1–999) en el pago a crédito.
422 — Rechazo de Hacienda
Section titled “422 — Rechazo de Hacienda”El MH rechazó la recepción (contribuyente inactivo, normativa o autorización de emisión).
{ "errorCode": "MH_RECEPTION_TAXPAYER_ERROR", "message": "The taxpayer is not active at the tax authority.", "statusCode": 422}Fix: Verifica que la empresa esté activa para ser emisora de factura electrónica o que la configuración para emisión de factura electrónica esté lista para el ambiente Sandbox o Producción.