/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–500 chars. The question that drives each scheduled /v1/check.
brand
required
string1–100 chars. The brand to track.
mode
required
string'quick' or 'perplexity_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 [email protected].

Webhook payload

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

json
{
  "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

FieldTypeDescription
x-mentionsapi-signatureoptional
stringHMAC SHA-256 hex of the raw body, keyed by your webhook_secret. 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-delivery-idoptional
stringUnique 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.

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 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

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 mode:quick or mode:perplexity_live.

Full error catalog at /docs/errors.