Webhooks
7.1 Déclarer votre endpoint
POST /v1/webhooks
{ "url": "https://votre-systeme.ma/belive/webhook", "events": ["reservation.created", "reservation.confirmed", "reservation.cancelled", "reservation.declined", "reservation.checked_in"] }
Réponse 201 avec un secret whsec_… affiché une seule fois : stockez-le comme la clé API. Cinq endpoints actifs au maximum. L'URL doit être en HTTPS en production.
7.2 Ce que vous recevez
Un POST JSON par événement :
{
"id": "6e1a…",
"type": "reservation.checked_in",
"occurredAt": "2026-10-16T21:45:12.000Z",
"data": {
"reservation": {
"beliveReservationId": "e753ec50-…", "accessCode": "BLV-S8Z9", "status": "confirmed",
"date": "2026-10-16", "slot": "21:30", "table": { "id": "27ced4e4-…", "name": "Table 9" },
"guests": 3, "customerFirstName": "Othmane", "source": "partner", "externalRef": "LACALE-5001",
"checkedInAt": "2026-10-16T21:45:11.000Z", "venue": { "id": "31f705c0-…", "name": "La Cale Rabat" }
}
}
}
En-têtes : X-Belive-Event (type), X-Belive-Delivery (identifiant unique de livraison), X-Belive-Signature (voir 7.3).
| Événement | Quand | Ce que vous faites en général |
|---|---|---|
reservation.created |
Une réservation apparaît (Belive, personnel ou vous) | Bloquer la table dans votre planning si source n'est pas partner |
reservation.confirmed |
Paiement reçu ou demande acceptée | Marquer confirmée |
reservation.cancelled |
Annulation (client, personnel, ou vous) | Libérer la table |
reservation.declined |
Demande refusée par l'établissement | Libérer la table |
reservation.checked_in |
Le client a passé la porte avec Belive Terminal | Marquer « arrivé » |
webhook.test |
Vous avez appelé POST /v1/webhooks/test |
Vérifier votre réception et la signature |
Vos propres réservations déclenchent aussi des événements (source: "partner", avec votre externalRef) : utilisez-les pour confirmer que tout est synchronisé, et ignorez-les sinon.
7.3 Vérifier la signature (obligatoire)
Chaque livraison est signée : X-Belive-Signature: t=<timestamp>,v1=<hex> où v1 = HMAC-SHA256(secret, "<timestamp>.<corps brut>"). Vérifiez sur le corps brut, avant tout parsing JSON, et rejetez les messages de plus de 5 minutes.
Node.js :
import crypto from "node:crypto";
export function verifyBeliveSignature(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 || "", "hex");
const want = Buffer.from(expected, "hex");
return given.length === want.length && crypto.timingSafeEqual(given, want);
}
PHP :
function verifyBeliveSignature(string $rawBody, string $header, string $secret): bool {
parse_str(str_replace(',', '&', $header), $p);
$t = (int)($p['t'] ?? 0);
if (!$t || abs(time() - $t) > 300) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $p['v1'] ?? '');
}
Python :
import hmac, hashlib, time
def verify_belive_signature(raw_body: bytes, header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
t = int(parts.get("t", "0") or 0)
if not t or abs(time.time() - t) > 300:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))
7.4 Bonnes pratiques de réception
- Répondez
2xxen moins de 10 secondes. Traitez le message après avoir répondu si votre traitement est long. - En cas d'échec (pas de réponse ou code non
2xx), Belive réessaie 5 fois : après 1 min, 5 min, 30 min, 2 h, puis 12 h. Ensuite la livraison est marquée morte et peut être rejouée depuis le portail vendeur. - Un même événement peut donc arriver plusieurs fois : dédupliquez sur
X-Belive-Delivery(ou suriddu corps). - L'ordre n'est pas garanti en cas de reprise : utilisez
occurredAtet lestatusdu corps, qui reflète l'état au moment de l'envoi. - Pour tester :
POST /v1/webhooks/testenvoie unwebhook.testsigné.

