Docs / Reference / Webhooks

Webhooks

Subscribe to signed outbound webhooks for scan.completed and credits.low, and verify the signature before trusting a delivery.

Webhooks push events to your systems so you don’t have to poll. Create a subscription with a delivery URL, and AskThis sends a signed POST whenever a subscribed event fires.

You can manage subscriptions two ways: in the dashboard under Developers (create, pause, send a test, delete — the signing secret is shown once at creation), or over the API as shown below.

Events

Event Fires when
scan.completed An onboarding or re-scan finishes and prompts are ready
scan.failed A scan job errors out
credits.low Your credit balance drops below the low-balance threshold
credits.depleted Your credit balance hits zero — act now (top up or upgrade)
citation.detected A Citation Monitor run finds your site cited by an AI engine
subscription.updated Your plan changes — created, upgraded, downgraded or cancelled
invoice.paid A gateway invoice is paid
site.installed A site’s widget is verified live for the first time

Subscribe to any subset. Subscribing to an event name outside this list is rejected with a 422.

Example payloads

// credits.depleted
{ "event": "credits.depleted", "payload": { "orgId": "org_...", "balance": 0 },
  "timestamp": "2026-07-15T09:00:00.000Z" }

// citation.detected
{ "event": "citation.detected",
  "payload": { "siteId": "cq_8f2a", "cited": 3, "total": 6, "engines": ["chatgpt", "gemini"] },
  "timestamp": "2026-07-15T09:00:00.000Z" }

// subscription.updated
{ "event": "subscription.updated",
  "payload": { "orgId": "org_...", "plan": "business", "status": "active", "cancelled": false },
  "timestamp": "2026-07-15T09:00:00.000Z" }

Subscribing

# Create a subscription → the signing secret is returned ONCE
curl -X POST https://api.askthis.io/api/v1/webhook-subscriptions \
  -H "Authorization: Bearer <session>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.yoursite.com/askthis",
    "events": ["scan.completed", "credits.low"]
  }'

# List subscriptions (the secret is never shown again)
curl https://api.askthis.io/api/v1/webhook-subscriptions -H "Authorization: Bearer <session>"

# Delete a subscription
curl -X DELETE https://api.askthis.io/api/v1/webhook-subscriptions/<id> \
  -H "Authorization: Bearer <session>"

Verifying deliveries

Every delivery is signed with HMAC-SHA256 over the raw request body, using your subscription secret. The hex signature arrives in the X-AskThis-Signature header. Recompute it and compare before trusting the payload:

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody, header, secret) {
  const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(header ?? "");
  return a.length === b.length && timingSafeEqual(a, b);
}

// Express example (raw body required)
app.post("/askthis", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.get("X-AskThis-Signature"), process.env.ASKTHIS_WEBHOOK_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString());
  // handle event.type: "scan.completed" | "credits.low"
  res.status(200).end();
});

Test & debug

Verify a subscription without waiting for a real event — send a sample signed ping:

curl -X POST https://api.askthis.io/api/v1/webhook-subscriptions/<id>/test \
  -H "Authorization: Bearer <session>"
# → { "data": { "delivered": true, "status": "200" }, ... }

And inspect the recent delivery attempts (newest first) to debug failures:

curl https://api.askthis.io/api/v1/webhook-subscriptions/<id>/deliveries \
  -H "Authorization: Bearer <session>"
# → [{ "event": "scan.completed", "status": "200", "at": "..." }, ...]

Delivery guarantees

  • Signed — always verify the signature; reject on mismatch.
  • Time-boxed — each delivery attempt times out after 5 seconds, so respond fast (queue the work, return 200).
  • Retried — failed deliveries are retried, so make your handler idempotent (dedupe on the event ID).

See also: API keys for authenticating the management calls above.