Do not burn tokens watching. Wake your agent only when it matters.
Documentation · v0.1

AI Brand Watch

Everything you need to run, schedule and consume Brand Watch — as a human or as an agent.

QuickstartConnect via MCPInput schemaOutput & metricsAudit vs WatchPricing eventsWebhook payloadVerify the signatureDelivery & retriesFAQ

Quickstart

  1. Open AI Brand Watch on the Apify Store (free Apify account; free plan credits cover a trial run).
  2. Enter your brand name. That's the minimum — Desvela discovers the category questions, competitors and your domain, and freezes them as a prompt set. To go fully manual, override with your own queries, competitors and domain.
  3. Run it once — this audit run establishes your baseline (no alerts yet, by design). Every later run reuses the same frozen prompt set, so diffs are comparable.
  4. Add an Apify Schedule (e.g. weekly). From then on you only hear about changes.

Connect via MCP

Run Brand Watch from Claude, Cursor or any MCP client — your agent starts watches and reads results directly. Endpoint: https://mcp.apify.com/?tools=desvela/brand-watch, authenticated with your Apify API token (runs bill to your Apify account, pay per check). Also listed in the official MCP registry as dev.desvela/brand-watch.

Claude Code

claude mcp add brand-watch \
  --transport http https://mcp.apify.com/?tools=desvela/brand-watch \
  --header "Authorization: Bearer YOUR_APIFY_TOKEN"

Cursor (.cursor/mcp.json)

