The developer docs are in English only for now. Everything else on the site is in British English.
Webhooks
Add an endpoint under Developers in the portal, pick the events you want, and we POST JSON to it when they happen.
Events
question.reservedquestion.completedquestion.underfilledquestion.cancelledquestion.refusedcredits.purchasedcredits.lowcredits.expiringdispute.resolvedconnection.cap_warning
Payload
{
"id": "evt_3kd9",
"type": "question.completed",
"createdAt": "2026-09-24T10:12:00Z",
"data": {
"questionId": "q_8k2f",
"status": "complete",
"summary": {
"winner": "Menu B",
"margin": 36,
"confidence": "high"
},
"creditsSpent": 600
}
}Verifying signatures
Every delivery carries x-50heads-signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> with your endpoint's secret. Check it against the raw bytes before parsing, and reject timestamps more than five minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";
// Header: x-50heads-signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>">
export function verify(rawBody: string, header: string, secret: string, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=") as [string, string]));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const given = Buffer.from(parts.v1 ?? "", "hex");
const wanted = Buffer.from(expected, "hex");
return given.length === wanted.length && timingSafeEqual(given, wanted);
}Retries and replay
Answer 2xx within ten seconds. Anything else is retried with backoff, eight times over 24 hours. Deliveries can arrive more than once or out of order: use the event id to ignore repeats. The delivery log and a replay button are under Developers in the portal.