/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.
/v1/watch— Create a watch/v1/watch/{id}— Read a watch/v1/watch/{id}— Pause (soft-delete) a watchCreate a watch
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
| Field | Type | Description |
|---|---|---|
queryrequired | string | 1–480 chars. The question that drives each scheduled /v1/check. |
brandrequired | string | 1–100 chars. The brand to track. |
moderequired | string | Any 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. |
intervalrequired | string | 'hourly' | 'daily' | 'weekly'. Cron precision is 5-min granularity within each bucket. |
webhook_urlrequired | string (URL) | Where we POST when triggers fire. Must be publicly reachable HTTPS. |
webhook_secretrequired | string | 16–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)
{
"request_id": "req_01JBQ2K8H...",
"id": "f3e8c7d2-...",
"status": "active",
"next_run_at": "2026-04-26T12:00:00Z"
}Read a watch — GET /v1/watch/{id}
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}
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:
{
"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
| Field | Type | Description |
|---|---|---|
x-mentionsapi-signatureoptional | string | Plain 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 | string | Event type — currently always 'watch.fired'. Future versions may add 'watch.error' for delivery failures. |
x-mentionsapi-watch-idoptional | string | UUID of the watch. Use this to look up your local mapping. |
x-mentionsapi-timestampoptional | string | Unix seconds at signing time. Informational on the primary scheme; authenticated by the legacy x-mentions-signature scheme below. |
x-mentionsapi-delivery-idoptional | string | Unique per delivery attempt. Idempotent on your side — log + reject duplicates. |
x-mentions-signatureoptional | string | Legacy 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.
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
| Field | Type | Description |
|---|---|---|
400 invalid_requestoptional | Error | Schema validation failed (e.g. webhook_secret too short). |
404 watch_not_foundoptional | Error | GET/DELETE on an id that doesn't belong to your account. |
501 mode_roadmapoptional | Error | Tried 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.