API de Facturación Electrónica
Emite documentos tributarios electrónicos (DTE) ante el SII de Chile desde cualquier sistema, con una sola llamada HTTP. Diseñada para integradores, ERPs y plataformas de software.
Introducción
La API de Lael permite emitir, consultar y descargar documentos tributarios electrónicos
ante el Servicio de Impuestos Internos (SII) de Chile. Está basada en REST, usa JSON en
todas las respuestas y sigue el patrón asíncrono: el request inicial
retorna HTTP 202 de forma inmediata con un tracking_id, y el resultado
final se entrega vía webhook.
URL base
# Producción
https://api.lael.cl/v1
# Sandbox / Certificación SII (documentos sin validez legal)
https://apicert.lael.cl/v1
api.lael.cl para producción y apicert.lael.cl para el
sandbox de certificación del SII (documentos válidos técnicamente pero sin
efecto tributario real). El ambiente queda determinado por el dominio y por el prefijo de tu
API Key (lael_test_ en sandbox vs lael_live_ en producción).
Autenticación
Todas las rutas requieren el header X-API-Key con tu API Key:
X-API-Key: lael_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Las API Keys son secretas y deben transmitirse únicamente desde tu servidor (nunca desde el browser o app móvil). Si una key es comprometida, puedes revocarla desde el portal de partners y generar una nueva.
| Prefijo | Ambiente | Efecto SII |
|---|---|---|
lael_test_ | Sandbox | SII certificación — sin validez tributaria |
lael_live_ | Producción | SII producción — documentos válidos legalmente |
Sandbox vs Producción
| Característica | Sandbox | Producción |
|---|---|---|
| API Key | lael_test_... | lael_live_... |
| SII | Ambiente certificación | Ambiente producción |
| Folios CAF | CAFs de prueba | CAFs reales del emisor |
| Validez legal | No | Sí |
| Cobro | Gratis | Según plan |
| Disponibilidad | Inmediata al registrarse | Requiere aprobación |
Inicio rápido (5 minutos)
1. Crea tu cuenta y obtén una API Key sandbox.
2. Sube tu certificado .p12 en el portal o vía API.
3. Emite tu primer DTE:
curl -X POST https://apicert.lael.cl/v1/dte/generar \
-H "X-API-Key: lael_test_xxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"Documento": {
"Encabezado": {
"IdentificacionDTE": { "TipoDTE": 33, "FechaEmision": "2026-05-30" },
"Emisor": { "Rut": "76.123.456-7", "RazonSocial": "Mi Empresa SpA",
"GiroEmis": "Servicios TI", "DireccionOrigen": "Av. X 123",
"ComunaOrigen": "Santiago", "Ciudad": "Santiago" },
"Receptor": { "Rut": "12.345.678-9", "RazonSocial": "Cliente SA",
"Direccion": "Calle Y 456", "Comuna": "Las Condes" },
"Totales": { "MontoNeto": 100000, "TasaIVA": 19, "IVA": 19000,
"MontoTotal": 119000 }
},
"Detalles": [{ "Nombre": "Servicio de desarrollo", "Cantidad": 1,
"Precio": 119000, "MontoItem": 119000 }]
},
"Certificado": { "Rut": "76123456-7", "Password": "mi_password_p12" }
}'
<?php
// Con SDK PHP: composer require lael-cl/sdk
use LaelCl\Client;
use LaelCl\Dte\FacturaAfecta;
$lael = new Client('lael_test_xxxxxxxxxxxxxxxx');
$factura = FacturaAfecta::make()
->emisor('76.123.456-7', 'Mi Empresa SpA')
->receptor('12.345.678-9', 'Cliente SA')
->agregar('Servicio de desarrollo', cantidad: 1, precio: 100000)
->total(119000, neto: 100000, iva: 19000);
$resultado = $lael->dte()->emitir($factura)->esperar(timeoutSeg: 60);
echo $resultado->folio; // Folio asignado
echo $resultado->urls->pdf; // URL permanente del PDF
4. Consulta el estado con GET /v1/dte/status/{tracking_id} o espera el webhook.
Totales sí
declaras MontoNeto e IVA por separado.
POST /v1/dte/generar
Emite un DTE ante el SII. Responde HTTP 202 de forma inmediata con un tracking_id. El documento se procesa en segundo plano.
Body (JSON)
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
Documento | object | Requerido | Estructura del DTE |
Documento.Encabezado.IdentificacionDTE.TipoDTE | int | Requerido | Tipo de documento (ver tabla) |
Documento.Encabezado.IdentificacionDTE.FechaEmision | string | Requerido | Fecha contable AAAA-MM-DD |
Documento.Encabezado.Emisor | object | Requerido | Datos del emisor |
Documento.Encabezado.Receptor | object | Requerido | Datos del receptor |
Documento.Encabezado.Totales.MontoTotal | int | Requerido | Monto total en CLP (entero) |
Documento.Detalles | array | Requerido | Líneas de detalle (mín. 1) |
Documento.Referencia | array|null | Opcional | Requerido para NC (61), ND (56) y exportación 111/112 |
Certificado.Rut | string | Opcional | RUT del cert. Si omites, usa el del vault |
Certificado.Password | string | Opcional | Password del .p12. Si omites, usa el del vault |
IdempotencyKey | string | Opcional | Clave única por request para evitar duplicados en reintentos |
Respuesta — 202 Accepted
{
"ok": true,
"data": {
"tracking_id": "01HWXYZ1234ABCDEF567890AB",
"status": "pendiente",
"tipo_dte": 33,
"rut_emisor": "76.123.456-7",
"sandbox": false,
"idempotency_key": "mi-erp-doc-9999",
"urls": {
"status": "https://api.lael.cl/v1/dte/status/01HWXYZ...",
"pdf": "https://api.lael.cl/v1/dte/doc/a1b2c3d4e5f6.../pdf",
"xml": "https://api.lael.cl/v1/dte/doc/b2c3d4e5f6a1.../xml"
},
"estimado_ms": 8000
}
}
GET /v1/dte/status/{tracking_id}
Consulta el estado de un documento. Úsalo para polling o para verificar el resultado final.
Valores de status
| Valor | Significado |
|---|---|
pendiente | En cola, esperando procesamiento |
procesando | Firmando y enviando al SII |
completado | SII aceptó el documento |
fallido | Falló tras 5 intentos — revisar error |
cancelado | Cancelado manualmente |
Respuesta cuando status: "completado"
{
"ok": true,
"data": {
"tracking_id": "01HWXYZ...",
"status": "completado",
"tipo_dte": 33,
"folio": 125,
"rut_emisor": "76.123.456-7",
"monto_total": 119000,
"emitido_at": "2026-05-30T14:32:11-04:00",
"sii": {
"track_id": "987654321",
"estado": "EPR",
"glosa": "Envio Procesado"
},
"urls": { "pdf": "...", "xml": "..." },
"intentos": 1,
"error": null
}
}
GET /v1/dte/doc/{url_token}/pdf
Descarga o muestra el PDF del DTE. No requiere X-API-Key — la URL es opaca y permanente (válida 6 años).
Content-Type: application/pdf
GET /v1/dte/doc/{url_token}/xml
Descarga el XML original del DTE, firmado y con el timbre SII. Custodiado por 6 años. No requiere X-API-Key.
Content-Type: application/xml; charset=utf-8
POST /v1/partner/webhook
Registra o actualiza la URL de webhook donde Lael enviará las notificaciones cuando el SII procese un DTE.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
url | string | Requerido | URL HTTPS de tu servidor |
eventos | array | Opcional | Default: todos los eventos |
Respuesta incluye el secret para verificar firmas HMAC. Guárdalo en un lugar seguro.
GET /v1/partner/uso
Retorna el resumen de consumo del período (excluye sandbox). Útil para billing interno.
{
"ok": true,
"data": {
"periodo": "2026-05",
"total_emitidos": 87,
"total_rechazados": 2,
"desglose_tipo": [{ "tipo_dte": 33, "emitidos": 85 }]
}
}
POST /v1/partner/cert
Sube el certificado digital .p12 de un RUT emisor. La password se cifra con AES-256 — nunca se almacena en texto plano.
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
rut_emisor | string | Requerido | RUT sin puntos, con guión |
p12_base64 | string | Requerido | Contenido del .p12 en base64 |
password | string | Requerido | Password del certificado |
curl -X POST https://api.lael.cl/v1/partner/cert \
-H "X-API-Key: lael_live_xxxx" \
-H "Content-Type: application/json" \
-d '{
"rut_emisor": "76123456-7",
"p12_base64": "'"$(base64 -w0 /ruta/certificado.p12)"'",
"password": "mi_password"
}'
Tipos de DTE soportados
| TipoDTE | Nombre | Referencia | Notas |
|---|---|---|---|
33 |
Factura Afecta | — | Documento más común. IVA 19%. |
34 |
Factura Exenta | — | Sin IVA (servicios exentos). |
39 |
Boleta Electrónica | — | RUT receptor 66.666.666-6 si no hay individualización. |
41 |
Boleta Exenta | — | Sin IVA + sin individualización de receptor. |
46 |
Factura de Compra | — | El emisor compra al receptor (retención IVA). |
52 |
Guía de Despacho | — | Requiere TipoDespacho e IndTraslado. |
56 |
Nota de Débito | Sí | Aumenta el valor de un DTE. Requiere Referencia. |
61 |
Nota de Crédito | Sí | Anula o disminuye. Requiere Referencia. |
110 |
Factura de Exportación | — | Receptor: 55.555.555-5. |
111 |
NC de Exportación | Sí | Requiere Referencia. |
112 |
ND de Exportación | Sí | Requiere Referencia. |
Webhooks
Lael envía un POST a tu URL cuando el estado de un DTE cambia. El body es JSON y está firmado con HMAC-SHA256.
Eventos disponibles
| Evento | Cuándo se dispara |
|---|---|
dte.completado | SII aceptó el documento (EPR/SOK) |
dte.rechazado | SII rechazó el documento (RCH) |
dte.error | Falló tras 5 intentos de envío |
Estructura del payload
{
"evento": "dte.completado",
"tracking_id": "01HWXYZ1234ABCDEF567890AB",
"timestamp": "2026-05-30T18:32:15Z",
"sandbox": false,
"data": {
"tipo_dte": 33,
"folio": 125,
"rut_emisor": "76.123.456-7",
"monto_total":119000,
"sii_estado": "EPR",
"urls": {
"pdf": "https://api.lael.cl/v1/dte/doc/.../pdf",
"xml": "https://api.lael.cl/v1/dte/doc/.../xml"
}
}
}
Verificar la firma HMAC-SHA256
$body = file_get_contents('php://input');
$secret = 'tu_webhook_secret';
$firma = 'sha256=' . hash_hmac('sha256', $body, $secret);
if (!hash_equals($firma, $_SERVER['HTTP_X_LAEL_SIGNATURE'])) {
http_response_code(401);
exit;
}
$data = json_decode($body, true);
// Procesar $data['evento'], $data['tracking_id'], etc.
const crypto = require('crypto');
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const firma = 'sha256=' + crypto
.createHmac('sha256', process.env.LAEL_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (firma !== req.headers['x-lael-signature']) {
return res.status(401).send('Unauthorized');
}
const data = JSON.parse(req.body);
console.log(data.evento, data.tracking_id);
res.sendStatus(200);
});
import hmac, hashlib
from flask import request, abort
SECRET = b'tu_webhook_secret'
@app.route('/webhook', methods=['POST'])
def webhook():
body = request.get_data()
firma = 'sha256=' + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
recibida = request.headers.get('X-Lael-Signature', '')
if not hmac.compare_digest(firma, recibida):
abort(401)
data = request.json
return '', 200
Manejo de errores
Todas las respuestas de error usan el campo ok: false y error con descripción en español.
{
"ok": false,
"error": "TipoDTE 99 no es soportado. Soportados: 33, 34, 39, 41, 46, 52, 56, 61, 110, 111, 112"
}
| HTTP | Causa común |
|---|---|
400 | Body malformado o falta JSON |
401 | X-API-Key ausente, inválida o revocada |
403 | Token sandbox intentando acceder a recurso de producción |
404 | tracking_id no encontrado o no pertenece al partner |
422 | Payload válido pero con datos inválidos (RUT, TipoDTE, etc.) |
429 | Rate limit excedido (300 req/min por token) |
500 | Error interno — contáctanos si persiste |
Límites y tarifas
Rate limits
| Límite | Valor |
|---|---|
| Requests por minuto (por token) | 300 |
| Body máximo del request | 5 MB |
| Reintentos automáticos ante falla SII | 5 (backoff: 2 min → 15 → 1h → 6h → 12h) |
Planes
- ✓ 50 DTEs en producción
- ✓ Sandbox SII ilimitado
- ✓ Todos los tipos de documento
- ✗ Sin webhooks
- ✗ Sin renovación automática
- ✓ 100 DTEs incluidos por mes
- ✓ $20 por DTE adicional
- ✓ Sandbox SII ilimitado
- ✓ Todos los tipos de documento
- ✓ Webhooks incluidos
- ✓ DTEs ilimitados
- ✓ Sin cargos adicionales
- ✓ Sandbox SII ilimitado
- ✓ Todos los tipos de documento
- ✓ Webhooks incluidos
Servicios de Certificación SII (pago único + IVA)
Cuando tu sistema necesita certificarse ante el SII para emitir un tipo de documento, puedes contratar el servicio de certificación con Lael. Realizamos el proceso completo por ti.
| Set de Certificación | Tipos DTE | Precio |
|---|---|---|
| Boleta Electrónica | 39 / 41 | $29.990 |
| Factura Afecta + ND + NC | 33 / 56 / 61 | $39.990 |
| Factura Exenta + ND + NC | 34 / 56 / 61 | $49.990 |
| Guía de Despacho | 52 | $59.990 |
| Factura de Compra | 46 | $69.990 |
| Exportación — Factura + ND + NC | 110 / 111 / 112 | $79.990 |
Licencias de Software (pago único)
Para integradores que prefieren operar en su propia infraestructura sin suscripción mensual.
- ✓ Licencia perpetua por RUT emisor
- ✓ Emisión de todos los tipos DTE
- ✓ Gestión de folios CAF incluida
- ✓ Actualizaciones por 12 meses
- ✓ 1 hora de onboarding técnico
- ✓ Wizard de certificación SII
- ✓ Todos los sets de certificación
- ✓ Compatible con API DTE + Folios
- ✓ Actualizaciones por 12 meses
- ✓ 1 hora de onboarding técnico
SDK PHP
Instala el SDK oficial vía Composer para una integración con tipado fuerte, polling automático y manejo de errores:
composer require lael-cl/sdk
<?php
use LaelCl\Client;
use LaelCl\Dte\FacturaAfecta;
use LaelCl\Dte\NotaCredito;
use LaelCl\Exception\DteRechazadoException;
$lael = new Client('lael_live_xxxxxxxx');
// ─── Emitir factura afecta ───────────────────────────────────────
$factura = FacturaAfecta::make()
->emisor('76.123.456-7', 'Mi Empresa SpA')
->receptor('12.345.678-9', 'Cliente SA')
->agregar('Consultoría', cantidad: 1, precio: 500000)
->total(595000, neto: 500000, iva: 95000);
try {
$r = $lael->dte()->emitir($factura)->esperar(timeoutSeg: 60);
echo "Folio: " . $r->folio . PHP_EOL;
echo "PDF: " . $r->urls->pdf . PHP_EOL;
echo "Track: " . $r->sii->trackId . PHP_EOL;
} catch (DteRechazadoException $e) {
// SII rechazó el documento
error_log("DTE rechazado: " . $e->getMessage());
}
// ─── Verificar webhook ────────────────────────────────────────────
use LaelCl\Webhook\WebhookVerifier;
$body = file_get_contents('php://input');
$firma = $_SERVER['HTTP_X_LAEL_SIGNATURE'];
if (WebhookVerifier::verify($body, $firma, 'mi_secret')) {
$data = json_decode($body, true);
// $data['evento'] === 'dte.completado'
}
¿Listo para integrar?
Crea tu cuenta, obtén una API Key sandbox y emite tu primer DTE en minutos.