BELIVE 360Connect

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 2xx en 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 sur id du corps).
  • L'ordre n'est pas garanti en cas de reprise : utilisez occurredAt et le status du corps, qui reflète l'état au moment de l'envoi.
  • Pour tester : POST /v1/webhooks/test envoie un webhook.test signé.