API FlavosoWebhooks et événementsWebhooks

Webhooks

Flavoso signale chaque changement immédiatement par un POST signé vers votre serveur : configuration, réception, accusé de réception, nouvelles tentatives et journal.

Fonctionnement des webhooks

Quand quelque chose change dans un établissement qui a connecté votre application, un événement est créé, et Flavoso l'envoie par POST à l'adresse du webhook de votre application. Votre application n'a pas besoin d'interroger Flavoso sans cesse : une nouvelle commande arrive en général en quelques secondes.

Changement dans l'établissement

Tableau de bord, cuisine, application mobile, imprimante de tickets ou API

Événement

Enveloppe avec id, type, data ; figée au premier envoi

Livraison

POST vers votre adresse, signé avec Flavoso-Signature

Livré

votre serveur répond par un 2xx en moins de 10 secondes

Sinon : nouvelle tentative 8 tentatives sur près de deux jours

  1. 1immédiatement
  2. 21 min
  3. 35 min
  4. 430 min
  5. 52 h
  6. 66 h
  7. 712 h
  8. 824 h

Si au moins 10 tentatives d'affilée échouent sur 24 heures, l'adresse est désactivée et vous recevez un e-mail.

Rattrapez ce qui s'est perdu avec GET /events, jusqu'à 30 jours en arrière.

Une application a un seul webhook. Il reçoit les événements de tous les établissements qui ont connecté votre application, pour autant que la connexion dispose du droit de lecture correspondant (voir Droits et événements). S'y ajoutent les événements de vos connexions elles-mêmes (app.*).

Configuration

  1. 1

    Saisir l'adresse

    Une adresse https, accessible depuis Internet, 500 caractères au maximum. Flavoso n'accepte pas les adresses de réseaux privés.

    Console › Application › Webhooks

  2. 2

    Choisir les événements

    Seulement ce dont votre application a besoin. Une caisse a besoin de order.*, souvent de menu.* et store.*, toujours de app.disconnected.

  3. 3

    Copier le secret de signature

    Il commence par whsec_. Votre serveur s'en sert pour vérifier chaque livraison. Vous pouvez l'afficher à nouveau ou le renouveler à tout moment dans la console, sous Webhooks.

  4. 4

    Envoyer un événement de test

    Vérifie tout le parcours : adresse, signature, réponse. Le résultat apparaît juste en dessous, dans les livraisons.

« Créer un nouveau secret de signature » remplace le secret de signature ; pendant 24 heures, Flavoso signe alors avec les deux. Vous pouvez désactiver puis réactiver un webhook, ou le supprimer : l'adresse, le secret et toutes les livraisons, journal compris, disparaissent alors. Les événements eux-mêmes restent disponibles 30 jours via GET /events.

L'enveloppe

Chaque livraison est une enveloppe JSON. Elle est la même pour les webhooks et pour GET /events :

ChampSignification
idL'identifiant de l'événement (evt_…). C'est grâce à lui que vous repérez les doublons.
objectToujours event.
typeLe type, par exemple order.created. Voir Types d'événements.
created_atLe moment du changement.
api_versionLa date de version, comme dans Flavoso-Api-Version.
livemodefalse pour l'établissement de test.
tenant_idL'établissement.
connection_idLa connexion de votre application à cet établissement. Elle vous permet de trouver les jetons correspondants.
dataLe contenu : data.object est l'objet sous la même forme que celle renvoyée par les routes, ainsi que previous_status, changed ou previous selon le type.
{
  "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": []
        }
      ]
    }
  }
}

L'enveloppe est accompagnée des en-têtes suivants :

En-têteSignification
Flavoso-SignatureLa signature, voir Vérifier les signatures.
Flavoso-Event-IdIdentique à id dans l'enveloppe.
Flavoso-Event-TypeIdentique à type.
Flavoso-Delivery-AttemptLe numéro d'envoi de cette livraison, à partir de 1. Chaque envoi compte, qu'il soit automatique ou manuel.
Content-Typeapplication/json
User-AgentFlavoso-Webhooks/1.0 (+https://docs.flavoso.com/en/api), pour reconnaître les livraisons dans vos journaux.
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",...}

Recevoir et accuser réception

Une livraison est réussie quand votre serveur répond par un code 2xx quelconque en moins de 10 secondes. Le contenu du corps de votre réponse n'a pas d'importance.

  • Vérifiez d'abord la signature, puis répondez, puis traitez : un long traitement avant la réponse dépasse le délai, et Flavoso renvoie la même livraison.
  • Une redirection (3xx) compte comme un échec. Enregistrez l'adresse finale.
  • Retenez l'id de chaque événement traité et ignorez les doublons.
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);

