Integraciones
Conecta La Carta Viva con tu TPV
Cada pedido, cobro y reserva llega a tu TPV, a tu contabilidad o a tus automatizaciones en cuanto pasa, firmado para que nadie pueda colarse. Y si tu TPV prefiere preguntar, tiene una API para leer pedidos y marcar agotados. Abierto a cualquier proveedor: sin acuerdos y sin coste extra.
Para el restaurante: tres pasos
- En el panel, entra en Ajustes → Conexiones y pulsa «Nueva conexión».
- Pega la dirección que te dé tu proveedor (empieza por
https://) y elige qué quieres que reciba: pedidos, cobros, reservas. - Pásale el secreto que aparece y pulsa «Enviar prueba». Si contesta, ya está: desde ese momento le llega todo solo.
¿Usas Zapier, Make o n8n? Crea un disparador de tipo Webhook («Catch Hook» en Zapier), copia su dirección y pégala como conexión. Así puedes mandar cada reserva a una hoja de cálculo, cada pedido a tu contabilidad o un aviso a tu móvil, sin programar.
Eventos
pedido.creado: Entra un pedido: desde la mesa, del camarero, para llevar o a domicilio. Con todas sus líneas.pedido.actualizado: El pedido cambia de estado en cocina (aceptado, preparando, listo, servido) o se cancela.pago.recibido: Se cobra con tarjeta, Apple Pay o Google Pay desde el móvil: toda la cuenta, una parte o unos platos.reserva.creada: Llega una reserva: desde tu enlace de reservas (web, Instagram, Google Maps) o metida a mano por el equipo.reserva.actualizada: Una reserva cambia de hora, de personas o de estado (confirmada, sentada, cancelada, no vino).prueba: El botón «Enviar prueba» del panel. Para comprobar la firma sin esperar a un pedido.
Qué llega
Un POST con un JSON. Arriba, el sobre (igual en todos los eventos); dentro de datos, lo que ha pasado. Los importes van en céntimos y con el IVA incluido, como en la carta. Las fechas, en UTC (ISO 8601).
POST https://tu-tpv.com/webhooks/lacartaviva
Content-Type: application/json
X-LCV-Evento: pedido.creado
X-LCV-Envio: 0b7e5c1a-3f7d-4a52-9d1e-6c2f8a4b9e10
X-LCV-Firma: t=1791229330,v1=5f2c…e81a
{
"id": "0b7e5c1a-3f7d-4a52-9d1e-6c2f8a4b9e10",
"evento": "pedido.creado",
"version": "2026-10-05",
"creado": "2026-10-05T19:42:10.512Z",
"local": { "id": "c1d2…", "nombre": "Casa Lola" },
"datos": {
"pedido": {
"id": "9a8b…",
"numero": 42,
"estado": "placed",
"canal": "mesa",
"mesa": "7",
"dia": "2026-10-05",
"creado": "2026-10-05T19:42:10.498Z",
"subtotal_cents": 2350,
"impuestos_cents": 0,
"propina_cents": 0,
"total_cents": 2350,
"lineas": [
{
"producto_id": "5e4f…",
"nombre": "Croquetas de jamón",
"cantidad": 2,
"precio_unidad_cents": 850,
"total_cents": 1700,
"opciones": [],
"notas": null,
"es_envio": false
},
{
"producto_id": "7c6d…",
"nombre": "Caña",
"cantidad": 1,
"precio_unidad_cents": 650,
"total_cents": 650,
"opciones": [{ "nombre": "Grande", "precio_cents": 150 }],
"notas": "Muy fría",
"es_envio": false
}
],
"cliente": null
}
}
}Notas sobre los pedidos
canal:mesa,llevarodomicilio. En los dos últimos,clientetrae nombre, teléfono, hora pedida, dirección, código postal y si paga online o al recoger.precio_unidad_centsya incluye las opciones elegidas (el detalle va enopciones).- El envío a domicilio llega como una línea con
es_envio: trueyproducto_id: null. estado:placed(recibido),accepted,preparing,ready,servedocancelled.numeroes el número del pedido en el día y en el local (el que se ve en cocina).
Cobros y reservas
datos.pago:estado(cobrado,devuelto,devuelto_en_parte),importe_cents,propina_cents,total_cents,metodo,modo(fulltoda la cuenta,equala partes,by_itemspor platos),canal,mesay lospedidosde esa cuenta.datos.reserva:localizador,estado,empieza,acaba,personas,tronas,nombre,telefono,email,nota_cliente,canal,mesas,creadoyactualizado.
Comprobar la firma
La cabecera X-LCV-Firma tiene la forma t=<segundos>,v1=<firma>. La firma es el HMAC-SHA256, en hexadecimal, de t + "." + cuerpo con el secreto de la conexión. Compárala siempre sobre el cuerpo tal cual llega y descarta lo que tenga más de cinco minutos.
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto';
// cuerpo: el texto TAL CUAL llega (sin volver a convertirlo a JSON).
export function firmaValida(cuerpo, cabecera, secreto) {
const partes = Object.fromEntries(cabecera.split(',').map((p) => p.split('=')));
const t = Number(partes.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // más de 5 min: se descarta
const esperada = createHmac('sha256', secreto).update(`${t}.${cuerpo}`).digest('hex');
const recibida = Buffer.from(partes.v1 ?? '', 'hex');
return recibida.length === 32 && timingSafeEqual(recibida, Buffer.from(esperada, 'hex'));
}Python
import hashlib, hmac, time
def firma_valida(cuerpo: bytes, cabecera: str, secreto: str) -> bool:
partes = dict(p.split("=", 1) for p in cabecera.split(","))
t = int(partes.get("t", "0"))
if abs(time.time() - t) > 300: # más de 5 min: se descarta
return False
esperada = hmac.new(secreto.encode(), f"{t}.".encode() + cuerpo, hashlib.sha256).hexdigest()
return hmac.compare_digest(esperada, partes.get("v1", ""))PHP
function firma_valida(string $cuerpo, string $cabecera, string $secreto): bool {
parse_str(str_replace(',', '&', $cabecera), $p);
$t = (int) ($p['t'] ?? 0);
if (abs(time() - $t) > 300) return false; // más de 5 min: se descarta
$esperada = hash_hmac('sha256', $t . '.' . $cuerpo, $secreto);
return hash_equals($esperada, $p['v1'] ?? '');
}Si tu TPV pregunta: la API
Para los TPV que no pueden recibir avisos (un ordenador dentro del local, detrás del router). El restaurante crea una clave de API en Ajustes → Conexiones (se enseña una sola vez) y elige sus permisos: leer y agotados. Va en la cabecera Authorization: Bearer lcv_… de cada petición.
GET /api/v1/pedidos?desde=…: pedidos creados desde esa fecha, con su estado actual. Misma forma quedatos.pedido.GET /api/v1/pagos?desde=…: cobros desde esa fecha, conestadocobrado,devueltoodevuelto_en_parte.GET /api/v1/reservas?desde=…: reservas creadas o cambiadas desde esa fecha (poractualizado).GET /api/v1/carta: todos los platos con suid, para emparejarlos con los del TPV.PATCH /api/v1/platos/{id}con{"disponible": false}: lo agota en todas las cartas abiertas al momento (permisoagotados).
GET https://panel.lacartaviva.com/api/v1/pedidos?desde=2026-10-05T18:00:00Z
Authorization: Bearer lcv_…
{
"datos": [ { "id": "9a8b…", "numero": 42, "estado": "placed", "canal": "mesa", "lineas": [ … ] } ],
"siguiente": "2026-10-05T19:42:10.498Z",
"hay_mas": false
}PATCH https://panel.lacartaviva.com/api/v1/platos/5e4f…
Authorization: Bearer lcv_…
Content-Type: application/json
{ "disponible": false }- Las listas van en orden y de 100 en 100 (
limitede 1 a 100). Para la siguiente vuelta, usasiguientecomodesde: el último puede repetirse, descarta porid. - Sin
desde, las últimas 24 horas. Pregunta como mucho cada pocos segundos: el límite es de 120 peticiones por minuto por clave (después,429conRetry-After). - Errores:
401clave que falta, inventada o revocada;402el restaurante no tiene la suscripción activa;403la clave no tiene ese permiso;404ese plato no es de este restaurante. - Las claves se guardan solo como huella: si se pierde una, se revoca y se crea otra.
Entregas y reintentos
- Contesta con un 2xx en menos de 8 segundos. Si tienes que hacer algo largo, guárdalo y procésalo después.
- Si no hay 2xx, se reintenta al minuto, a los 5 minutos, a los 30, a las 2 horas y a las 12 horas. Después se da por fallido (y se ve así en el panel).
- Cada envío puede llegar más de una vez: usa
X-LCV-Envio(igual queid) para descartar repetidos. - El orden no está garantizado: guíate por
estadoy por las fechas. El contenido es el de ese momento, no el del instante del evento. - Solo direcciones
httpscon un dominio de verdad; no se siguen redirecciones. versioncambia solo si algo deja de ser compatible. Añadir campos nuevos no la cambia: ignora los que no conozcas.
¿Eres proveedor de TPV o de software para hostelería?
Escríbenos a hola@lacartaviva.com y te damos un local de pruebas para montar la integración con datos de verdad.
Preguntas frecuentes
¿Funciona con mi TPV?
Funciona con cualquier TPV, programa de contabilidad o automatización que pueda recibir avisos por internet (lo que los técnicos llaman «webhook»). Muchos ya lo hacen; si el tuyo no, Zapier, Make o n8n hacen de puente. Pásale esta página a tu proveedor: con ella tiene todo lo que necesita.
¿Cuesta algo conectarlo?
No. Las conexiones van incluidas en la cuota, sin acuerdos ni permisos: el restaurante las da de alta en Ajustes y su proveedor las recibe. Hasta 10 por local.
¿Y si mi TPV está apagado o no contesta?
Se reintenta solo: al minuto, a los 5 minutos, a la media hora, a las 2 horas y a las 12 horas. En el panel se ve cada envío, si llegó y qué contestó el otro lado.
¿Mi TPV es un ordenador del local, sin dirección en internet. ¿Sirve?
Sí. En vez de esperar avisos, el TPV pregunta él cada pocos segundos con una clave de API: pedidos, cobros y reservas nuevos. Y con la misma clave puede leer la carta y marcar un plato agotado, que desaparece de todas las mesas al momento.