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

FieldTypeDescription
ask.completedoptional
eventFires 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

json
{
  "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.

http
MentionsAPI-Signature: t=1714117284,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd
MentionsAPI-Event-Id: evt_01H8X2Y3Z4PQR5STU6VW7XYZ8
Content-Type: application/json
verify.mjs
import 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.