Nouvelles tentatives

Tout ce qui n'est pas un 2xx compte comme un échec : un code d'erreur, un délai dépassé, une redirection, un serveur injoignable. Flavoso réessaie alors, 8 tentatives au total sur près de deux jours. Une caisse éteinte pendant la nuit reçoit encore tout le lendemain matin.

TentativeDélai depuis la précédente
1immédiatement
21 min
35 min
430 min
52 h
66 h
712 h
824 h

Chaque délai se compte à partir de la tentative précédente. Flavoso-Delivery-Attempt compte chaque envoi, y compris les envois manuels. Chaque tentative envoie la même enveloppe, octet pour octet, avec une nouvelle signature.

Désactivation après des échecs persistants

Si au moins 10 tentatives d'affilée échouent et que rien ne passe pendant 24 heures, Flavoso désactive le webhook et vous envoie un e-mail. Les livraisons en attente sont alors ignorées.

Une fois le problème corrigé, réactivez-le dans la console et renvoyez les livraisons manquées avec « Renvoyer », ou rattrapez-les avec GET /events, jusqu'à 30 jours en arrière.

Ordre et doublons

  • Ordre : non garanti. Les événements d'un même objet peuvent arriver dans le désordre, par exemple une nouvelle tentative après un événement plus récent. L'état le plus récent l'emporte, d'après le updated_at de l'objet : data.object.updated_at est l'heure du changement que signale l'événement. Conservez l'état dont le updated_at est le plus récent et ignorez les états plus anciens.
  • Doublons : possibles, par exemple quand votre réponse se perd en route. L'id de l'événement fait foi.
  • État dans l'enveloppe : data.object est construit au premier envoi puis figé ; pour les commandes et les réservations, status et updated_at sont ceux du changement que signale l'événement. Pour connaître à coup sûr l'état actuel, relisez l'objet avec GET.
  • Champs de votre application : external_id et acknowledged_at valent toujours null dans les événements, car la même enveloppe est envoyée à tous les destinataires. Vous les trouvez dans les réponses des routes.

Livraisons et renvoi

Sous Webhooks, vous trouvez toutes les livraisons vers votre adresse, pour tous les établissements connectés, filtrables par statut, type, établissement et période. La vue détaillée d'une livraison montre l'enveloppe, les en-têtes envoyés, signature comprise, et chaque tentative : heure, automatique ou manuelle, statut HTTP, durée, erreur et le début de la réponse de votre serveur (jusqu'à 2 000 caractères).

« Renvoyer » relance une livraison, avec la même enveloppe et le même id, sans entamer les 8 tentatives automatiques. « Renvoyer » et « Envoyer un événement de test » sont limités ensemble à 10 envois par minute et par application, boutons de l'onglet Événements de l'établissement de test compris.

Événements de test

« Envoyer un événement de test » envoie à votre adresse un événement du type de votre choix, même d'un type auquel vous n'êtes pas abonné, y compris ping. Les données proviennent de votre établissement de test quand il en contient, sinon ce sont des données d'exemple ; livemode vaut toujours false. C'est ensuite une livraison comme les autres : signée, avec nouvelles tentatives, et visible dans le journal. Pour cela, la connexion à l'établissement de test doit disposer du droit de lecture correspondant.

Les actions réelles dans l'établissement de test déclenchent de vrais événements : une commande test déclenche order.created, un changement de statut dans la console order.status_changed, une pause store.paused.

Pour développer sur votre propre ordinateur, il vous faut une adresse publique, par exemple via un tunnel. Sinon, utilisez GET /events, qui n'en a pas besoin.

Les webhooks propres à un établissement

Un propriétaire peut aussi configurer lui-même des webhooks pour son établissement, sans application, par exemple pour son propre système ou un service d'automatisation : jusqu'à cinq adresses, dans le tableau de bord sous Réglages, Intégrations. Ils ont le même format ; connection_id y vaut null, et il n'y a pas d'événements app.*. Plus de détails dans le manuel.

Une question reste ouverte ? Écrivez à support@flavoso.com en indiquant l'identifiant de X-Request-Id. Il nous permet de retrouver votre requête dans le journal.