{
  "mcpServers": {
    "brand-watch": {
      "url": "https://mcp.apify.com/?tools=desvela/brand-watch",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}

Claude Desktop (claude_desktop_config.json — via mcp-remote)

{
  "mcpServers": {
    "brand-watch": {
      "command": "npx",
      "args": ["-y", "mcp-remote",
        "https://mcp.apify.com/?tools=desvela/brand-watch",
        "--header", "Authorization: Bearer YOUR_APIFY_TOKEN"]
    }
  }
}

Then ask your agent: "run a brand watch for <brand> and summarize its mention rate across engines" — or, fully manual, "with these 10 category questions on openai+perplexity, 3 samples each, and summarize the mention rate".

Input schema

Only brandName is required. Omit queries to enter zero-config mode: the actor auto-discovers a category prompt set (and reuses a frozen one from a previous run when present). Supply queries to go custom — the historical behaviour is unchanged.

FieldTypeNotes
brandNamestring · requiredThe brand to track.
domainstringYour own top-level domain (e.g. desvela.ai). Enables owned vs earned citation rates and matches the domain as a mention.
brandAliasesstring[]Domain, alternate spellings — anything the engines may call you.
competitorsstring[]Tracked alongside your brand; new entrants trigger alerts.
competitorDomainsobjectCompetitor domains keyed by name (e.g. {"Coda": "coda.io"}). Measures competitorCitationRate; null without it, never 0.
queriesstring[] · max 30Optional. Without it the actor runs zero-config (auto-discovery of a prompt set) or reuses the frozen set from a previous run. With it, used verbatim (custom mode).
autoGenerateQueriesboolean · default falseOnly with given queries: discovered queries are added and combined (dedupe, max 30). Ignored in zero-config mode.
enginessubset of openai, perplexity, anthropic, geminiDefault: openai, perplexity.
samplesPerQueryinteger 1–5 · default 3Mention rate is computed across samples.
webhookUrlstringPOST endpoint for signed change events.
notifyOnlyOnChangeboolean · default trueIf false, the webhook also fires on no-change runs.
includeRecommendationsboolean · default falseGenerate 3–5 concrete GEO actions from this run's data. Charged as one recommendation event, only when delivered.
includeSentimentboolean · default falseOpt-in paid add-on: a positive/neutral/negative label per measured cell. Descriptor only — never participates in diff or alerts. Charged per delivered cell as sentiment.
promptSetActionkeep · renew · default keepkeep reuses the frozen prompt set (the WATCH mode). renew consciously regenerates it — that run sets a new baseline and emits no change events.

Output & metrics

Every measured cell (query × engine) in the dataset carries schemaVersion: 2 and these fields:

MetricMeaning
mentionRateFraction of samples (0–1) whose answer mentioned the brand. The canonical signal — shareOfVoice remains as a legacy alias with the same value.
competitiveShareOfVoiceBrand mentions over samples where the brand or a watched competitor appears. Null when no competitor appears in any sample — never 0.
avgPosition / recommendationRankTextual order of first appearance vs explicit rank in a numbered recommendation list. prominenceScore (0–1) derives from the rank; null means "not measurable", not "low prominence".
ownedCitationRate / earnedCitationRateFraction of samples citing your domain vs citing any non-own source (requires domain). Null — never 0 — when citations weren't available.
competitorCitationRate + competitorRatesFraction of samples citing each watched competitor's domain (requires competitorDomains).
stableSourcesSources present in ≥50% of samples — the stable base for diffing, so a new source never fires from LLM jitter alone.
sentimentDominant positive/neutral/negative label per cell, opt-in. Descriptor only — never alerts.
evidenceVerbatim excerpt from the engine's answer per distinct sample — the part you can check yourself.

Audit vs Watch

Each run has a runRole. The first run of a prompt set is an audit: it establishes the baseline silently — no alerts, by design. Every later run reuses the same frozen prompt set and is a watch: it diffs against that baseline and fires change events. A prompt set is only replaced by an explicit promptSetAction: renew, which is itself an audit run. Comparability is the point: if the questions changed between runs, the diff would mix "my visibility changed" with "what I ask changed".

Pricing events

EventPriceCharged when
actor-start$0.02Once per run.
brand-query-checked$0.08Per query × engine, all samples included. Never charged if all samples for that pair failed.
recommendation$0.04Opt-in via includeRecommendations. Charged only when the recommendations are actually generated and delivered.
prompt-discovery$0.04Zero-config mode only, when a prompt set is generated by the auto-discovery.
sentiment$0.04Opt-in via includeSentiment, per measured cell actually delivered.
change-alertfreePer change event detected.

Example: 20 queries × 2 engines, weekly ≈ $14/month.

Webhook payload

One POST per run. The body is a self-contained, versioned envelope (schema v2): it carries the run, the frozen question set it was measured under and the verbatim evidence, so a consumer can understand a change without reopening the dataset. The event type is a mention-rate change — appeared · disappeared · sov_shift (≥25 points) · new_source · new_competitor. When a change fires, the payload also carries the context that explains it: reason, mention_rate (with delta), new_cited_source (a source the engine now cites in every sample), competitors_now_mentioned, evidence, samples and stableSources. Set-membership jitter never triggers an alert on its own. share_of_voice is kept as a legacy alias of mention_rate — consumers of the v1 contract keep working unchanged.

{
  "schemaVersion": 2,
  "runId": "wAT3x...",
  "brand": "Acme CRM",
  "promptSetId": "ps_a1b2",
  "promptSetVersion": 4,
  "generatedAt": "2026-08-08T10:05:00.000Z",
  "events": [{
    "event": "brand_visibility_changed",
    "schemaVersion": 2,
    "brand": "Acme CRM",
    "engine": "perplexity",
    "query": "best CRM for small business in Spain",
    "promptSetId": "ps_a1b2",
    "promptSetVersion": 4,
    "runId": "wAT3x...",
    "change": {
      "type": "disappeared",
      "reason": "mentionRate 0.67 → 0 (disappeared)",
      "mention_rate": { "before": 0.67, "after": 0, "delta": -0.67 },
      "share_of_voice": { "before": 0.67, "after": 0 },
      "new_cited_source": "reddit.com/r/smallbusiness/..."
    },
    "evidence": ["Acme is absent from the engine's answer for this query"],
    "samples": 3,
    "stableSources": ["hubspot.com/...", "zoho.com/..."],
    "checked_at": "2026-08-08T09:00:00Z",
    "previous_checked_at": "2026-08-01T09:00:00Z"
  }]
}

Headers: X-Desvela-Signature: hmac-sha256=<hex> (computed over the entire envelope body) and User-Agent: Desvela-BrandWatch/0.1 (+https://desvela.dev/bot).

Verify the signature

The secret is generated on your first run and stored with your watch state in the brand-watch-state key-value store in your Apify account (key STATE-*). Each run's SUMMARY record shows a hint of it.

import { createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, header, secret) {
  const expected = 'hmac-sha256=' +
    createHmac('sha256', secret).update(rawBody).digest('hex');
  return timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}

Delivery & retries

FAQ

Why a mention rate instead of a single answer? AI answers vary between runs; single snapshots are noise. We sample N times and report the rate — the % of answers that mention you. Competitive share of voice and citation rates ride along on the same samples.

Do I need to provide the questions? No. Just a brand name: Desvela discovers the category questions, competitors and your domain, and freezes them as a prompt set. Every scheduled run measures the same frozen questions, so the diff is meaningful. You can override with your own list at any time.

Is the API answer identical to the consumer product? No — correlated, not pixel-identical. We always measure the same way, so diffs stay valid. Full note on the product page.

MCP? Available today via Apify's MCP gateway: point your client at https://mcp.apify.com/?tools=desvela/brand-watch with your Apify API token as Authorization: Bearer. Your agent can start runs and read results over MCP — runs are billed to your Apify account as usual. A native server is live for discovery: POST https://registry.desvela.dev/mcp exposes the free preflight(domain) tool (no auth), backed by our index of the top-100K.

x402? Live, through Apify. Brand Watch is eligible for agentic payments, so an agent can run it paying USDC on Base with no Apify account, no billing and no API key. Call POST https://api.apify.com/v2/acts/desvela~brand-watch/runs without credentials and you get 402 Payment Required with the terms in the payment-required header. The prepaid token has a $1 minimum and expires in 14 days, so spend it rather than topping up per run.