/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–500 chars. The question that drives each scheduled /v1/check. |
brandrequired | string | 1–100 chars. The brand to track. |
moderequired | string | 'quick' or 'perplexity_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 [email protected].
Webhook payload
When a trigger fires, we POST the following JSON to your webhook_url:
{
"event": "watch.fired",
"watch_id": "f3e8c7d2-...",
"fired_at": "2026-04-26T12:00:42Z",
"trigger": "rank_changed",
"delta": {
"providers": {
"chatgpt": { "old_rank": 6, "new_rank": 3 },
"perplexity": { "old_rank": 6, "new_rank": 6 }
}
},
"current": { /* full /v1/check response shape */ },
"previous_run_at": "2026-04-25T12:00:00Z"
}Webhook headers
| Field | Type | Description |
|---|---|---|
x-mentionsapi-signatureoptional | string | HMAC SHA-256 hex of the raw body, keyed by your webhook_secret. 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-delivery-idoptional | string | Unique per delivery attempt. Idempotent on your side — log + reject duplicates. |
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 exponential backoff: 5s, 30s, 5min, 30min, 2h, 6h. After 6 failures we mark the watch as degraded and email the account owner. Idempotency: x-mentionsapi-delivery-id is unique per delivery; 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 mode:quick or mode:perplexity_live. |
Full error catalog at /docs/errors.