API de FlavosoWebhooks y eventosWebhooks
Webhooks
Flavoso notifica cada cambio al instante a tu servidor con un POST firmado: configuración, recepción, confirmación, reintentos y registro.
Cómo funcionan los webhooks
Cuando algo cambia en un negocio que ha conectado tu app, se produce un evento, y Flavoso lo envía con un POST a la dirección del webhook de tu app. Tu app no tiene que consultar constantemente: un pedido nuevo suele llegar en pocos segundos.
Cambio en el negocio
Panel, cocina, app móvil, impresora de tickets o API
Evento
Sobre con id, type, data; congelado en el primer envío
Entrega
POST a tu dirección, firmado con Flavoso-Signature
Entregado
tu servidor responde con 2xx en 10 segundos
Si no: nuevo intento 8 intentos a lo largo de casi dos días
- 1de inmediato
- 21 min
- 35 min
- 430 min
- 52 h
- 66 h
- 712 h
- 824 h
Si fallan al menos 10 intentos seguidos durante 24 horas, la dirección se desactiva y recibes un e-mail.
Lo que se haya perdido lo recuperas con GET /events, hasta 30 días atrás.
Una app tiene exactamente un webhook. A él llegan los eventos de todos los negocios que han conectado tu app, siempre que la conexión tenga el permiso de lectura correspondiente (consulta Permisos y eventos). Además recibe los eventos sobre tus propias conexiones (app.*).
Configuración
- 1
Introducir la dirección
Una dirección
https, accesible desde internet, de 500 caracteres como máximo. Flavoso no acepta direcciones de redes privadas.Consola › App › Webhooks
- 2
Elegir los eventos
Solo lo que necesita tu app. Una caja necesita
order.*, a menudomenu.*ystore.*, y siempreapp.disconnected. - 3
Copiar el secreto de firma
Empieza por
whsec_. Con él tu servidor comprueba cada entrega. Puedes volver a mostrarlo o renovarlo en cualquier momento en la consola, en Webhooks. - 4
Enviar un evento de prueba
Comprueba todo el camino: dirección, firma, respuesta. El resultado aparece debajo, en las entregas.
«Crear un nuevo secreto de firma» sustituye el secreto de firma; durante 24 horas, Flavoso firma con los dos. Puedes desactivar un webhook y volver a activarlo, o eliminarlo: en ese caso desaparecen la dirección, el secreto y todas las entregas con su registro. Los eventos en sí siguen disponibles 30 días a través de GET /events.
El sobre
Cada entrega es un sobre JSON. Es el mismo para el webhook y para GET /events:
| Campo | Significado |
|---|---|
id | Identificador del evento (evt_…). Con él reconoces los duplicados. |
object | Siempre event. |
type | El tipo, por ejemplo order.created. Consulta Tipos de eventos. |
created_at | Cuándo se produjo el cambio. |
api_version | La fecha de versión, como en Flavoso-Api-Version. |
livemode | false en el negocio de prueba. |
tenant_id | El negocio. |
connection_id | La conexión de tu app con este negocio. Con ella encuentras los tokens correspondientes. |
data | El contenido: data.object es el objeto en el mismo formato que devuelven las rutas y, según el tipo, además previous_status, changed o previous. |
{
"id": "evt_2bf5a9b06b75442a9244e884a1acfc57",
"object": "event",
"type": "order.created",
"created_at": "2026-10-04T17:45:10.410Z",
"api_version": "2026-10-08",
"livemode": true,
"tenant_id": "7d0e5b8a-3c41-4f2e-9a6b-1e2d3c4b5a69",
"connection_id": "4b3f7c1e-9a2d-4e8b-b5f6-0c1d2e3f4a5b",
"data": {
"object": {
"object": "order",
"id": "cce01f52-5188-410f-9659-6ce26ba9b784",
"code": "3271CD",
"type": "delivery",
"status": "pending",
"source": "storefront",
"created_at": "2026-10-04T17:44:59.942Z",
"updated_at": "2026-10-04T17:45:10.118Z",
"requested_for": null,
"currency": "EUR",
"amounts": {
"subtotal": 2490,
"delivery_fee": 250,
"service_fee": 0,
"discount": 0,
"tip": 200,
"total": 2940
},
"tax": {
"included": true,
"lines": [
{
"rate_bps": 700,
"gross": 2421,
"net": 2263,
"tax": 158
},
{
"rate_bps": 1900,
"gross": 319,
"net": 268,
"tax": 51
}
]
},
"payment": {
"method": "online",
"status": "paid",
"paid_at": "2026-10-04T17:45:10.000Z"
},
"customer": {
"name": "Alex",
"phone": "+49 151 00000000"
},
"delivery": {
"address": "Musterstraße 1",
"postal_code": "10115",
"city": "Berlin"
},
"table": null,
"note": "Please ring twice",
"external_id": null,
"acknowledged_at": null,
"items": [
{
"id": "e9c0ce66-7f7f-42cf-a4bf-7d66f87768c1",
"product_id": "303b87c9-58a3-4644-9a8b-2841cc1f89d6",
"name": "Pizza Margherita",
"quantity": 2,
"unit_price": 1100,
"line_total": 2200,
"tax_rate_bps": 700,
"options": [
{
"id": "1f2e3d4c-5b6a-4798-8a7b-6c5d4e3f2a1b",
"name": "Extra cheese",
"price_delta": 150,
"group_id": "8e7d6c5b-4a39-4281-b0f9-e8d7c6b5a4f3",
"group_name": "Extras"
}
]
},
{
"id": "0d1e2f3a-4b5c-4d6e-8f70-81a2b3c4d5e6",
"product_id": "6a7b8c9d-0e1f-4a2b-9c3d-4e5f6a7b8c9d",
"name": "Cola 0,33 l",
"quantity": 1,
"unit_price": 290,
"line_total": 290,
"tax_rate_bps": 1900,
"options": []
}
]
}
}
}Además llegan estas cabeceras:
| Cabecera | Significado |
|---|---|
Flavoso-Signature | La firma; consulta Comprobar las firmas. |
Flavoso-Event-Id | Lo mismo que id en el sobre. |
Flavoso-Event-Type | Lo mismo que type. |
Flavoso-Delivery-Attempt | El número de envío de esta entrega, empezando por 1. Cuenta cada envío, automático o reenviado a mano. |
Content-Type | application/json |
User-Agent | Flavoso-Webhooks/1.0 (+https://docs.flavoso.com/en/api), para que reconozcas las entregas en tus registros. |
POST /flavoso/webhook HTTP/1.1
Host: pos.example.com
Content-Type: application/json
User-Agent: Flavoso-Webhooks/1.0 (+https://docs.flavoso.com/en/api)
Flavoso-Signature: t=1759598710,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
Flavoso-Event-Id: evt_2bf5a9b06b75442a9244e884a1acfc57
Flavoso-Event-Type: order.created
Flavoso-Delivery-Attempt: 1
{"id":"evt_2bf5a9b06b75442a9244e884a1acfc57","object":"event","type":"order.created",...}Recibir y confirmar
Una entrega se considera recibida si tu servidor responde con cualquier 2xx en un plazo de 10 segundos. El contenido del cuerpo de tu respuesta no importa.
- Primero comprueba la firma, después responde y luego procesa: un procesamiento largo antes de la respuesta agota el plazo, y Flavoso vuelve a enviar la misma entrega.
- Una redirección (3xx) cuenta como fallo. Registra la dirección definitiva.
- Guarda el
idde cada evento procesado y omite los duplicados.
import express from "express";
import { verifyFlavosoSignature } from "./flavoso-signature.js"; // see "Verifying signatures"
const app = express();
const SECRET = process.env.FLAVOSO_WEBHOOK_SECRET; // whsec_...
// express.raw: the body stays exactly as it arrived, which the signature needs
app.post("/flavoso/webhook", express.raw({ type: "application/json" }), async (req, res) => {
const raw = req.body.toString("utf8");
if (!verifyFlavosoSignature(raw, req.get("Flavoso-Signature") ?? "", SECRET)) {
return res.status(400).send("invalid signature");
}
const event = JSON.parse(raw);
// Answer within 10 seconds, then work. Duplicates are possible: the event id decides.
res.sendStatus(200);
if (await alreadyHandled(event.id)) return;
await queue.add(event); // your job queue: book the order, update the menu, ...
});
app.listen(3000);Reintentos
Todo lo que no sea 2xx cuenta como fallo: un código de error, un tiempo de espera agotado, una redirección, un servidor inaccesible. Entonces Flavoso vuelve a intentarlo, 8 intentos en total a lo largo de casi dos días. Una caja apagada durante la noche lo recibe todo igualmente por la mañana.
| Intento | Espera desde el anterior |
|---|---|
| 1 | de inmediato |
| 2 | 1 min |
| 3 | 5 min |
| 4 | 30 min |
| 5 | 2 h |
| 6 | 6 h |
| 7 | 12 h |
| 8 | 24 h |
Las esperas cuentan siempre desde el intento anterior. Flavoso-Delivery-Attempt cuenta cada envío, también los manuales. Cada intento envía el mismo sobre, byte a byte, con una firma nueva.
Desactivación por errores persistentes
Si fallan al menos 10 intentos seguidos y durante 24 horas no llega nada, Flavoso desactiva el webhook y te envía un e-mail. En ese caso, las entregas pendientes se omiten.
Una vez solucionado el problema, vuelve a activarlo en la consola y reenvía las entregas perdidas con «Reenviar», o recupéralas con GET /events, hasta 30 días atrás.
Orden y duplicados
- Orden: no está garantizado. Los eventos de un mismo objeto pueden llegar desordenados, por ejemplo un reintento después de un evento posterior. Gana el estado más reciente según el
updated_atdel objeto:data.object.updated_ates el momento del cambio que comunica el evento. Quédate con el estado deupdated_atmás reciente e ignora los más antiguos. - Duplicados: son posibles, por ejemplo si tu respuesta se pierde por el camino. Lo que cuenta es el
iddel evento. - Estado en el sobre:
data.objectse construye en el primer envío y después queda congelado; en pedidos y reservas,statusyupdated_atson los del cambio que comunica el evento. Si necesitas tener la certeza del estado actual, vuelve a leer el objeto conGET. - Campos de tu app:
external_idyacknowledged_ataparecen siempre comonullen los eventos, porque el mismo sobre va a todos los destinatarios. Las respuestas de las rutas sí los llevan.
Entregas y reenvío
En Webhooks están todas las entregas a tu dirección, de todos los negocios conectados, filtrables por estado, tipo, negocio y periodo. La vista de detalle de una entrega muestra el sobre, las cabeceras enviadas con la firma y cada intento: momento, automático o manual, estado HTTP, duración, error y el principio de la respuesta de tu servidor (hasta 2.000 caracteres).
«Reenviar» vuelve a enviar una entrega, con el mismo sobre y el mismo id. No cuenta para los 8 intentos automáticos. «Reenviar» y «Enviar un evento de prueba» se pueden usar en total como máximo 10 veces por minuto y app, incluidos los botones de la pestaña Eventos del negocio de prueba.
Eventos de prueba
«Enviar un evento de prueba» envía a tu dirección el tipo que elijas, incluso uno al que no estés suscrito, o bien ping. Los datos vienen de tu negocio de prueba cuando los hay y, si no, son datos de ejemplo; livemode es siempre false. A partir de ahí, es una entrega como cualquier otra: firmada, con reintentos y en el registro. Para ello, la conexión con el negocio de prueba necesita el permiso de lectura correspondiente.
Lo que haces de verdad en el negocio de prueba desencadena eventos reales: un pedido de prueba, order.created; un cambio de estado en la consola, order.status_changed; una pausa, store.paused.
Para desarrollar en tu propio ordenador necesitas una dirección pública, por ejemplo mediante un túnel. O usas GET /events, que no la necesita.
Webhooks propios de un negocio
Un propietario también puede configurar él mismo webhooks para su negocio, sin app, por ejemplo para un sistema propio o un servicio de automatización: en el panel, en Ajustes, Integraciones, hasta cinco direcciones. Tienen el mismo formato; allí connection_id es null y no hay eventos app.*. Más información en el manual de Flavoso.
¿Algo no ha quedado claro? Escribe a support@flavoso.com e indica el identificador de X-Request-Id. Así podremos encontrar tu solicitud en el registro.