Aillo webhooks
Aillo sends your branches’ pickup requests to your system as signed JSON. Configure the address in API & webhooks.
Events
message.created— A caller left a message (for example: name, order number, parking spot), or every line was busy.message.claimed— Someone on your team pressed “I’ll take it”; claimed_by says who.message.handled— Your team closed the request: outcome is « served » (« Servi ✓ ») or « left » (« Client parti »).ping— A test event sent from your workspace.
Curbside pickup fields
On a curbside pickup line (« appel-cueillette »), data.details holds the separate fields the assistant collected, so your system never has to read the text:
type— always « pickup »order_number— the order number, as confirmed by the callerparking_spot— the parking spot number, or where the car iscustomer_name— the name the caller gavevehicle— no longer asked; always empty (kept for compatibility)phone— the caller’s number when it was shown, else empty
For other messages, details is null. claimed_by, claimed_at and outcome are null until someone takes or closes the request.
The request
Aillo sends an HTTPS POST with a JSON body to your address. Reply with any 2xx status within 10 seconds; the body of your answer is ignored. Headers:
Aillo-Event— the event typeAillo-Delivery— a unique id for this delivery; use it to ignore duplicatesAillo-Signature— t=<unix seconds>,v1=<signature>
Example body
{
"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
}
}Verify the signature
Compute an HMAC-SHA256 of the timestamp, a dot and the raw body, with your signing secret (whsec_…), and compare it with v1. Refuse requests older than 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"])Retries
If your system does not answer with 2xx, Aillo retries after 30 seconds, 2 minutes, 10 minutes, 30 minutes, 1, 3, 6 and 12 hours, then stops. Events can arrive more than once or out of order: use Aillo-Delivery and created_at.
Security and privacy
- Only public https addresses are accepted.
- Events contain your callers’ personal information: store them as your privacy policy requires. Aillo keeps the delivery history 30 days.
- You can pause sending or create a new secret at any time.
Questions: [email protected]