/v1/watch — Brand Watch CRUD

Schedule a recurring /v1/check and webhook your endpoint when triggers fire (mention added/removed, rank changed, citation changed). Watch creation is free; each scheduled run is billed at the configured mode's rate.

POST/v1/watch
GET/v1/watch/{id}
DELETE/v1/watch/{id}

Create a watch

bash
curl https://api.mentionsapi.com/v1/watch \
  -H "Authorization: Bearer lvk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "query": "best CRM for small business",
    "brand": "HubSpot",
    "mode": "quick",
    "interval": "daily",
    "webhook_url": "https://your.app/webhooks/mentionsapi",
    "webhook_secret": "whsec_at_least_16_chars_long_secret",
    "trigger_on": ["rank_changed", "mention_added"]
  }'

Request body — POST /v1/watch

FieldTypeDescription
query
required
string1–480 chars. The question that drives each scheduled /v1/check.
brand
required
string1–100 chars. The brand to track.
mode
required
stringAny shippable /v1/check mode: 'quick', 'perplexity_live', 'chatgpt_live', 'gemini_live', 'ai_overview', 'ai_mode', 'bing_copilot', or 'all_live'. mode:deep and mode:change_track return 501 today.
interval
required
string'hourly' | 'daily' | 'weekly'. Cron precision is 5-min granularity within each bucket.
webhook_url
required
string (URL)Where we POST when triggers fire. Must be publicly reachable HTTPS.
webhook_secret
required
string16–128 chars. Used to compute the HMAC SHA-256 signature on every delivery.
trigger_onoptional
string[]Subset of: 'mention_added' | 'mention_removed' | 'rank_changed' | 'citation_changed'. Defaults to all four if omitted.

Response — POST /v1/watch (201)

json
{
  "request_id": "req_01JBQ2K8H...",
  "id": "f3e8c7d2-...",
  "status": "active",
  "next_run_at": "2026-04-26T12:00:00Z"
}

Read a watch — GET /v1/watch/{id}

bash
curl https://api.mentionsapi.com/v1/watch/f3e8c7d2-... \
  -H "Authorization: Bearer lvk_live_..."

Returns the same shape as POST. status is 'active' when the watch is enabled, 'paused' after a soft-delete.

Delete (pause) a watch — DELETE /v1/watch/{id}

bash
curl -X DELETE https://api.mentionsapi.com/v1/watch/f3e8c7d2-... \
  -H "Authorization: Bearer lvk_live_..."

DELETE is a soft-pause: the watch is set to enabled=false but the record (and its historical runs) are preserved. A subsequent GET returns status: 'paused' rather than 404. There is currently no hard-delete; if you need permanent deletion, email support@mentionsapi.com.

Webhook payload

When a trigger fires, we POST the following JSON to your webhook_url:

json
{
  "event": "watch.fired",
  "watch_id": "f3e8c7d2-...",
  "watch_name": "best CRM for small business × HubSpot",
  "fired_at": "2026-04-26T12:00:42Z",
  "trigger": "rank_changed",
  "triggers_fired": ["rank_changed"],
  "run_id": "9b41c2e0-...",
  "diff_summary": "rank moves: HubSpot 6→3; top 3 reordered",
  "delta": {
    "new_brands": [],
    "dropped_brands": [],
    "rank_changes": [
      { "brand": "HubSpot", "from_rank": 6, "to_rank": 3 }
    ],
    "top3_reordered": true
  },
  "previous_response": [ /* previous run's brand_mentions[] (null on first run) */ ],
  "current_response": { /* this run's full response: providers[], brand_mentions[], citations[], usage */ }
}

trigger is the first trigger that fired; triggers_fired lists all of them. delta.rank_changes carries per-brand from_rank to_rank moves — previous_response is the previous run's brand_mentions array (not a full response), and current_response is this run's full response.

Webhook headers

FieldTypeDescription
x-mentionsapi-signatureoptional
stringPlain HMAC SHA-256 hex of the raw body, keyed by your webhook_secret (no prefix, no timestamp in the MAC). Verify before parsing the body.
x-mentionsapi-eventoptional
stringEvent type — currently always 'watch.fired'. Future versions may add 'watch.error' for delivery failures.
x-mentionsapi-watch-idoptional
stringUUID of the watch. Use this to look up your local mapping.
x-mentionsapi-timestampoptional
stringUnix seconds at signing time. Informational on the primary scheme; authenticated by the legacy x-mentions-signature scheme below.
x-mentionsapi-delivery-idoptional
stringUnique per delivery attempt. Idempotent on your side — log + reject duplicates.
x-mentions-signatureoptional
stringLegacy timestamped variant: 't=<unix-seconds>,v1=<hex>' where v1 is HMAC SHA-256 of '<t>.<raw body>'. Use this if you want replay protection baked into the MAC.

HMAC verification

Compute SHA-256 HMAC of the raw request body using your webhook_secret; compare with x-mentionsapi-signature using a constant-time compare. If they don't match, drop the request — it isn't from us.

javascript
import crypto from "node:crypto";

function verifyMentionsApiSignature(
  rawBody: string,
  signatureHeader: string, // "x-mentionsapi-signature"
  secret: string,
): boolean {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signatureHeader),
  );
}

Retry policy

Failed deliveries (any non-2xx response, or no response within 10s) are retried with backoff: 1min, then 5min — 3 total attempts. After the final failure the delivery is recorded as failed and the run is marked undelivered; the next scheduled run fires a fresh delivery as usual. Idempotency: x-mentionsapi-delivery-id is unique per delivery attempt; treat duplicates as no-ops.

Error codes

FieldTypeDescription
400 invalid_requestoptional
ErrorSchema validation failed (e.g. webhook_secret too short).
404 watch_not_foundoptional
ErrorGET/DELETE on an id that doesn't belong to your account.
501 mode_roadmapoptional
ErrorTried to create a watch with mode:deep or mode:change_track. Use any shippable mode (e.g. mode:quick or mode:all_live).

Full error catalog at /docs/errors.