Antes de empezar (común a ambas vías)
Base URL: https://api.boletitas.cl/v1 · Formato: JSON (UTF-8)
· Fechas: ISO 8601 (UTC) · Montos: pesos chilenos, enteros.
La API está disponible en el plan Pro. Genera tu API key en tu panel: Mi plan → 🔌 API (solo el dueño). La emisión usa las credenciales del SII ya cifradas de tu cuenta — tu software nunca toca la clave del SII.
bol_live_… y
sandbox bol_test_…. Con la de sandbox integras y pruebas sin emitir en el SII real,
sin gastar folios ni cupo.Endpoints
| Método | Ruta | Qué hace |
|---|---|---|
| POST | /v1/boletas | Emite una boleta. |
| POST | /v1/boletas/lote | Emite hasta 100 boletas en una llamada. |
| GET | /v1/boletas/{id} | Estado y datos de una boleta. |
| GET | /v1/boletas/{id}/pdf | Descarga el PDF de la boleta. |
| POST | /mcp | Servidor MCP (JSON-RPC) para agentes de IA. |
Autenticación
Manda tu API key en cada request, por header. Dos formas equivalentes:
Authorization: Bearer bol_live_tu_api_key # — o bien — X-API-Key: bol_live_tu_api_key
Sin una key válida → 401 no_autorizado. Trata tu bol_live_ como una
contraseña: nunca la pongas en código de front-end ni en repos públicos. ¿Se filtró? Revócala y
genera otra desde el panel.
Quickstart
De cero a una boleta emitida, con curl (usa tu key de sandbox para probar):
- Genera tu API key en el panel (Mi plan → 🔌 API).
- Emite una boleta:
curl -X POST https://api.boletitas.cl/v1/boletas \ -H "Authorization: Bearer bol_test_tu_api_key" \ -H "Content-Type: application/json" \ -d '{ "tipo": "afecta", "items": [{ "descripcion": "Corte de pelo", "precio": 15000 }] }'→ responde202con{ "id": "a1b2c3…", "estado": "en_proceso" } - Consulta el resultado (o recíbelo por webhook):
curl https://api.boletitas.cl/v1/boletas/a1b2c3… \ -H "Authorization: Bearer bol_test_tu_api_key"
Emitir una boleta
POST/v1/boletas — asíncrono: responde 202 con un
id; el resultado lo consultas por GET o te llega por webhook. La boleta admite
varias líneas.
Lo mínimo
Solo necesitas tipo y las líneas. Igual que por WhatsApp, la boleta se emite sin receptor (consumidor final) — no hace falta identificar al cliente:
{
"tipo": "afecta",
"items": [
{ "descripcion": "Corte de pelo", "precio": 15000 }
]
}
precio ya incluye IVA (es lo que paga el cliente).Con receptor (opcional) y todos los campos
{
"tipo": "afecta", // "afecta" (39, con IVA) | "exenta" (41)
"items": [
{ "descripcion": "Corte de pelo", "cantidad": 1, "precio": 15000 }
],
"receptor": { "rut": "12345678-9", "razon_social": "Cliente", "correo": "c@correo.cl" }, // opcional
"referencia_externa": "venta-8842" // opcional, tu id interno
}
Campos
| Campo | ¿Obligatorio? | Detalle |
|---|---|---|
tipo | Sí | afecta (con IVA) o exenta. |
items | Sí | Al menos una línea. Admite varias. |
items[].descripcion | Sí | Nombre del producto o servicio. |
items[].precio | Sí | Precio unitario en CLP (entero > 0). En afectas incluye IVA. |
items[].cantidad | No | Por defecto 1. |
receptor | No | rut, razon_social, correo (todos opcionales). Sin receptor = consumidor final. |
referencia_externa | No | Tu id interno. Vuelve en la respuesta y el webhook, y sirve de clave de idempotencia. |
Header opcional Idempotency-Key: <uuid> → un reintento no duplica la boleta (o usa
referencia_externa, que cumple el mismo rol).
Response 202
{ "id": "a1b2c3…", "estado": "en_proceso", "referencia_externa": "venta-8842", "creado": "…" }
Estados de una boleta
La emisión es asíncrona: el 202 confirma que la recibimos, no que ya está en el SII.
Una boleta pasa por:
en_proceso →
emitida (con folio + PDF) · o ·
error (con motivo)
Te enteras del resultado de dos formas: por webhook (te avisamos — recomendado) o por GET
(consultas tú por el id). Normalmente tarda unos segundos.
Emisión masiva (lote)
POST/v1/boletas/lote — emite muchas boletas en una sola
llamada (ideal para automatizar desde una planilla/Excel). Máximo 100 por llamada; cuenta como
1 request contra el rate limit. Devuelve un resultado por boleta (éxito parcial).
Cada boleta del arreglo acepta los mismos campos que el POST individual — incluido el
receptor (opcional): puedes ponerlo en algunas y omitirlo en otras.
{
"boletas": [
{ "tipo": "afecta", "items": [{ "descripcion": "Producto A", "precio": 9990 }], "referencia_externa": "fila-1" },
{ "tipo": "afecta", "items": [{ "descripcion": "Producto B", "precio": 15000 }],
"receptor": { "rut": "12345678-9", "correo": "cliente@correo.cl" }, // opcional, por boleta
"referencia_externa": "fila-2" }
]
}
referencia_externa única a cada boleta: identifica la fila
y actúa como clave de idempotencia — si reintentas el lote, las que ya se crearon no se
duplican.Response 200
{
"total": 2, "aceptadas": 2, "con_error": 0,
"resultados": [
{ "referencia_externa": "fila-1", "ok": true, "id": "a1b2…", "estado": "en_proceso" },
{ "referencia_externa": "fila-2", "ok": true, "id": "c3d4…", "estado": "en_proceso" }
]
}
Si superas tu cupo a mitad del lote, las que exceden vuelven con ok:false /
cupo_agotado — el resto se emite igual.
Consultar el resultado
GET/v1/boletas/{id} — el estado y los datos de la boleta en
cualquier momento.
{
"id": "a1b2c3…",
"estado": "emitida", // en_proceso | emitida | error
"tipo": "afecta",
"folio": 1234,
"total": 15000,
"pdf_url": "https://api.boletitas.cl/v1/boletas/a1b2c3…/pdf",
"referencia_externa": "venta-8842",
"motivo": null, // el detalle del fallo cuando estado = "error"
"emitida_en": "2026-08-13T14:05:00Z"
}
| Campo | Detalle |
|---|---|
estado | en_proceso, emitida o error. |
folio | Folio del SII (cuando está emitida). |
total | Monto total de la boleta en CLP. |
pdf_url | Link al PDF (disponible cuando está emitida). |
motivo | Por qué falló, cuando estado = "error". |
emitida_en | Cuándo se resolvió (ISO 8601 UTC). |
GET/v1/boletas/{id}/pdf — te redirige al PDF (link
temporal firmado). Úsalo cuando la boleta esté emitida.
Idempotencia
Reintentar una emisión que "pareció fallar" (un timeout de red, por ejemplo) no debe duplicar la boleta. Dos formas de garantizarlo:
- Header
Idempotency-Key: <uuid>en elPOST, o referencia_externaen el cuerpo (cumple el mismo rol).
Si repites el mismo request con la misma clave, te devolvemos la misma boleta (mismo
id), sin re-emitir. En el lote, la clave de cada boleta es su
referencia_externa — por eso conviene que sea única por fila.
Webhook (recomendado)
En vez de estar consultando, deja que te avisemos. Configura tu webhook_url en el panel
(🔌 API → Webhook). Cuando una boleta queda emitida o falla, te hacemos un POST
con el mismo cuerpo del GET de consulta.
Verifica la firma
Al guardar tu URL te damos un webhook_secret. Firmamos cada envío con él; verifícalo
antes de confiar en el evento:
X-Boletitas-Signature: HMAC-SHA256(cuerpo_crudo, webhook_secret)
// Node.js — en tu endpoint de webhook const crypto = require("crypto"); const esperada = crypto.createHmac("sha256", WEBHOOK_SECRET) .update(cuerpoCrudo) // el body tal cual llegó, sin re-serializar .digest("hex"); if (esperada !== req.headers["x-boletitas-signature"]) { return res.status(401).end(); // firma inválida → ignóralo }
Reintentos
Responde 2xx apenas lo recibas (idealmente encola y procesa después). Si no respondes
2xx, reintentamos con backoff hasta 6 veces. Haz tu endpoint idempotente
(dedupe por el id de la boleta): un evento podría llegarte más de una vez.
Límites de uso
Hasta ~120 requests por minuto por API key (el lote cuenta como 1 request, así que emitir
en lotes rinde mucho más). Al pasarte respondemos 429 rate_limit con un header
Retry-After (segundos a esperar). ¿Tu volumen necesita más? Escríbenos y lo subimos.
VÍA 2 · PARA AGENTES DE IA
🤖 MCP · emisión por lenguaje natural
Boletitas expone un servidor MCP (Model Context Protocol) para que asistentes de IA (Claude, ChatGPT y otros compatibles) emitan boletas por lenguaje natural — «emite una boleta afecta de $15.000 por un corte de pelo», o «toma este Excel y emite una boleta por cada fila». El agente interpreta la orden y llama a las herramientas; por debajo es la misma API (mismo plan Pro, mismo cupo, mismo sandbox, misma idempotencia).
Endpoint: https://api.boletitas.cl/mcp (JSON-RPC 2.0 sobre HTTP, POST) Auth: Authorization: Bearer bol_live_tu_api_key
Conecta tu cliente
Config típica de un cliente MCP (Claude Desktop y compatibles):
{
"mcpServers": {
"boletitas": {
"url": "https://api.boletitas.cl/mcp",
"headers": { "Authorization": "Bearer bol_live_tu_api_key" }
}
}
}
Herramientas
| Herramienta | Qué hace |
|---|---|
emitir_boleta | Emite una boleta (tipo + líneas; receptor opcional). |
emitir_boletas | Emite hasta 100 en una llamada (desde una planilla/Excel). |
consultar_boleta | Estado, folio y PDF de una boleta por su id. |
Ejemplo (JSON-RPC)
Listar las herramientas disponibles:
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }
Emitir una boleta llamando a la herramienta:
{
"jsonrpc": "2.0", "id": 2, "method": "tools/call",
"params": {
"name": "emitir_boleta",
"arguments": {
"tipo": "afecta",
"items": [{ "descripcion": "Corte de pelo", "precio": 15000 }]
}
}
}
REFERENCIA
Cupo, sandbox y planes
Pro incluye 500 documentos/mes por API (se resetean cada mes; no se acumulan). ¿Necesitas más? Compra paquetes desde el panel (pago único, vencen a 12 meses; se consumen recién después de agotar la base del mes).
Al agotar el cupo, la API responde 429 cupo_agotado. Importante: tu canal WhatsApp
sigue ilimitado — el tope es solo del canal API, la operación humana nunca se corta.
Sandbox (bol_test_): marca la boleta como emitida con un folio ficticio, sin
tocar el SII, sin gastar folios ni cupo. Perfecto para integrar y probar tu webhook de punta a punta
antes de pasar a bol_live_.
Errores
Todos los errores llegan con esta forma; el code es estable (programa contra él, no
contra el mensaje):
{ "error": { "code": "datos_invalidos", "message": "Datos inválidos.", "detalles": ["items no puede estar vacío…"] } }
| HTTP | code | Qué pasó |
|---|---|---|
| 401 | no_autorizado | API key inválida, ausente o revocada. |
| 403 | plan_no_incluye_api | Tu plan no es Pro. |
| 404 | no_encontrada | No existe una boleta con ese id. |
| 422 | datos_invalidos | Faltan campos o son inválidos (ver detalles). |
| 422 | lote_muy_grande | Más de 100 boletas en un lote. Divídelo. |
| 429 | cupo_agotado | Sin documentos disponibles. Compra un paquete. |
| 429 | rate_limit | Muchas solicitudes por minuto (ver Retry-After). |
| 500 | error_interno | Algo falló de nuestro lado. Reintenta; si persiste, escríbenos. |
Referencia rápida
| Base URL | https://api.boletitas.cl/v1 |
| Auth | Authorization: Bearer bol_live_… o X-API-Key |
| Tipos | afecta (39, con IVA) · exenta (41) |
| Máx. por lote | 100 boletas por llamada |
| Rate limit | ~120 req/min por API key |
| Cupo Pro | 500 documentos/mes + paquetes |
| Webhook | firmado (HMAC-SHA256), hasta 6 reintentos con backoff |
Boletitas · API v1 · ¿Dudas o necesitas más volumen? Escríbenos desde boletitas.cl. Referencia OpenAPI interactiva en camino.