Webhooks
When you ask with async: true, we POST the result to your registered endpoint once the fan-out completes. Deliveries are signed with HMAC-SHA256 and retried with exponential backoff.
Supported events
| Field | Type | Description |
|---|---|---|
ask.completedoptional | event | Fires when an async or standard-mode /v1/ask finishes. The data payload contains the same NormalizedResponse you would have received from a live /v1/ask call. |
Additional events (monitor runs, usage thresholds) are on the roadmap. Only ask.completed is emitted today.
Registering an endpoint
Create and manage webhook endpoints via the /v1/webhooks CRUD API or the dashboard. The signing secret is returned once at creation — store it immediately.
Payload shape
{
"id": "evt_01H8X2Y3Z4PQR5STU6VW7XYZ8",
"type": "ask.completed",
"created_at": "2026-04-24T09:01:24.102Z",
"account_id": "acct_…",
"api_version": "v1",
"data": {
/* Full /v1/ask NormalizedResponse:
request_id, id, cached, cache_tier, providers[],
brand_mentions[], citations[], usage { ... } */
}
}Signing
Each delivery carries an HMAC-SHA256 of the raw body keyed with your webhook secret. Verify before trusting the payload.
MentionsAPI-Signature: t=1714117284,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
MentionsAPI-Event-Id: evt_01H8X2Y3Z4PQR5STU6VW7XYZ8
Content-Type: application/jsonimport crypto from "node:crypto";
export function verifyWebhook(rawBody, signatureHeader, secret) {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=")),
);
const signedPayload = `${parts.t}.${rawBody}`;
const expected = crypto.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
const ok = crypto.timingSafeEqual(
Buffer.from(parts.v1, "hex"),
Buffer.from(expected, "hex"),
);
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
return ok && fresh;
}Retries
Any non-2xx response is retried with exponential backoff. After the final attempt, the delivery is moved to a dead-letter queue visible in the dashboard. You can replay any delivery from the dashboard within 30 days.
Idempotency
The MentionsAPI-Event-Id header is stable across retries. Store the id on your side and early-return if you've already processed it — that is enough to dedupe cleanly.
Rotating the secret
Rotate via PATCH /v1/webhooks/:id with { "rotate_secret": true }. The response includes the new plaintext secret ONCE; swap it in your verifier, then you're done.