Brand Studio docs

For agents

Service-account API keys, the MCP endpoint and its tools, idempotent retries, and the contract-version promise — how a program calls this product, not a person.

Everything above this page describes a report a person opens in a browser. This page describes the same product called by something else — your own script, agent, or scheduled job — issued its own credential, scoped to only what it needs, and able to retry without paying twice.

Service-account API keys

A key authorizes a program without handing it a human's login. Issue one from the account's API keys page (or POST /api/account/api-keys while signed in with a real session — a key cannot create or revoke its own siblings). It is shown once, as bsk_live_<random>; only its SHA-256 hash is ever stored, and every later view shows just the last four characters.

  • GET /api/account/api-keys — every key on file, masked
  • POST /api/account/api-keys — { name, scopes: ["run_pulls", "read_reports"] } -> { key, api_key }
  • DELETE /api/account/api-keys?id=... — revoke one key, permanently
ScopeGrants
run_pullsMay quote and start a comprehensive pull and spend the account's credits, within the alpha limits.
read_reportsMay poll a pull's status and read a finished report or brand kit. Cannot start one.

Issue narrow keys

A key that could do both is barely better than the session token it replaces. Issue narrow keys — one for the job that starts pulls, a different one for the job that only reads them — and revoke either without touching the other.

Retries that do not charge twice

POST /api/comprehensive (and the MCP run_brand_pull tool, which calls the identical handler) accepts an optional idempotency_key, unique per account. A retry sent with the same key — a flaky network, a queue redelivery, an agent that resends on timeout — returns the ORIGINAL pull: the same pull_id, HTTP 200, and never a second reservation or a second charge. The guarantee is enforced at the database, not only in application code: a partial unique index on the pull row is what makes a genuine race between two concurrent identical requests collapse to one pull rather than two.

The MCP endpoint

POST /api/mcp speaks Streamable HTTP MCP in its simplest legal shape: one JSON-RPC 2.0 object in, one out, no session id, no SSE stream to keep open. Hand-rolled, not the SDK — there are exactly three methods to implement.

FieldValue
EndpointPOST /api/mcp
AuthAuthorization: Bearer — a service-account key (bsk_live_..., described above) or the token an assistant receives when it connects through OAuth (Claude or ChatGPT; setup on /mcp), on every call including initialize. An OAuth assistant is read-only.
Methodsinitialize, tools/list, tools/call

Tools

ToolScopeDoes
list_brandsread_reportsEvery saved brand with its latest free read, its weekly checks and schedules, its page-list read and its latest report ID.
get_brand_basicsread_reportsOne saved free read in full: the graded palette with its evidence, typography, logos, icons, homepage images, social profiles, links and the methods that made the read.
get_brand_changesread_reportsWhat changed between a saved brand's free read and the read before it.
list_brand_changesread_reportsWhat changed across the account's saved brands in the last days.
get_site_pagesread_reportsThe pages a saved brand lists in its own sitemaps, and what changed since the previous read.
get_social_changesread_reportsWhat changed between a saved brand's two latest social snapshots.
get_search_changesread_reportsWhat changed between a saved brand's two latest search snapshots, and whether newer Google data exists.
get_brand_monitoringread_reportsEverything saved about one brand as one Markdown document.
list_brand_reportsread_reportsThe account's saved full reports.
get_brand_report_overviewread_reportsA saved report's overview.
get_brand_report_sectionread_reportsA bounded page of saved evidence in one area of a report.
get_brand_mediaread_reportsPreserved image and video references from a report, with their provenance.
open_brand_reportread_reportsOpens a saved report as an interactive app in the chat. Never starts collection.
get_brand_report_view_chunkread_reportsOne display chunk of a saved report, used by the interactive app.
quote_brand_pullrun_pullsA zero-spend preview of a pull: its scope, plan hash and maximum provider cost. No collection and no charge.
run_brand_pull(url, pull_plan, seo_market, plan_bindings, plan_hash, approved_max_cost_usd, tier?, idempotency_key?)run_pullsValidates the same reviewed plan as the website, reserves the durable queue job and returns its pull_id. Missing or stale approval is refused before collection.
get_pull_status(pull_id)read_reportsRunning, passed, partial, or failed, with per-leg progress.
get_brand_report(pull_id)read_reportsThe finished capture_result, stamped with contract_version.
get_brand_kit(pull_id)read_reportsA download URL for the brand kit .zip. An OAuth connection receives a report link instead.

First request a zero-spend quote at POST /api/query-manifest using pull_plan, seo_market, plan_bindings and tier. Review the returned scope, hash and maximum provider cost before supplying that exact approval envelope to run_brand_pull. The server independently enforces the operator-approved user, plan, cap and one-run approval ID; a quote does not enable spending. Work runs in the shared durable queue, never inline. Scope refusals name the missing scope. Customer credits remain separate from provider dollars, and unsupported or parked selections cannot run.

The versioning promise

Every report response — the status poll once a pull has evidence, and the MCP get_brand_report tool's own payload — carries contract_version: "brand-studio-report/v1". That is a promise about the OUTER envelope a third party may build against, not a restatement of the dozen internal schema_version fields already carried inside capture_result and its collectors (those describe internal shape and are unchanged by this promise). A breaking change to what a report response carries ships as v2, with a deprecation window, rather than as a silent edit under a consumer already depending on v1.

The full text of this page is also served at /llms.txt, in the shape a model reading it directly expects.

On this page