Webhooks

Receive real-time HTTP callbacks when events happen in your account.

Setup

Configure your webhook endpoint in Settings → Webhooks within the app. You provide a URL and receive a signing secret. Webhooks are available on Growth and Pro plans.

All payloads are sent as POST requests with Content-Type: application/json.

Headers

X-Webhook-SignatureHMAC-SHA256 signature of the raw request body
X-Webhook-EventThe event type (e.g. prompt.run.completed)

Retries

Failed deliveries are retried up to 3 times with increasing backoff: 10 seconds, 60 seconds, 5 minutes. A delivery is considered failed if your endpoint returns a non-2xx status or times out after 10 seconds.

Signature verification

Verify the X-Webhook-Signature header to confirm the payload came from Cited Monitor. Compute an HMAC-SHA256 of the raw request body using your signing secret and compare it to the header value.

Node.js
const crypto = require('crypto');

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// In your Express handler:
app.post('/webhooks/citedmonitor', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const isValid = verifyWebhook(req.rawBody, signature, process.env.WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = req.body;
  // Handle event...
  res.status(200).send('OK');
});
Python
import hmac
import hashlib

def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)
PHP
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $payload, $webhookSecret);

if (!hash_equals($expected, $signature)) {
    http_response_code(401);
    exit('Invalid signature');
}

Events

All payloads share the same envelope structure.

Envelope
{
  "event": "event.type",
  "timestamp": "2026-06-13T14:22:03.000000Z",
  "data": { ... }
}
EVENT prompt.run.completed

Fired when a prompt run finishes successfully and results have been processed.

Data fields

prompt_run_idintegerThe completed run ID
prompt_idintegerThe prompt that was run
entity_idintegerThe brand being monitored
providerstringProvider slug (e.g. openai)
modelstringModel used (e.g. gpt-4o)
entity_countintegerNumber of entities extracted
citation_countintegerNumber of citations found
Example payload
{
  "event": "prompt.run.completed",
  "timestamp": "2026-06-13T14:22:03.000000Z",
  "data": {
    "prompt_run_id": 91,
    "prompt_id": 12,
    "entity_id": 1,
    "provider": "openai",
    "model": "gpt-4o",
    "entity_count": 7,
    "citation_count": 2
  }
}
EVENT prompt.run.failed

Fired when a prompt run fails due to an API error, invalid key, rate limit, etc.

Data fields

prompt_run_idintegerThe failed run ID
prompt_idintegerThe prompt that was run
providerstringProvider slug
modelstringModel used
error_typestringauth_failure rate_limited provider_error unknown
error_messagestringHuman-readable error description
Example payload
{
  "event": "prompt.run.failed",
  "timestamp": "2026-06-13T14:22:05.000000Z",
  "data": {
    "prompt_run_id": 92,
    "prompt_id": 12,
    "provider": "anthropic",
    "model": "claude-sonnet-4-20250514",
    "error_type": "rate_limited",
    "error_message": "Rate limit exceeded. Please try again in 30 seconds."
  }
}
EVENT entity.new

Fired when a previously unseen brand or entity is discovered in an AI response.

Data fields

entity_idintegerThe new entity ID
namestringThe entity name
entity_typestringbusiness product place person media
domainstring|nullAssociated domain, if detected
Example payload
{
  "event": "entity.new",
  "timestamp": "2026-06-13T14:22:03.000000Z",
  "data": {
    "entity_id": 23,
    "name": "Fischer's at Baslow Hall",
    "entity_type": "business",
    "domain": null
  }
}