Para empezar
La API vive en https://biggercontrol.com/api/publica/v1/ (el entorno de pruebas, en prueba.biggercontrol.com). Habla JSON, todos los textos de error vienen en español y los importes son siempre texto con dos decimales ("575.00"), nunca números, para no perder centavos.
- Cree una llave. Quien administra la empresa entra a Administración › Conexiones, le pone un nombre (por ejemplo «Tienda en línea») y elige su permiso: solo leer, o leer y emitir facturas. La llave se muestra una sola vez: cópiela en ese momento. Si se pierde, se revoca y se crea otra.
- Llámela desde su servidor, nunca desde una página web: la API no admite CORS y rechaza los pedidos que traen la cabecera
Origin. Una llave dentro de una página la ve cualquiera. - Pruebe primero. Las llaves del entorno de pruebas empiezan con
bc_test_y las de producción conbc_live_; una no sirve en el entorno de la otra.
curl https://biggercontrol.com/api/publica/v1/facturas?limite=5 \
-H "Authorization: Bearer bc_live_SU_LLAVE_AQUI"
La empresa sale siempre de la llave: el pedido no dice (ni puede decir) de qué empresa es. Una llave solo ve lo de su empresa.
Responsabilidad fiscal
Una factura emitida por la API es una factura fiscal de su empresa. Gasta un número de su CAI, queda en el libro de ventas y en la declaración del ISV, igual que una emitida en pantalla. Quien conecta el sistema responde por lo que ese sistema emite: los datos del cliente, los precios, el ISV y que no se emita dos veces lo mismo. Pruebe siempre con una llave bc_test_ en el entorno de pruebas, y use una Idempotency-Key distinta por cada pedido de su tienda.
Autenticación
Cada pedido lleva la llave en la cabecera Authorization: Bearer <llave>. Bigger Control solo guarda una huella de la llave (HMAC-SHA256 con un secreto del servidor): ni nosotros podemos volver a mostrarla. Una llave revocada deja de servir en el acto.
| Permiso | Puede |
|---|---|
| Solo leer | Consultar facturas, clientes, productos y cobros pendientes. |
| Leer y emitir facturas | Lo anterior y además emitir facturas. No puede anular, registrar pagos ni cambiar nada de la empresa. |
Errores
Toda respuesta que no sale bien trae el código HTTP y un JSON con un codigo (para su programa) y un mensaje en español (para una persona):
{
"error": {
"codigo": "pedido_rechazado",
"mensaje": "Escriba al menos una línea de la factura.",
"faltas": ["Escriba al menos una línea de la factura."]
}
}
| HTTP | codigo | Qué pasó |
|---|---|---|
| 400 | idempotency_key_falta, idempotency_key_mala, cuerpo_malo, parametro_malo | Falta o no vale la Idempotency-Key, el cuerpo no es un objeto JSON de a lo más 64 KB, o un parámetro de la dirección no es válido. |
| 401 | llave_falta, llave_invalida | No viene la llave, o no existe, es de otro entorno o fue revocada. |
| 403 | sin_permiso, origen_no_permitido | La llave no tiene ese permiso (por ejemplo, emitir con una llave de solo leer), la empresa no tiene el módulo (productos necesita Inventario) o el pedido viene de una página web. |
| 404 | no_existe, no_encontrada | Esa dirección o ese recurso no existe. |
| 405 | metodo_no_permitido | Ese método no existe para esa dirección (la cabecera Allow dice cuáles sí). |
| 409 | idempotency_key_reusada, ocupado | La Idempotency-Key ya se usó con una factura distinta, o hay otra factura emitiéndose en ese instante (repita el pedido). |
| 415 | tipo_no_permitido | Falta Content-Type: application/json. |
| 422 | pedido_rechazado, campo_no_permitido | Los datos no pasan las reglas (RTN, montos, líneas…; faltas trae la lista) o el pedido trae campos que la API no acepta. |
| 423 | empresa_bloqueada | El sistema bloqueó la emisión de la empresa por seguridad. Se puede leer, no emitir; avise a quien administra. |
| 429 | limite_excedido | Demasiados pedidos (vea Límites). La cabecera Retry-After dice cuántos segundos esperar. |
| 503 | reintentar, no_disponible | No se pudo confirmar si la factura salió: repita el mismo pedido con la misma Idempotency-Key. |
Límites
- 100 pedidos por minuto por llave. Pasado el límite,
429conRetry-After. Otra llave de la misma empresa no se ve afectada. - Una dirección que manda muchas llaves que no valen se frena un rato, aunque después mande una buena.
- El cuerpo de un pedido puede pesar hasta 64 KB. Las listas se piden de a 50 como máximo.
- Una empresa puede tener hasta 10 llaves vigentes y 5 webhooks activos.
Facturas
GET /facturas: la lista, de la más nueva a la más vieja. Parámetros opcionales: buscar (por número o cifras de él, nombre del cliente sin importar mayúsculas ni tildes, o RTN), limite (1 a 50, por omisión 25), antes (el siguiente de la página anterior) y estado (emitida o anulada).
curl "https://biggercontrol.com/api/publica/v1/facturas?limite=2&estado=emitida" \
-H "Authorization: Bearer bc_live_SU_LLAVE_AQUI"
{
"facturas": [
{ "correlativo": "000-001-01-00000012", "fecha": "2026-10-16", "cliente": "TIENDA S.A.",
"rtn": "08019999999999", "total": "575.00", "estado": "emitida", "cobro": null }
],
"siguiente": "000-001-01-00000011",
"total": 12
}
siguiente es null cuando no hay más páginas; total cuenta todas las del filtro.
GET /facturas/{correlativo}: una factura con sus líneas.
{
"factura": {
"correlativo": "000-001-01-00000012", "estado": "emitida", "motivo": null, "cobro": null,
"fecha": "2026-10-16", "cliente": "TIENDA S.A.", "rtn_cliente": "0801-9999-999999",
"condicion": "Contado", "vence": "", "cai": "...", "orden_compra": "",
"lineas": [ { "cantidad": "2", "descripcion": "Camisa blanca", "precio": "250.00", "descuento": "0.00", "total": "500.00" } ],
"descuento": "0.00", "subtotal": "500.00", "gravado": "500.00", "importe_exento": "0.00", "isv": "75.00", "total": "575.00"
}
}
Una factura anulada trae "estado": "anulada" y su motivo.
Emitir una factura
POST /facturas con una llave de leer y emitir facturas. Usa el mismo motor que la pantalla de Nueva factura: el siguiente número del CAI vigente, el ISV, el candado y todas las validaciones. El número, la fecha y la clave de emisión los pone el sistema: no se mandan (si vienen, el pedido se rechaza con campo_no_permitido).
La cabecera Idempotency-Key es obligatoria (de 8 a 64 letras, números, guion o guion bajo; por ejemplo el número de pedido de su tienda). Si la red falla y usted repite el pedido con la misma clave y el mismo contenido, recibe la misma factura (200, "repetida": true, cabecera Idempotent-Replayed: true) y no se emite otra. La misma clave con un contenido distinto es 409.
curl -X POST https://biggercontrol.com/api/publica/v1/facturas \
-H "Authorization: Bearer bc_live_SU_LLAVE_AQUI" \
-H "Idempotency-Key: pedido-tienda-1001" \
-H "Content-Type: application/json" \
-d '{
"cliente": "TIENDA EN LINEA S.A.",
"rtn": "08019999999999",
"condicion": "Contado",
"lineas": [ { "descripcion": "Camisa blanca", "cantidad": "2", "precio": "250" } ]
}'
HTTP 201
{ "factura": { "correlativo": "000-001-01-00000012", "fecha": "2026-10-16", "total": "575.00", "estado": "emitida", "repetida": false } }
| Campo | Qué es |
|---|---|
cliente (obligatorio) | Nombre o razón social del cliente. |
rtn (obligatorio) | RTN del cliente: 14 números (se aceptan guiones). Para consumidor final use el que su empresa use en pantalla. |
lineas (obligatorio) | De 1 a 6 líneas: descripcion, cantidad y precio (texto con cifras, precio sin ISV); opcional exento: true si no paga ISV y, con el módulo Inventario, producto (el id de GET /productos) para que la venta baje la existencia. |
condicion | "Contado" o "Crédito". Sin ella se toma como crédito. |
descuento | Descuento en lempiras, como texto (por omisión, ninguno). |
vence, plazo_meses | Vencimiento (AAAA-MM-DD) o meses de crédito, solo en crédito. |
cobro, orden_compra, ingreso | Referencias opcionales que salen impresas en la factura. |
bodega | Con Inventario, el id de la bodega de donde salen los productos. |
La respuesta puede traer avisos (por ejemplo, sin existencia suficiente): no impiden la factura.
Si recibe 503 (reintentar), no se sabe si la factura salió: repita el pedido con la misma Idempotency-Key. Nunca genere una clave nueva para reintentar.
Clientes
GET /clientes: el directorio de clientes, los más usados primero. Parámetros: q (busca en el nombre y el RTN) y limite (1 a 500).
{ "clientes": [ { "rtn": "08019999999999", "nombre": "TIENDA S.A.", "nombre_comercial": "", "direccion": "", "telefono": "", "correo": "" } ], "total": 1 }
Productos
GET /productos: el catálogo con su existencia. Necesita que a la empresa le hayan activado el módulo Inventario (si no, 403). No incluye costos.
{
"productos": [ { "id": 3, "codigo": "CAM-01", "barras": "", "nombre": "Camisa blanca", "tipo": "producto", "unidad": "pieza",
"precio": "250.00", "gravado": true, "activo": true, "existencia": "10", "minimo": "5", "bajo_minimo": false,
"por_bodega": { "1": "10" } } ],
"bodegas": [ { "id": 1, "nombre": "Principal", "por_omision": true, "activa": true } ]
}
Un servicio ("tipo": "servicio") no lleva existencia: existencia va vacía.
Cobros pendientes
GET /cobros/pendientes: lo que los clientes deben (facturas a crédito con saldo), agrupado por cliente.
{ "hoy": "2026-10-17", "pendiente": "1725.00", "vencido": "0.00", "n": 3,
"clientes": [ { "cliente": "TIENDA S.A.", "rtn": "08019999999999", "debe": "1725.00", "vencido": "0.00",
"facturas": [ { "correlativo": "000-001-01-00000012", "fecha": "2026-10-16", "vence": "2026-11-15",
"total": "575.00", "pagado": "0.00", "saldo": "575.00", "dias_atraso": 0 } ] } ] }
Asistentes de IA (MCP)
bigger Control tiene un servidor MCP remoto (Model Context Protocol) para que su asistente de IA, como Claude o ChatGPT, consulte la empresa y prepare facturas por usted. Vive en https://biggercontrol.com/mcp (el entorno de pruebas, en prueba.biggercontrol.com/mcp), usa el transporte Streamable HTTP con mensajes JSON-RPC 2.0 por POST y habla la versión 2026-07-28 del protocolo y las anteriores (2025-11-25, 2025-06-18 y 2025-03-26, con initialize). No ofrece canal GET, y, como la API pública, no admite CORS ni pedidos con Origin.
Cómo conectarlo
- Claude (claude.ai) y ChatGPT piden OAuth: quien administra abre Administración › Conexiones, copia la dirección y la pega al añadir un conector personalizado (los nombres de los menús dependen del asistente y de su plan). Se abre la pantalla de bigger Control, elige solo leer o leer y emitir, aprueba y escribe su contraseña. La conexión queda en la lista de llaves con su nombre y fecha (por ejemplo «Claude · 17/10/2026») y se revoca desde ahí, en el acto.
- Claude Code, Claude Desktop y Cursor dejan poner una cabecera: use una llave de la API como
Authorization: Bearer <llave>.
claude mcp add --transport http bigger-control https://biggercontrol.com/mcp \
--header "Authorization: Bearer bc_live_SU_LLAVE_AQUI"
OAuth 2.1
Sin token, /mcp contesta 401 con WWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource/mcp". Los metadatos están en /.well-known/oauth-protected-resource y /.well-known/oauth-authorization-server (los únicos con CORS abierto: son públicos y de solo lectura). Hay registro dinámico de clientes públicos (POST /oauth/register), PKCE con S256 obligatorio y el parámetro resource. El código de autorización vale 2 minutos y un uso; el token de acceso, 1 hora; el de renovación, 30 días y también un uso (cada renovación entrega un par nuevo, y presentar uno ya usado revoca la conexión). Todo secreto se guarda con HMAC, como las llaves. Los tokens solo valen en /mcp, atados a una empresa y a un permiso.
Herramientas
| Herramienta | Qué hace | Permiso |
|---|---|---|
resumen_del_mes | Facturado con y sin ISV, emitidas, anuladas, ISV del mes y lo que deben los clientes. | Leer |
buscar_facturas | Por número, cliente o RTN, y por estado. | Leer |
ver_factura | Una factura completa. | Leer |
clientes | El directorio de clientes. | Leer |
cobros_pendientes | Lo que deben, con vencidas y días de atraso. | Leer |
productos | Catálogo con existencias (si la empresa tiene Inventario). | Leer |
preparar_factura | Vista previa con totales y el número que tocaría, más una confirmacion de un solo uso que vence en 10 minutos. No emite. | Leer y emitir |
emitir_factura | Emite la factura preparada. Exige la confirmacion; repetir la misma no duplica (la segunda vez se rechaza). Respeta el cupo del plan. | Leer y emitir |
Las herramientas llevan las anotaciones MCP: readOnlyHint en las de leer, y destructiveHint e idempotentHint en emitir_factura. Anular una factura no se expone: se hace en la pantalla. Cada factura emitida así queda en la bitácora de la empresa como «emitida por asistente: Claude» (el nombre del asistente conectado). Los pedidos cuentan en el mismo límite por llave que la API (100 por minuto), y la empresa sale siempre del token.
Responsabilidad fiscal: la factura que su asistente emita es una factura de su empresa y usted responde por ella, igual que por las de la pantalla. Por eso el asistente tiene que mostrarle la vista previa y pedirle permiso antes de llamar a emitir_factura.
Webhooks
En Administración › Conexiones quien administra registra la dirección de su sistema y elige los eventos. Bigger Control envía un POST con JSON a esa dirección cada vez que el evento ocurre.
- La dirección debe ser
https, de un sitio público de internet, sin usuario ni contraseña y en el puerto normal (443). Nunca se envía a direcciones locales o privadas (localhost, 10.x, 172.16 a 172.31, 192.168.x, 169.254.x, ::1…) ni a direcciones numéricas: se valida al registrarla y otra vez en cada envío. - No se siguen redirecciones. Su sistema debe contestar con un código
2xxen menos de 8 segundos; no hace falta que devuelva nada en el cuerpo (y bigger Control no lo guarda). - Reintentos: si no contesta 2xx, se repite a 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 24 horas. Tras 7 intentos la entrega queda «Fallida». El registro de entregas está en la misma pantalla.
- Un aviso puede llegar más de una vez (un reintento después de que su sistema ya lo procesó): use el
iddel aviso (ent_123) para ignorar los repetidos. Los avisos no garantizan el orden. - El secreto de la firma (
whsec_…) se muestra una sola vez al registrar el webhook.
Verificar la firma
Cada envío trae estas cabeceras: X-BiggerControl-Evento, X-BiggerControl-Entrega y X-BiggerControl-Firma, con la forma t=1760000000,v1=<hexadecimal>. La firma es el HMAC-SHA256, en hexadecimal, del texto «t.cuerpo» (el valor de t, un punto y el cuerpo EXACTO que recibió, sin volver a formatearlo) con su secreto como llave.
- Lea
tyv1de la cabecera. - Calcule el HMAC-SHA256 de
t + "." + cuerpocon su secretowhsec_…. - Compárelo con
v1en tiempo constante. - Rechace el aviso si
tes de hace más de 5 minutos (evita que repitan uno viejo). Cada reintento se firma de nuevo con la hora de su envío.
// Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
export function firmaValida(cuerpoCrudo, cabecera, secreto) {
const t = /t=(\d+)/.exec(cabecera)?.[1];
const v1 = /v1=([0-9a-f]{64})/.exec(cabecera)?.[1];
if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const esperada = createHmac("sha256", secreto).update(`${t}.${cuerpoCrudo}`).digest("hex");
return timingSafeEqual(Buffer.from(esperada), Buffer.from(v1));
}
# Python
import hmac, hashlib, re, time
def firma_valida(cuerpo_crudo: bytes, cabecera: str, secreto: str) -> bool:
t = re.search(r"t=(\d+)", cabecera); v1 = re.search(r"v1=([0-9a-f]{64})", cabecera)
if not t or not v1 or abs(time.time() - int(t.group(1))) > 300:
return False
esperada = hmac.new(secreto.encode(), t.group(1).encode() + b"." + cuerpo_crudo, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperada, v1.group(1))
Eventos y campos
Todos los avisos tienen la misma envoltura. Los datos del evento son lo mínimo (números, fechas, totales y estados), sin el nombre ni el RTN del cliente: pida el detalle con la API.
{
"id": "ent_41",
"evento": "factura.emitida",
"creado": "2026-10-16T16:42:05.000Z",
"entorno": "produccion",
"datos": { "correlativo": "000-001-01-00000012", "fecha": "2026-10-16", "total": "575.00", "estado": "emitida" }
}
| Evento | Cuándo | Campos de datos |
|---|---|---|
factura.emitida | Se emite una factura nueva (por pantalla, caja o API). Repetir una Idempotency-Key no avisa otra vez. | correlativo, fecha, total, estado |
factura.anulada | Se anula una factura. | correlativo, estado |
pago.registrado | Se registra un pago de un cliente. | id, factura, fecha, monto, saldo (lo que queda), saldada |
nota.emitida | Se emite una nota de crédito o de débito. | correlativo, tipo (credito o debito), factura, fecha, total |
inventario.bajo_minimo | Una factura deja un producto por debajo de su mínimo (una vez por cruce, no en cada venta). | producto (id), codigo, existencia, minimo, factura |
Dudas o algo que falta. Escríbanos a soporte@biggercontrol.com con el código y el mensaje del error. Nunca nos mande su llave ni su secreto.