English

Webhooks Aillo

Aillo transmet les demandes de cueillette de vos succursales à votre système en JSON signé. Configurez l’adresse dans API et webhooks.

Événements

  • message.created — Un appelant a laissé un message (par exemple : nom, numéro de commande, place de stationnement), ou toutes les lignes étaient occupées.
  • message.claimed — Une personne de votre équipe a appuyé sur « Je m’en occupe »; claimed_by indique qui.
  • message.handled — Votre équipe a fermé la demande : outcome vaut « served » (« Servi ✓ ») ou « left » (« Client parti »).
  • ping — Un événement d’essai envoyé depuis votre espace.

Champs de l’appel-cueillette

Sur une ligne d’appel-cueillette, data.details contient les champs séparés recueillis par l’assistante; votre système n’a jamais à lire le texte :

  • type — toujours « pickup »
  • order_number — le numéro de commande, tel que confirmé par l’appelant
  • parking_spot — le numéro de la place, ou l’endroit où est la voiture
  • customer_name — le nom donné par l’appelant
  • vehicle — n’est plus demandé ; toujours vide (gardé par compatibilité)
  • phone — le numéro de l’appelant s’il était affiché, sinon vide

Pour les autres messages, details vaut null. claimed_by, claimed_at et outcome sont null tant que personne n’a pris ou fermé la demande.

La requête

Aillo envoie un POST HTTPS avec un corps JSON à votre adresse. Répondez avec un statut 2xx en moins de 10 secondes; le contenu de votre réponse est ignoré. En-têtes :

  • Aillo-Event — le type d’événement
  • Aillo-Delivery — un identifiant unique de cet envoi, pour ignorer les doublons
  • Aillo-Signature — t=<secondes unix>,v1=<signature>

Exemple de corps

{
  "id": "5b0d7c1e-9f4a-4a53-9a7e-2f1c8f0e6b11",
  "type": "message.created",
  "created_at": "2026-10-09T14:05:12.481Z",
  "business": { "id": "8f3e…", "name": "Magasin 214 – Laval" },
  "data": {
    "message_id": "c41a…",
    "body": "Commande 4821 · Place 6 · Julie Tremblay · Honda CR-V grise · +15145550123",
    "urgent": true,
    "call_id": null,
    "created_at": "2026-10-09T14:05:12.402Z",
    "handled_at": null,
    "details": {
      "type": "pickup",
      "order_number": "4821",
      "parking_spot": "6",
      "customer_name": "Julie Tremblay",
      "vehicle": "",
      "phone": "+15145550123"
    },
    "claimed_by": null,
    "claimed_at": null,
    "outcome": null
  }
}

Vérifier la signature

Calculez un HMAC-SHA256 de l’horodatage, d’un point et du corps brut, avec votre secret de signature (whsec_…), et comparez-le à v1. Refusez les requêtes de plus de 5 minutes.

Node.js

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const { t, v1 } = Object.fromEntries(header.split(",").map(p => p.split("=")));
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}

Python

import hashlib, hmac, time

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    if abs(time.time() - int(parts["t"])) > 300:
        return False
    expected = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Nouvelles tentatives

Si votre système ne répond pas 2xx, Aillo réessaie après 30 secondes, 2 minutes, 10 minutes, 30 minutes, 1, 3, 6 et 12 heures, puis s’arrête. Un événement peut arriver plus d’une fois ou dans le désordre : utilisez Aillo-Delivery et created_at.

Sécurité et confidentialité

  • Seules les adresses https publiques sont acceptées.
  • Les événements contiennent des renseignements personnels de vos appelants : conservez-les selon votre politique de confidentialité. Aillo garde l’historique des envois 30 jours.
  • Vous pouvez suspendre les envois ou créer un nouveau secret en tout temps.

Questions : [email protected]