Skip to content

API

The dashboard is a client of this API; anything it shows, you can fetch. Base path /api. Answers are JSON, except the downloads and streams the OpenAPI description names with their media type: CSV exports (text/csv, streamed; a stream that fails part-way ends the connection rather than a short file looking complete), server-sent events, a zip, PDFs, Markdown, HTML pages and the traffic Worker’s JavaScript. Errors are always JSON, { "error": "…", "code": "…", "requestId": "…" } with a conventional status code (the sentence is for a person; code, where present, names the refusal for a program: bad_id, bad_json, bad_url, bad_input, site_cap, workspace_closed and so on; requestId is the response’s x-request-id, to quote when asking for help). A malformed id, URL or body is a 400, never a 500; deleting something that is not there is a 404. A check that ran and found nothing (a domain’s proof not there yet, a confirmation email that does not need resending) answers 200 with ok: false or sent: false, a code and the reason in error and detail/reason: that is the check’s answer, not a failure. The same description as an OpenAPI 3.1 document, for code generators and API clients: GET /api/openapi.json (the document, also listed in the API catalogue at the well-known address). For agents there is also an MCP server over the same data. Routes kept for older integrations are marked deprecated there, with the route that replaced them, and are not listed on this page.

Authenticate with an API key from Settings, API: Authorization: Bearer ca_…. A key has scopes: read acts as a viewer, write also changes what an analyst changes, and export alone reads only the exports and report downloads. A key expires (a year by default), may be limited to some sites and to some addresses, and is limited to 300 requests a minute; see API keys. The role column is the lowest role that may call the route: viewer, analyst, admin or owner. Admin and owner routes need a signed-in member (“no keys”); public routes need no authentication. Large result sets are capped at 500 rows. The long lists (runs, findings, a site’s history, the audit log) page with an opaque cursor: a page that has more after it carries an x-next-cursor header, and ?cursor= with that value returns the next page (?envelope=1 puts { items, nextCursor } in the body instead).

Start runs, follow them, and fetch the deliverables.

Method Path Role What it does
GET /workflows viewer Runs in this workspace, newest first, with their site and headline numbers. ?limit= (100, at most 500) and ?cursor= (the x-next-cursor header of the previous page); ?envelope=1 answers { items, nextCursor }. Query: ?siteId= (one site).
POST /workflows analyst { url, kind?, maxPages?, render?, llm?, languages?, intentCount?, perspectives? }: start a full or change run. 402 when the site or run allowance is used up; 403 when the domain is not verified. In worker mode the run is queued and status is queued.
POST /estimate viewer Pre-flight for a run of this size: { url?, kind?, pages?, render?, llm? } gives the allowance it consumes and what is left, requests and crawl minutes; model cost only where it is the caller’s to see.
GET /quote viewer What this org’s plan costs at a given number of sites. Query: ?sites= (a number of sites).
POST /workflows/:id/resume analyst { from?, only?, allowMore? }: resume a paused or failed run, re-run from a named step, or run only the named steps. Continuing a failed or paused run from where it stopped is free; executing again a completed step that re-crawls the site or re-asks the answer engines (discover, fetch, cite) is a new run’s worth of work and passes the same allowance as one (402 when it is spent) and, like one, needs a verified site (403). A run paused at the size its plan gives a run resumes only when an owner or admin allows it to continue: allowMore: true, with no figure to send or to read (the server decides how far, up to twice the size its plan gives a run); without it, 402. The API sends and receives no token numbers.
GET /workflows/:id viewer One run: status, every step of the pipeline in order with progress and timings (steps not reached yet are pending; steps this kind of run does not execute are skipped with the reason), summary and headline numbers.
GET /workflows/:id/events viewer Server-sent events while the run is live: state on every change (all the run’s steps, and the events written since the last frame), done at the end. Query: ?delta= (1 sends only the steps that changed after the first frame).
GET /workflows/:id/report viewer The site audit of a completed run: the standalone branded document, the shareable artefact; ?format=pdf for a PDF, ?download=1 for a file.
POST /workflows/:id/email analyst Send the report email for this run now, to the workspace’s configured recipients, and say what happened.
GET /workflows/:id/board viewer The run’s board numbers and brief, when the synthesis wrote one.

Everything a run produced. The workspace’s run pages read these.

Method Path Role What it does
GET /workflows/:id/intents/:intentId/handoff viewer A question the site does not answer as a writing brief; ?agent=writer|claude|codex|llm (default writer). Query: ?download= (1 sends it as a file).
GET /workflows/:id/citations viewer The citation panel of a run: per question, what each live engine answered and whether the site was cited; the summary with calibration against the simulator.
GET /workflows/:id/findings viewer The run’s findings, most severe first, with review verdicts, statuses and ticket references.
GET /workflows/:id/templates viewer Templates with page counts, word counts, JS ratio, timings and structured-data coverage.
GET /workflows/:id/perspectives viewer Perspective matrix: template × perspective → {served, blocked, challenge, error} counts.
GET /workflows/:id/entities viewer Entities resolved across pages with their defining page and Wikidata match.
GET /workflows/:id/claims viewer Factual claims with their evidence type, most specific first: ?limit= (500, at most 2000) and ?offset=. x-total-count is how many the run has and x-truncated: true says the list ends before them (x-next-offset is where the next page starts).
GET /workflows/:id/intents viewer Intents with their coverage verdicts and the pages retrieved for each, with who each is for (intent_class, relevance) and where it came from (provenance); support, content, machine and echo questions last.
GET /workflows/:id/simulations/:intentId viewer One simulation: the answer, the verdict and the passages it was built from.
GET /workflows/:id/analyses viewer Synthesis, site profile, review summary, performance profile and the change diff. Query: ?kind= (the kind of analysis).
GET /workflows/:id/events/all viewer The run’s event log, including delivery outcomes, oldest first, a page at a time: ?limit= (1000, at most 5000) and ?cursor= (the x-next-cursor header of the previous page, present while more follow; x-total-count is the whole log). The body is the list; ?envelope=1 answers { items, nextCursor, total } instead.
GET /workflows/:id/pages viewer Page inventory: status, template, words, AI-crawler refusals, performance. Query: ?cursor= (the x-next-cursor of the previous page), ?envelope= (1 answers { items, nextCursor } instead of the bare list), ?limit= (rows per page).
GET /workflows/:id/pages/table viewer The run’s pages as the Pages tab shows them, a page of rows at a time, sorted and filtered here: { items, nextCursor, from, to, matching, summary, cited }. Query: ?q= (in the address or title), ?template= (a template fingerprint), ?status= (200 or other), ?ai= (refused or served), ?cited= (yes or no: cited in a real answer in the last 90 days), ?sort= (url, cited, status, ai, words, js, lcp or clicks; the inventory’s order by address without one), ?desc=1, ?limit= (10 by default, 200 at most; every row when 15 or fewer match), ?cursor= (the nextCursor of the page before, in the same order and filters). Each item is an inventory row with citedIn (the real answers that cited the page: answers, questions, engines; null when none did). summary counts every page the run sampled (refused by an AI crawler, not answering 200, needing JavaScript for over 40% of the text, cited), lists its templates, names the page to fix first and the cited pages the run did not sample; cited is the window and the asks with an answer in it.
GET /workflows/:id/pages/:pageId viewer One page of the run: every identity’s response, technical facts, extracted content and lab performance, and cited: the run’s simulated answers that cited it.

The inbox across sites, one finding, the lifecycle, ticket export, verified fixes and the forecasts’ calibration record.

Method Path Role What it does
GET /findings/:id/handoff viewer One finding as a brief; ?agent=claude|codex|llm (default claude), ?download=1 for a .md file.
POST /findings/handoff viewer Several findings as one task list, most severe first as given: { ids, agent? }; the answer is Markdown.
GET /findings viewer Findings inbox across the org’s latest runs, filterable; most severe first. ?limit= (500, the most) and ?cursor= (the x-next-cursor header of the previous page) page through it; ?envelope=1 answers { items, nextCursor }. Query: ?category= (a finding category), ?run= (one dated run’s id instead of each site’s latest), ?severity= (critical, high, medium, low or info), ?siteId= (one site), ?status= (a status).
PATCH /findings analyst { ids, status?, note?, plan? }: one decision for up to 200 findings, in one transaction. Answers { updated, failed, results: [{ id, ok, status?, error? }] } in the order given: a finding that is not in the workspace (or not an id) is reported, not fatal; anything else rolls the whole batch back.
PATCH /findings/:id analyst { status, note? } changes the status; { plan: { ownerUserId?, ownerLabel, when } | null } sets or clears who is doing it and by when (and moves an open finding to accepted).
GET /findings/:id viewer One finding with its site: summary, recommendation, evidence, knowledge-base entries, status, plan and ticket reference.
POST /findings/:id/ticket analyst Export a finding as an issue; the reference is stored on the finding and carried from run to run. Idempotent.
POST /findings/tickets analyst Export several findings at once; each result names its finding, so one failure does not hide the others.
GET /findings/counts viewer ?siteId=: findings still to fix (open, planned, done and being checked) in each site’s latest completed run, and of those the critical and high ones; per site and in all. The one count Home, the sites, a site’s overview, the rail and Findings agree on.
GET /findings/:id/thread viewer A finding’s thread: its comments (oldest first, deleted ones as a line), who watches it, whether the caller does, and the members who can be mentioned.
POST /findings/:id/comments analyst { body, mentions? }: comment on a finding. mentions are the user ids the composer picked; each mentioned member is told and starts watching, and so does the author; other watchers hear of the comment.
PATCH /findings/comments/:cid analyst { body }: the author edits their comment.
DELETE /findings/comments/:cid analyst The author, or an admin, removes a comment; the thread keeps a line saying one was removed.
PUT /findings/:id/watch viewer { watching }: follow or stop following a finding.
POST /findings/plan analyst { ids, plan: { ownerUserId?, ownerLabel, dueAt? } | null }: plan up to 200 findings at once, or clear their plans. dueAt is a calendar day or null; the when bucket follows from it. Planning an open finding makes it Planned. Applied to the same finding in every run the next comparison reads; the person given them is told once each. Answers per finding, so one failure does not hide the others.

Answers by engine: cited and named shares per engine, per question and over time.

Method Path Role What it does
GET /matrix viewer Per engine: cited share with its interval, mentioned share, questions asked and cited, the trend against the previous period of the same length, crawler access, the hosts cited in the site’s place, the engine’s retrieval focus, and for Google the share of results pages without an AI Overview. ?siteId= for one site, else every site of the workspace. Query: ?engines= (comma-separated answer engine ids), ?from= (start of the period (an ISO date)), ?to= (end of the period (an ISO date)).
GET /matrix/engines/:engine viewer One engine in depth (?siteId= optional): every question with its latest answer and the hosts cited instead, who wins most often in the site’s place, and the weekly cited share. Query: ?from= (start of the period (an ISO date)), ?to= (end of the period (an ISO date)).
GET /engines/calibration viewer How often the engine’s app agrees with its API, per engine: on full runs the panel’s top questions are also read from ChatGPT’s and Gemini’s apps (never part of a figure), and each is compared with the API’s majority on whether the site is cited, whether it is named, and how much their cited domains overlap. platform across every workspace (weekly aggregates only), workspace on this workspace’s own runs; ?from=&to= (the last 90 days by default).
GET /engines viewer The answer engines the workspace’s plan measures that this installation can ask right now, with their labels and one-line focus; locked: the ones the installation can ask that the plan leaves out (PRICING-TIERS-PLAN.md phase 1), each with the cheapest plan that includes it, which the app names once and never shows as an empty card.

Bulk data for BI tools and scripts. A key with the export scope alone may read these.

Method Path Role What it does
GET /export/visibility.json viewer Visibility by ISO week × site × engine × domain: the cited share of the site, its named competitors and (?others=, up to 20) the domains cited most instead, each with its asks, 95% interval, questions asked and cited, and for the site the answers that named it without a link. ?siteId= for one site (every visible site otherwise), ?from=&to= (the last 12 weeks by default, a year at most), ?engines= a comma list.
GET /export/citations.csv viewer The citation panel’s answers in the period, one line per source each answer cited (an answer that cited nothing is one line without a source): when, which run, which engine and question, the asks behind it and in how many the site was cited, whether it was named without a link, and whether Google showed no AI Overview. ?siteId=&from=&to=&engines=.
GET /export/findings.csv viewer The findings of each site’s latest completed run (the list the Findings page shows), most severe first, with status, owner and due date. ?siteId=&status=&severity=.
GET /export/visibility.csv viewer Visibility by ISO week × site × engine × domain, the rows of /api/export/visibility.json as CSV: the cited share of the site, its named competitors and (?others=, up to 20) the domains cited most instead, with asks, the 95% interval and questions. ?siteId=&from=&to=&engines= (the last 12 weeks by default).

The prompt library: the questions your workspace tracks as prompts, each asked of its engines on a schedule, with their answers and what changed. A tracked prompt joins the topic it fits. Each answer one engine gives a tracked prompt is one prompt check, and the plan includes a number of tracked prompts and of prompt checks a month (Settings, Usage).

Method Path Role What it does
GET /prompts viewer The library: prompts of the workspace (?siteId=, ?tag=), each with its latest verdict per engine, the engines it is asked and its projected monthly checks, plus the plan’s allowance and the engines the platform has.
POST /prompts analyst Deprecated (the tracked-prompt list is retired, FEATURE-GAP-PLAN.md item 10): a question is added to a topic, and tracked word for word, with POST /api/sites/:id/topics/:topicId/questions { text, exact: true }. { siteId, text, locale?, engines?, cadence?, tags?, brandTerms?, competitors?, shareable? }: the text becomes a question tracked word for word in the site’s topic it fits best (the prompt row keeps only its history, paused). Text up to 500 characters; one per wording and locale per site (409); refused with 402 beyond the plan’s allowance of questions tracked word for word.
PATCH /prompts/:id analyst { text?, locale?, engines?, cadence?, tags?, brandTerms?, competitors?, shareable? }: change a prompt. Resuming it or asking more engines is checked against the plan like a new one.
DELETE /prompts/:id analyst Delete a prompt with its answer history and its notices.
POST /prompts/:id/run analyst Run a prompt now on its engines. Analysts and up; 30 manual runs an hour per workspace; counts against the plan’s monthly prompt checks (one prompt asked of one engine once) like a scheduled run, and refuses with 402 when none are left. Answers 202 at once and runs in the background; ?wait=1 waits for the answers instead.
GET /prompts/:id/results viewer A prompt’s history: ?engine= narrows it, ?limit= (20, at most 200) caps the answers returned in full. The timeline (up to 400 points) carries each answer’s verdict and change kinds without the text.
POST /prompts/import analyst CSV import into a site’s library (?siteId=, or { siteId, csv } as JSON): columns text, locale, tags (separated by ; or |), cadence, header optional, 1,000 rows at most. Each row is created or skipped with its reason; the plan is checked row by row.
POST /prompts/from-question analyst { siteId, query, cadence?, engines? }: copy a question from the site’s question set (the questions its runs generated) into the library, in the question’s language.

What Vercatus learns across sites: the engine playbooks (what plays like each did, from verdicts and natural experiments, above the privacy floor), the engines’ confirmed changes with the topics they touched, a site’s AI Demand Index with its coverage of demand, the co-citation network, and notices.

Method Path Role What it does
GET /sites/:id/ai-demand viewer The AI Demand Index of the site’s active topics (PHASE-5.md §3): per topic, AI conversations a month in its market as a range (low–high, the 10th–90th percentile of 5,000 draws over the factors’ ranges, mid the median), its grade (A–D), whether it is calibrated by first-party data (calibrated, calibratedSites), every factor as a source with its value and date, and a sentence; null until the daily pass has estimated it. coverage: the share (0–1) of the market’s searched demand the topics cover and the engines’ sub-queries that belong to no topic, with their volume and engines. weighting: whether Answer Share weights topics by demand or tier. The assistant-fetch demand stays GET /sites/:id/demand.
GET /intelligence/playbooks viewer ?vertical=&engine=&market=: the engine playbooks (PHASE-5.md §4): per play type, engine, vertical and market, what plays like it did across sites (customer verdicts, and natural experiments at half weight): n, sites, the won rate (a share in percent with its Wilson range, shrunk toward the parent segment) and the median effect with its range, with a sentence. Only segments with three workspaces or more (the privacy floor); aggregates, never a workspace. Markets roll up to their language and to all markets, and each play type’s record is the most specific segment that clears the floor, with its scope in words. research: per play type, the knowledge-base entries that support it, with their grades, kept apart from the records (DATA-INTEGRITY-QA.md DQ-41).
GET /intelligence/communities viewer ?market= (a code such as hu-HU; required): how much of the answers in a market cite forums, Q&A sites and communities across every Vercatus site, over the last 28 days. The share of search answers citing any community with its Wilson range, per engine, and the platforms (reddit.com, quora.com, a forum host) by the share of answers citing them. A market is shown from three sites and thirty answers, a platform or an engine from three sites; below that the answer is { withheld: true, reason } with no figure. Aggregates, never a workspace, a site, a topic or a subreddit.
GET /intelligence/changes viewer The engines’ confirmed changes of the last 90 days (PHASE-5.md §4): drifts in what an engine cites and new models, in plain words, each drift with the caller’s topics whose cited pages changed (topics[] with siteId), and per engine the caller’s topics whose cited pages changed this fortnight against the one before (kind pages, three pages or more on each side). watching says what the check rests on: the caller’s engines, topics and answers in 28 days, and the platform detector’s citation changes this week against its bar. Keeps to the caller’s sites.
GET /intelligence/rewards viewer ?siteId=&engines=a,b: what the engines in scope reward, against the site’s own pages (DATA-INTEGRITY-QA.md DQ-10): each engine’s strongest learned patterns (the site’s vertical’s own where that segment has cleared the bar, else the engine-wide ones), the medians of the pages it cites and passes over, the site’s median over its observed pages and where it sits. Global aggregates over public-web observations, the same for every workspace; correlational, never a verdict. Available from a site’s first day: the patterns do not wait for its own verdicts.
GET /intelligence/engine-advice viewer ?siteId=&engines=a,b: what engines say they reward (EA-13..17), for the site’s segment (its kind, else its vertical) and market, or across every segment and market without a site: per engine, kind of change and theme cluster, the share of briefed sites whose engines say it in a consensus of their latest briefs (advice), the most specific record that clears the privacy floor (three workspaces) and cannot be subtracted from another, with its scope in words; counts are coarse lower bounds and shares to 5 points, a cluster’s label only words three workspaces’ themes share, never an example. Engines say: perception, not a measured effect (framing). outcomes: per engine and kind, how often plays made or backed by such advice won 45 days after shipping, from three workspaces. own: when nothing pooled clears the floor, the site’s own consensus themes of its latest week. Scoped to the caller’s sites; no cost and no workspace count.
GET /notices viewer ?siteId=&unread=1&dismissed=1&kind=&before=&limit=: the workspace’s notices (drift touching its pages, competitors passing it, prompt changes), newest first, with the unread count. Dismissed ones only when asked.
PATCH /notices/:id viewer { read?: boolean, dismissed?: boolean }: mark a notice read or unread for the workspace, or dismiss and restore it (analyst and above).
POST /notices/read-all viewer Mark every unread notice of the workspace read.

Why a cited page beats yours on one question, and fact-checked rewrites of a passage, which page plays use.

Method Path Role What it does
POST /rewrites analyst { siteId, url, findingKey?, passage? }: rewrite one passage of a page in three strategies (answer-first, evidence-led, structured), each re-scored before and after, aimed at what real answers to the page’s questions quote from other sites (passage provenance: what to cover, never facts to take). Without passage, the finding’s evidence passage or the page’s weakest section is used. Answers 201 with the rewrite, 422 when no variant kept to the passage’s facts, or 202 with the id when it takes longer than the request may wait (GET /api/rewrites/:id then has it). Uses one rewrite.
GET /rewrites viewer ?siteId=&url=: the rewrites of a site, or of one page, newest first, with what is left of the plan’s rewrites.
GET /rewrites/:id viewer One rewrite: the passage, its context, the rules with their numbers, and the variants with their scores before and after.
PATCH /rewrites/:id analyst { chosen }: the index of the variant the client copied (null clears it). This is the feedback the repository learns from.
GET /winners viewer ?siteId=: why other pages win the site’s questions, newest first, each with its headline, and what is left of the plan’s analyses.
GET /winners/:id viewer One analysis: the ranked indicator comparison with weights, both passages, both structure maps and the explanation.
POST /winners/:id/refresh analyst Analyse again now: re-reads the cited page and the site’s latest run and rewrites the explanation. Uses one analysis from the plan when it completes. Answers with the analysis, or 202 when it takes longer than the request may wait.

Did it work: Answer Share since the site started against the leader, a verdict on every shipped play (Won, Lost ground, No effect, Inconclusive or Too early, from a difference in differences against untouched topics, graded A to D), the wins, the plays waiting for a read, the first-party outcomes, and the experiment that holds topics out as controls.

Method Path Role What it does
GET /sites/:id/results viewer The site’s results, ?days= (28 by default, at most 365, the “now” window) or ?from=&to= (ISO dates; a quarter for the quarterly view; to defaults to now): since, Answer Share in the first 14 days of poll answers (then) against the window (now: 14 days until day 42, then 28, by default), the change (null while the two overlap; windows says when the first reading is due), the leader, and a rolling 28-day trend since; verdicts, the latest read of every shipped play with the counts by state (a play waiting for its first read counts as Too early); wins; pending, the shipped plays waiting for a read with its date; outcomes, AI sessions (GA4), assistant fetches (crawler logs) and Search Console AI impressions, then and now, each with its source or null when not connected; and the experiment, or null.
GET /sites/:id/plays/:playId/verdicts viewer Every read of a play’s verdict (days 14, 30 and 45, its own or its bundle’s), oldest first.
POST /sites/:id/experiment analyst { heldTopicIds }: holds these topics out as controls (an experiment): plays on them are paused and labelled held, so every verdict has untouched topics to compare with. They must be the site’s active topics and leave at least one to treat; a running experiment is replaced. Answers { experiment }. Analyst role; audited.
DELETE /sites/:id/experiment analyst Ends the site’s experiment: held topics are released and their plays may start. Answers { experiment: null }. Analyst role; audited.
GET /results viewer The workspace’s sites the caller can see, ?days= (28 by default) or ?from=&to=: each one’s Answer Share then and now with the change, its wins in the window and its verdict counts (Home and the portfolio).

What exactly to change: plays, each one specific change on the site or elsewhere on the web with its evidence from the lenses, an expected gain with its basis, an effort, a how-to and draft (never sent by Vercatus), and an acceptance check a machine runs; their checks, activity and hand-offs; and “This is wrong” on a play, a topic or an AI demand figure.

Method Path Role What it does
GET /sites/:id/plays viewer The site’s plays, best first (by rank: expected gain × confidence ÷ effort; plays with no gain below), ?status= (one of proposed, planned, in_progress, shipped, measuring, dismissed), ?type= and ?limit= (1–500, 200 by default), ?evidence=1 (also the proposals the plan keeps as evidence or for the developer). Without a status, dismissed plays and stale proposals are left out of the list. counts holds every play that is not a stale proposal, by status and by type.
POST /sites/:id/plays/generate analyst Regenerates the site’s plays now from the lenses, the briefs and the findings; a person’s status, owner and due are kept. Answers with what changed. Analyst role; audited.
GET /sites/:id/plays/:playId viewer One play: its row; its evidence read fresh (its topics with today’s shares and leaders, the source pages with the brands they name, its searches with the site’s and the leader’s ranks in Google, Bing and Brave, what the engines say, and the site audit’s findings with their recommendation, evidence and how-to); its guide (null until written); its check history and its activity.
PATCH /sites/:id/plays/:playId analyst Changes a play, { status?, ownerId?, due?, dismissedReason?, checkUrl? }: a person sets proposed, planned, in_progress or dismissed (shipped comes from a passing check); the owner is a member of the workspace; due is a day; checkUrl is the page on the site that holds the change, which the page_answers check fetches. A play on a topic the site’s experiment holds out cannot be planned or started (400). Or, on its own, { ship: "done" | "confirm" | "reject" }: a person’s word on a ship. Done marks a play still to do as done now (PLAYS-UX-PLAN.md D6): it ships by that person and the check goes on reading the site. Confirm records who says they did it, and ships a play whose check asked “Did you do this?”; reject (“I did not”) un-ships a shipped play (back to the status it had, out of the verdicts and the Results chart) or stops the question, and that evidence never ships the play again; 409 when there is nothing to confirm or reject. Moving a shipped play back to another status by hand un-ships it the same way. Analyst role; audited.
POST /sites/:id/plays/:playId/guide/:part analyst Writes one half of the play’s guide, only when someone asks (opening a play writes nothing): …/guide/steps the numbered steps, …/guide/draft the short draft (an outreach note, a page outline, JSON-LD, copy), each from the play’s own evidence, in the language of the play’s market. A second press writes it again. Never invents claims, reviews or customers; nothing is sent by Vercatus. Analyst role; audited. The answer is { guide } with both halves as they now stand. A play whose draft is a page to write is written by …/article, not here (409).
POST /sites/:id/plays/:playId/article analyst Writes a page for the play now, only when someone asks (opening a play writes nothing): for a play whose draft is a page (a topic to answer, a search to rank for). The content engine researches the topic’s questions, the pages engines cite, the site’s own pages and facts, then writes a brief and the page in the market’s language with its meta tags, FAQ and JSON-LD, checks it (language, figures, addresses, structure) and turns anything the facts do not hold into an [ADD: …] for the author. Uses one of the plan’s monthly articles (402 with the reason when none is left); 409 while one is being written for the play; 422 for a play whose draft is a short note. Answers 201 with { article, allowance }, or 202 with { article: { id, status: "pending" } } when it takes longer than the request waits: the play then holds it. Analyst role; audited. Vercatus never publishes the page.
GET /sites/:id/plays/:playId/article viewer One version of the play’s page (?version= the number the play lists; the latest without it), as the app shows it. 404 when there is none.
POST /sites/:id/plays/:playId/guide analyst Writes both halves at once (the steps and the draft), when asked; the app asks for each half on its own. Analyst role.
POST /sites/:id/plays/:playId/check analyst Runs the play’s acceptance check now and answers with the result. A passing check ships a planned or in-progress play on strong evidence new since it started; otherwise the play asks “Did you do this?” (play.check.ask). Analyst role.
POST /sites/:id/plays/:playId/handoff analyst Hands the play over, { tool, context? }: claude, codex, llm or markdown answer { tool, kind: "brief", context, markdown, words }, the play as a lean task (the goal, where, what exactly, done when and only the constraints the task needs) or, with context: "full", with its background (the business, why, the topics, the evidence, every step and draft) for a reader without the project; linear, jira, github or asana open an issue in the workspace’s tracker with the lean task as its body and answer { tool, kind: "issue", ref } (kept on the play: a second press returns the same issue). Recorded in the play’s activity. Analyst role; audited.
POST /sites/:id/plays/handoff analyst { ids, tool, context?, note?, preview? }: hands several of the site’s plays over at once (NH-11): 1 to 25 play ids of this site (404 when one is not), a tool as for one play, lean or full, an optional one-line conventions note for the header. claude, codex, llm or markdown answer { tool, kind: "brief", context, markdown, count, words, sites }: one document with the site’s header and the rules once, the tasks numbered and grouped by page; preview: true builds it without recording a hand-off (text tools only). linear, jira, github or asana open one issue per play (each kept on its play) and answer { tool, kind: "issues", count, issues } with each issue or why it failed (502 when every one failed). A “handoff” event is recorded on each play handed. Analyst role; audited.
POST /plays/handoff analyst { ids, tool, context?, note?, preview? }: the batch hand-off across the workspace (NH-11): plays from any of the sites the caller can see (404 when one is not), the document grouped by site and then by page. Answers as the site’s batch hand-off. Analyst role; audited.
GET /plays viewer The workspace’s top open plays across the sites the caller can see, best first, ?limit= (1–100, 10 by default), each with its site.
GET /plays/counts viewer The open plays per site the caller can see and in all (open), and of them those aimed at measured topics (onTopics).
GET /plays/by-finding/:findingId viewer The play that holds a finding (by the finding’s id, from any run): { playId, siteId }, for the old finding links. 404 when no play holds it.
POST /sites/:id/quality-reports analyst { kind, targetId, reason, note?, shown? }: says a play, a topic or an AI demand figure is wrong. kind is play, topic or demand (a demand figure is named by its topic’s id); reason is one of the kind’s reasons (a play: not_my_market, no_stake, already_done, cannot_be_done, wrong_evidence, duplicate, other; a topic: not_my_business, wrong_market, another_meaning, other; a figure: too_high, too_low, not_believable, other); note at most 500 characters; shown the figure as it was shown. A play is dismissed for good with the reason (a shipped one un-shipped first), except “already done”, which confirms the ship (or, when no check has seen the change, closes the play as done without counting it shipped); a topic is paused; a figure is recorded. Answers 201 { report, outcome, message, undoUntil }. Analyst role; audited.
DELETE /sites/:id/quality-reports/:reportId analyst Undoes a report its maker filed in the last two minutes: the play or topic goes back as it was and the report is withdrawn. 404 for anyone else’s; 409 once too old or resolved. Analyst role; audited.

Why AI picks a site’s competitors, per topic and per competitor: the pages engines read and whom they name, the engines’ own searches in Google and Bing, what engines name without searching, the reasons from those numbers, and what each engine says about the site (Engine Briefs, perception, not cause).

Method Path Role What it does
GET /sites/:id/competitors viewer The site’s competitors, ?days= (28 by default) and ?market= (one market’s topics and their answers, such as hu-HU), by Answer Share: each with its change, the topics where it leads the site (largest gap first), the read pages naming it and of them those not naming the site, in how many of the engines’ checked searches it ranks, and its share without search.
GET /sites/:id/topics/:topicId/why viewer Why a topic’s leader is chosen, ?days= (28 by default): the leader and the site with their Answer Share; the Sources lens (the third-party pages the topic’s answers cited, how many could be read, each page with the brands it names and whether it names the site and the leader, the gap pages that name a competitor and not the site, and on how many read pages each brand appears); the Search lens (the engines’ own most frequent searches with the site’s and the leader’s best rank in Google’s and Bing’s top 10, and the engines whose index is not checked); the Memory lens (answers without search, per engine); and the reasons as plain sentences from those numbers, strongest first.
GET /sites/:id/sources viewer The pages to be on across the site’s topics, ?days= (28 by default): every third-party page its answers cited, gaps (pages naming a competitor and not the site) first, then by answers citing it × competitors named, each with its topics, engines and status; and the domains those pages sit on.
GET /sites/:id/competitors/:brandKey viewer One competitor beside the site, ?days= (28 by default): its row, both shares per topic, the pages naming it, its rank in each checked search, its share without search per engine, what each engine’s brief says about it, and recent changes (pages that newly name it or dropped it, clear share moves).
GET /sites/:id/briefs viewer The Engine Briefs: per engine, what it says about the site (how well it knows it, whether it would recommend it, a paragraph in its own terms, the facts it gives, its reasons for and against, what would change its mind with the lenses that back each item, and what moved since the previous brief), and when the next round is due. What the engine says, not what moves its answers.
POST /sites/:id/briefs/refresh analyst Asks every engine the brief probes again now, in the background (about 50 calls), and makes new summaries. Answers at once with { started: true }; a round already running for the site is not started twice. Analyst role; audited. Each round is paid for by Vercatus, so the press is bounded like every other paid action (LAUNCH-READINESS LR-001): the workspace needs a plan that measures the site (402), the domain must be verified where runs need it (403), a site is refreshed by hand once a day at most (429; the weekly round is the product, this is for “I changed something”), and at most three presses a day are counted, so a round that failed and left no summary cannot be looped either.
GET /sites/:id/briefs/:engine/probes viewer The raw probes behind an engine’s latest brief: each question, whether it was asked with search or without, the answer, what it cited and what the answer was read to say.

Where a site stands in the AI answers of its topics: Answer Share with its range, the leaderboard, each engine and topic, the topics and their questions, the brands counted and the answers behind every figure.

Method Path Role What it does
GET /sites/:id/standing viewer The site’s Answer Share, ?days= (28 by default, at most 365) and ?market=: of the AI answers on the site’s topics in the window, the share that name or cite the site, with its 95% range and the change against the previous window (clear only when its range excludes zero); the leaderboard of the top brands on one scale, each engine’s share and leader, every topic’s share, leader and gap, the clear moves, a rolling 28-day trend, and the setup line (topics, the plan’s poll asks, the answers recorded this period, the first answer). Unmeasured shares are null, never 0. Each topic carries its strongest reason from the Why lenses (reason, null when it has none); next holds the site’s top three open plays (PHASE-3.md §5), so the Overview needs no second call; wins how many wins (topics a Won verdict moved, PHASE-4.md §5) fell in the window. ?reasons=0 leaves the reasons out (every reason null), for screens that do not show them: the rest is the same and comes without reading the Why lenses.
GET /sites/:id/standing/reasons viewer Each topic’s strongest reason from the Why lenses, ?days= (28 by default): { reasons: { [topicId]: reason | null } }, the same reasons the standing carries, apart from its figures, so a screen can show the figures (?reasons=0) first.
GET /sites/:id/topics viewer The site’s topics that are not retired, oldest first: each with its market, stage, demand tier and its source, the site’s share, the leader, the gap, the change over the last 28 days and how many active questions it has.
POST /sites/:id/topics analyst Adds a topic of the workspace’s own, { name, phrasings? }: asked in the site’s first market, with up to ten phrasings as its questions (the name itself when none is given), tiered “medium” by hand until changed. Answers with the topic’s row as the list shows it. Refused when the plan’s topics are all active. Analyst role; audited.
GET /sites/:id/topics/refresh viewer When the site’s topics were last built and when the scheduled refresh builds them next (DQ-100: a publisher’s every week, other sites’ every month): { siteType, cadenceDays, refreshedAt, nextAt, enabled, last, retiredTopics }, last the latest build’s counts (topics added, retired and kept as they were, questions retired), nextAt null when the schedule is held. Registered before the one-topic route so “refresh” is not read as a topic id.
POST /sites/:id/topics/refresh analyst Refreshes the site’s topics now, as the schedule would (DQ-100): a topic named again keeps its id and the questions still asked, at most a share of the built topics is retired (never one of the workspace’s own), and new ones fill the plan. Answers at once with { started: true }; the refresh runs in the background. Analyst role; audited.
GET /sites/:id/topics/:topicId viewer One topic, ?days= (28 by default): who is recommended (each brand’s share overall and per engine), its active questions with where each came from, its latest 40 answers with the brands each names and the sources it cites, the searches the engines ran for it (most frequent first) and the domains they cited with the brands those answers named.
PATCH /sites/:id/topics/:topicId analyst Changes a topic, { status?, demandTier?, name? }: pause it (the poll skips it), retire it or make it active again; set its demand tier by hand (its source becomes “manual”); rename it. Making a topic active again needs room on the plan. Analyst role; audited.
POST /sites/:id/topics/rebuild analyst Builds the site’s topics again from its profile, markets, admitted searches, prompts and the engines’ searches; a topic named again keeps its id and history, one dropped is retired. Answers at once with { started: true }; the build runs in the background (a build already running is not started twice). Analyst role; audited.
POST /sites/:id/topics/:topicId/questions analyst Adds a question to a topic, { text, exact? }: asked by the poll like the topic’s own phrasings, in the topic’s market. exact: track it word for word (never rephrased, retired or moved by a topic build, asked on every engine at least weekly, its answers’ changes told), within the plan’s allowance (402 beyond it). A question the site already has moves to this topic. Analyst role; audited.
PATCH /sites/:id/topics/:topicId/questions/:questionId analyst { exact }: tracks a question of the topic word for word, or stops (FEATURE-GAP-PLAN.md item 10, the tracked prompts’ successor): never rephrased, retired or moved by a topic build, asked on every engine at least weekly, its answers’ changes told. Within the plan’s allowance (402 beyond it). Analyst role; audited.
PATCH /sites/:id/brands/:brandKey analyst Changes how a brand counts on the site, { status, kind? }: pin it, hide it (off every leaderboard and list) or let the answers decide again (auto); kind marks it a competitor or another brand. The site’s own brand cannot be hidden or changed in kind. Analyst role; audited.
POST /sites/:id/brands analyst Names a competitor, { name, domain?, kind: "competitor" }: the brand is created as a pinned competitor, or an existing one (a brand the answers brought) is pinned and made a competitor, keeping its counts; the domain, when given, joins its domains (so a cited page on it counts it as cited) and the site’s named competitor domains while there is room for them (five). Answers 201 { brand } when it is new, 200 { brand } when it was there. The site’s own brand or domain is refused. Analyst role; audited.
GET /sites/:id/answers/:sampleId viewer One answer of the site’s poll, for the evidence drawer: the full text, the engine and surface it came from, the question and topic, every source in order (cited first), the brands it names or cites and the searches the engine ran.

Verified domains, single sign-on (OIDC and SAML) with enforcement, and the data processing agreement.

Method Path Role What it does
POST /sso/lookup public Email-first sign-in: { email } → { sso, url?, enforced? }. sso is true when the address’s verified domain belongs to a workspace with an enabled connection; url starts that sign-in; enforced means password sign-in is refused for it.
GET /sso/:connectionId/start public Start a sign-in through a workspace’s connection (OIDC or SAML); ?email= hints the IdP, ?next= is the app path to return to. With ?test=1 a signed-in admin of that workspace tests the connection: the claims are shown and nobody is signed in.
GET /sso/oidc/callback public The OpenID Connect return for workspace connections and for “Continue with Google / Microsoft”: state, PKCE and nonce are checked against this browser’s cookie, then the person is signed in.
POST /sso/saml/:connectionId/acs public SAML assertion consumer service (HTTP-POST binding): the response must be signed by the connection’s certificate (or its second certificate during a rotation) and addressed to this URL. It answers the request this browser started, or, where the workspace allows sign-ins started at the IdP, no request at all (then it must be fresh and is used once).
GET /sso/saml/:connectionId/metadata public Our SAML service-provider metadata for a connection: entity id, assertion consumer service URL, signed assertions wanted. Give it to the IdP.
GET /security admin (no keys) Settings › Security: what the plan includes, the verified and pending domains with their DNS records, the connections (never secrets) with the details to give the IdP, enforcement and session length, and seats. Admins.
POST /security/domains owner (no keys) { domain }: claim a domain for single sign-on. The answer carries the DNS TXT record (vercatus-domain=<token> at the domain) to publish before verifying. A domain verified by another workspace is refused. Owners (a verified domain decides whose addresses a connection may sign in); plans with single sign-on.
POST /security/domains/:id/verify owner (no keys) Check the domain’s DNS TXT record now; on success the domain is verified for this workspace. Owners.
DELETE /security/domains/:id owner (no keys) Remove a domain; it stops routing sign-ins and is taken out of enforcement. Owners.
POST /security/connections owner (no keys) Create a connection: { protocol: "oidc", provider: google|microsoft|okta|generic, issuer, clientId, clientSecret, label?, defaultRole?, jit? } or { protocol: "saml", entryPoint, idpCert, idpIssuer?, label?, defaultRole?, jit? }. It starts disabled; test it, then enable it. Owners (a connection lets its identity provider sign people in to the workspace, admins and owners among them); OIDC needs single sign-on in the plan, SAML the Enterprise plan.
PATCH /security/connections/:id owner (no keys) Change a connection; any field of the create call, plus enabled. Enabling needs a verified domain and one successful test; the secret is kept unless a new one is sent. Turning off the last enabled connection lifts enforcement. Owners.
DELETE /security/connections/:id owner (no keys) Delete a connection. When it was the last enabled one, enforcement is lifted so nobody is locked out. Owners.
POST /security/connections/:id/bindings owner (no keys) { email }: confirm that this connection’s identity provider speaks for a member who is an owner or admin of the workspace, so that person can sign in through it (the first sign-in of an existing owner or admin through a connection is otherwise refused: ssoIdentityBound in sso.ts says why). An owner can bind themselves by running “Test sign-in” on the connection; this is for the admins. Owners.
PUT /security/settings owner (no keys) { enforceSsoDomains?, sessionHours?, idleMinutes?, requireMfa?, ipAllowlist? }, owners: - enforceSsoDomains: refuse password sign-in and registration for these verified domains (their people sign in through the workspace’s connection; API keys are unaffected). Needs an enabled connection that has signed someone in; Enterprise. Newly enforced domains end their people’s sessions that did not come through this workspace’s SSO. - sessionHours: cap session length in hours (1 to 720; null for the 30-day default). idleMinutes: end a session after this many minutes without a request (5 to 1440; null for none). Both apply to sessions already open. - requireMfa: everyone who signs in without this workspace’s own SSO must set up an authenticator app. - ipAllowlist: IPv4/IPv6 addresses or CIDR ranges (at most 100; [] to allow every address); sessions and API keys from elsewhere are refused. A list that would shut out the caller’s own address is refused. Enterprise.
GET /security/dpa admin (no keys) The data processing agreement: whether this workspace can sign (plan and signable version), the current version, and every agreement signed here with its state and download links. Admins.
GET /security/dpa/text admin (no keys) The text of the approved agreement, as Markdown, so it can be read before signing; 404 while no version is approved. The reviewer’s notes at the top of the file are not part of it. Admins.
POST /security/dpa/sign owner (no keys) { entity, address, country, registration?, signerName, signerTitle, signerEmail, signature }: sign the approved version for this workspace. signature is the signer’s name typed again. The time and address are recorded, the PDF stored and emailed; an earlier agreement becomes superseded. Owners, signed in.
GET /security/dpa/:id/pdf admin (no keys) Download a signed agreement as PDF; ?copy=countersigned for the countersigned copy. Admins.
GET /security/provisioning admin (no keys) SCIM: the base URL for the IdP, the workspace’s tokens (never the secret), and the group-to-role mappings. Admins.
POST /security/provisioning/tokens owner (no keys) { name }: a new SCIM token; the answer carries it once. Owners (a token lets an identity provider create and remove people, so it is not an admin’s to hand out); Enterprise.
DELETE /security/provisioning/tokens/:id admin (no keys) Revoke a SCIM token; the IdP’s next call with it is refused. Admins (revoking is always safe).
POST /security/role-mappings owner (no keys) { group, role, siteIds? }: members of this IdP group (its name or id, as SCIM or the sign-in’s groups claim carries it) get this role (admin, analyst, viewer or client) and, when siteIds names some, see only those sites (a client mapping must name them; admins always see every site). Replaces an existing mapping of the same group. Applies at the next SSO join and to everyone SCIM provisioned. Owners (a mapping decides who becomes an admin).
DELETE /security/role-mappings/:id owner (no keys) Remove a mapping; SCIM-provisioned members are re-evaluated. Owners.

Domains, ownership verification and schedules.

Method Path Role What it does
GET /org/sites viewer sites & domain verification
POST /sites analyst { url }: add a domain to the workspace, or return the one it has. Returns the site with its verification token. 400 for an address that is not a public domain (an IP, localhost, a local-network name); 402 at the plan’s site cap. When the caller verified the same host in another workspace where they are an owner or admin, the site is verified at once (method workspace) and verifiedThrough names that workspace; on the free report that starts its run (freeRunId). Query: ?tz= (the browser’s IANA time zone, for the default schedule).
GET /sites viewer Sites in the workspace with run counts and the last run.
GET /sites/:id/verification viewer The verification instructions for a site, read only. method is how it was verified (dns, meta, search_console, or null for a site verified before methods were recorded); searchConsole says whether a connection exists to verify with; required whether runs wait for verification on this instance. Every site gets its token when it is added; one that predates tokens answers token: null, tokenMissing: true and gets one from POST /api/sites/:id/verification (a GET never writes).
POST /sites/:id/verification analyst Give a site that has no verification token one (idempotent: a site that has one keeps it) and answer as GET does. Analysts and up.
GET /sites/:id/citations viewer Everything the answer engines said about this site, across runs: one entry per question per run with each engine’s answer and every source it rested on, with the source titles, plus tallies by cited domain, by topic and by language. /sites/:id/share is the headline figure over the same data. ?runs= caps the history at 12 runs, 24 maximum.
GET /sites/:id/share viewer Cited share for a site and each named competitor, run by run and engine by engine, with the questions a competitor is cited for and the site is not. ?from=&to= (YYYY-MM-DD, to inclusive) reads every run in the range (at most 400 points); without them ?runs= caps the history (12, 24 at most). ?engines=a,b narrows every figure to those engines; ?series=panel (the default once the site has a tracked panel) is the chain-linked index over its tracked questions with each cohort’s share, ?series=matched rests each run on the cells it shares with the run before, and ?series=all returns each run’s every answer.
GET /sites/:id/questions viewer The site’s living question set: what the latest full run newly asked and whether the site answers it, what stopped being asked, which answers moved, the searches the set does not carry yet, and what the same month asked a year ago.
GET /sites/:id/templates viewer Template health over time: for each template in the site’s latest run, pages sampled, AI readability, JavaScript-only text and answered questions across the last runs (?runs=, 8), with templates first seen in the latest run marked new.
PUT /sites/:id/competitors admin (no keys) { competitors: string[] }: name the competitors share of answer is measured against; domains, at most five, never the site itself.
GET /sites/:id/watch viewer The daily access watch for a site: whether it is on, the URLs it reads, the latest matrix, the last thirty checks and every change with its date.
PATCH /sites/:id/watch admin (no keys) { enabled?, urls? }: switch the watch on or off for this site, or pin the URLs it reads (at most twelve; null returns to the automatic choice).
POST /sites/:id/watch/check analyst Run the access check now; answers with the check and what moved since the previous one. Needs a verified domain where verification is required (403 verify_first): the check is about 130 requests to the site.
POST /sites/:id/verify analyst { method? }: check ownership now. By default via the DNS record or meta tag, which also upgrades a site verified through Search Console; with { "method": "search_console" }, via the site’s Search Console connection: every property its account can see is checked, and an owner or full user of the Domain property (or of the exact URL-prefix property) verifies the site. Says what was found when it fails; 400 when Search Console is not connected. On the free report, a verified domain starts the run and freeRunId names it.
GET /sites/:id/schedule viewer The site’s run rhythm. PUT replaces it; null turns scheduling off. Query: ?tz= (the browser’s IANA time zone, for the default schedule).
PUT /sites/:id/schedule analyst { timezone, monthlyFull: { day, hour }, weeklyLight: { weekday, hour }, paused }: set the site’s schedule; returns the next planned runs.
GET /sites/:id/history viewer Runs of one site with findings by severity per run and what appeared or resolved between consecutive runs. Query: ?cursor= (the x-next-cursor of the previous page), ?limit= (rows per page).
GET /sites/:id/search viewer Search Console for a site: connection state (never the token), totals, top queries (each with its class, flags and the page it landed on most) and pages from the last sync.
PUT /sites/:id/search analyst { property }: choose or change the Search Console property for a connected site, from the ones the account can read that cover its host; syncs at once.
DELETE /sites/:id/search admin (no keys) Disconnect Search Console: the token and everything read through it or derived from it are deleted at once, and a verification that rested on the connection is withdrawn (DNS and meta-tag verification stay).
GET /sites/:id/traffic viewer Crawler traffic for a site (OUTCOMES-PLAN.md A.6): the connection (token shown to admins as the hook URL and the Worker snippet), and the summary over 90 days.
DELETE /sites/:id/traffic admin (no keys) Disconnect: the token stops working and the stored aggregates go.
POST /sites/:id/traffic/connect analyst Turn traffic on for a site: a token, the hook URL and the Worker to deploy.
GET /sites/:id/traffic/worker analyst The Worker as a file to paste. Query: ?download= (1 sends it as a file).
POST /sites/:id/traffic/upload admin (no keys) A log file for a site not on Cloudflare (OUTCOMES-PLAN.md A.5): the raw body, up to 200 MB, plain or gzipped, in the combined or common access-log format, JSON lines, or a Vercel or Netlify drain export; read as it arrives, never held whole. The answer says how many lines, how many crawler hits, how many browsers dropped and how many lines were unreadable.
GET /sites/:id/traffic/observed viewer The crawlers’ own requests over the last seven days, per crawler, for the probe surfaces to show beside their cells; null until traffic is connected and something arrived.
POST /sites/:id/traffic/test analyst Test the connection: the app requests a page of the site as a bot and waits for the Worker to forward that request back.
GET /sites/:id/traffic/instructions analyst The setup as a document for IT or an agency: steps, the Worker file, the route, how to check; ?download=1 for a .md file.
GET /sites/:id/outcomes viewer Outcomes from Google Analytics for a site: connection state (never the token), the last 28 days against the 28 before by assistant and by landing page, the daily series.
PUT /sites/:id/outcomes analyst { property }: choose or change the Analytics property for a connected site, from the ones the account can read; syncs at once.
DELETE /sites/:id/outcomes admin (no keys) Disconnect Google Analytics from a site: the token and the rows are removed.
POST /sites/:id/outcomes/sync analyst Sync the site’s Analytics now (the last three days and the next slices of the backfill).
POST /sites/:id/search/sync analyst Pull the last 28 days from Search Console now; verification says whether the account’s access verified the site or withdrew its Search Console verification.
GET /sites/:id/changes viewer Change events for a site (newest first) and the hook settings, never the secrets. Query: ?limit= (rows per page).
PUT /sites/:id/cms admin (no keys) Enable or change the CMS hook: { enable?, rotate?, shopifySecret?, sanitySecret?, contentfulSecret?, urlTemplate?, markOpen?, autoRun? }. An empty secret keeps the stored one; null removes it; enable: false removes the hook.
GET /sites/:id/pages/cited viewer ?days= (90, at most 365): which of the site’s pages real answers cited: per page path, the asks whose answer cited it, the questions and the engines, and every ask with an answer in the window.
GET /sites/:id/brand viewer How answers name the site: the stored brand (null when derived) and what the matcher uses now (effective).
PUT /sites/:id/brand analyst { names?, aliases?, exclude? } sets how answers name the site from the next run on; null (or all empty) goes back to the derived brand.
GET /sites/:id/sov viewer Share of voice per engine for the site and each competitor, from every ask in the period: citation share weighted 1/k (k the domains an answer cited), mention share among the brands named, first-position share, recommendation rate, net sentiment (−1 to 1), and for results-page engines how often the AI answer was shown; each with its 95% interval and its numerator and denominator. ?from=&to= (dates; the last 90 days by default) and ?engines=.
GET /sites/:id/estimates viewer The estimates of the site’s visibility, oldest first: per engine (and all, the fixed-weight aggregate) and per brand (the site and each competitor), cited and named, each with its 95% interval, the questions and asks behind it, the probability that it fell against the period before, and the CUSUM alarm. ?kind=run (per run, the default) or week, ?from=&to=, ?entity= (a domain) and ?engine=.
GET /sites/:id/tracked-questions viewer The questions the site’s trend is measured on: the active ones by weight (with the cohort each joined with, its source, its Search Console impressions, who it is for and where it came from), the retired ones with who retired them and why, the latest versions of the set, and the rule that retires a question on its own.
POST /sites/:id/tracked-questions analyst { text, locale? }: track a question from the next run on (this month’s cohort; the site’s first market when no locale is given). A retired question with the same words comes back instead.
POST /sites/:id/tracked-questions/:qid/retire analyst { reason }: retire a question from the panel; its history stays and it can be restored.
POST /sites/:id/tracked-questions/:qid/restore analyst Bring a retired question back into the panel.
GET /sites/:id/competitor-brands viewer How answers name each competitor: the stored names, aliases and excluded phrases per competitor domain, and what the matcher uses now (effective).
PUT /sites/:id/competitor-brands analyst { brands }: { [competitor domain]: { names?, aliases?, exclude? } } for the domains on the site’s competitor list; answers are matched with them from the next ask on (and history on the next rescoring).
GET /sites/:id/type viewer What kind of site it is (DATA-INTEGRITY-QA.md decision 5): { type: "shop" | "publisher" | "local" | "software" | "other" | null, source: "owner" | "classified" | null, classified: { type, why }, options, commerceConfigured, showsCommerce }. The owner’s choice wins; without one the classifier decides from the site’s pages and its domain’s vertical, and its answer is kept on the site. The kind chooses the plays, the topic mix of the next build, and whether Commerce & compliance shows.
PUT /sites/:id/type analyst { type: "shop" | "publisher" | "local" | "software" | "other" } sets the kind by hand; { type: null } returns it to the classifier. The plays follow at their next generation and the topics at their next build; topics already measured are not rebuilt. Answers as GET. Analyst role; audited.
GET /sites/:id/company viewer The company profile: { kind, position, selling, capacity, namesCompetitors, why, source, at }, each field inferred (the site’s pages read once by a small model, the leaderboard, the workspace’s members) or set by the owner, whose answer wins. It sets the topic mix of the next build (comparison topics only for a challenger that names its competitors) and the plays. The first read infers it.
PUT /sites/:id/company analyst The owner’s answers: { kind?, position?, selling?, capacity?, namesCompetitors?, fixed? }, a value sets the field, null gives it back to the inference; fixed is the sections the plays leave as they are (path prefixes such as “/knowledge”, an empty list for none): pages that hold evidence are cited, never rewritten. The topics and plays follow at their next build. Answers as GET. Analyst role; audited.
GET /sites/:id/markets viewer A site’s markets: the ones in use (set by hand, or detected), what detection found and why, and the countries and languages to choose from.
PUT /sites/:id/markets admin (no keys) { mode: "auto" } returns to detection; { mode: "manual", markets: [{ country (ISO code or null for global), language }] } sets them by hand (one to six). The next run writes its questions for them.
DELETE /sites/:id admin (no keys) { confirm } with the site’s host typed back: remove the site with its runs, pages, findings, answers, prompts, goals and signals. Admins. Refused (409) while one of its runs is going. The runs still count against this month’s allowance if the host is added again. The billed site count follows.
GET /sites/:id/demand viewer ?days= (7 to 90, default 28): live demand from assistant fetches: per assistant and page, the questions the pages answer ranked by fetches, with the change on the period before.
GET /sites/:id/funnel viewer ?engines=: the visibility funnel per engine over the latest full run’s pages (access, indexed, retrieved, cited, clicked, converted), with the pages that leak at each stage.
POST /sites/:id/funnel/inspect analyst { source? } (“google” or “bing”): check up to 25 of the site’s pages in the search engine’s index now, within the day’s ration; the daily pass does the rest.
GET /sites/:id/bing viewer The site’s Bing Webmaster Tools connection (never the key).
PUT /sites/:id/bing admin (no keys) { apiKey }: connect Bing Webmaster Tools with the workspace’s own API key; the key is checked against the site’s host in Bing and stored sealed.
DELETE /sites/:id/bing admin (no keys) Disconnect Bing Webmaster Tools: the key and the stored Bing index checks are removed.
GET /sites/:id/ledger viewer ?days= (14 to 365, default 120): the crawl-to-citation ledger: each changed page with its first refetch per search crawler, first retrieval, first citation per engine and first AI visit; latencies per engine and page type; the slowing check on the money templates.
GET /sites/:id/economics viewer ?days= (7 to 90, default 30): crawl economics per crawler and page type (requests, bytes, errors, AI visits and key events per 1,000 requests) and the what-if’s inputs.
GET /sites/:id/economics/what-if viewer ?block=&charge=&price=&days=: the policy what-if: crawler ids to block and to charge (Pay Per Crawl, price in USD a request); the predicted change in cited asks, AI visits and key events per engine, with its range and method, and the robots.txt and Cloudflare rules.
GET /sites/:id/commerce viewer Agentic-commerce reconciliation: the settings (feed, ACP endpoint, tolerance), the latest check (feed against Offer against the JS-off price against quoted prices, per product page; the UCP, ACP and WebMCP checks), the recent checks’ counts, the method and its limits.
PUT /sites/:id/commerce/source admin (no keys) { feedUrl?, feedFormat?, acpEndpoint?, tolerance? }: the product feed (public HTTPS: Merchant XML or TSV, ACP or UCP JSON; auto detects), the declared ACP checkout endpoint, and the share two prices may differ by and still agree (0 to 0.1).
POST /sites/:id/commerce/check analyst Reconcile now: fetch the feed, compare the latest run’s product pages, probe the declared protocols and render a few pages for WebMCP. At most one check every five minutes a site.
GET /sites/:id/disclosures viewer Disclosure reachability: the rules, the latest check per rule and page (present in each view: JS-off, rendered, each crawler) with the answers that carried it, per page type, and the recent checks’ losses.
POST /sites/:id/disclosures/rules analyst { name, scope, scopeRef?, match, pattern, answerPattern? }: a required disclosure: words or a regular expression (checked for catastrophic backtracking), for the site, a URL pattern or a path glob; answerPattern is the short form an answer should carry.
PATCH /sites/:id/disclosures/rules/:ruleId analyst Change a disclosure rule: any of its fields, or active.
DELETE /sites/:id/disclosures/rules/:ruleId analyst Remove a disclosure rule; its past checks stay.
POST /sites/:id/disclosures/check analyst { live?, ruleId? }: check now, against the latest run’s stored bodies, or (live) by fetching up to ten of the covered pages from every view now.
GET /sites/:id/agent-tasks viewer Agent task trials: the tasks with their latest trial per identity, weekly success, the assistants’ observed fetches of the same page types, success and blockers per page type, the identities, the method and its limits.
POST /sites/:id/agent-tasks analyst { kind, name, startUrl, target?, identities?, maxSteps?, weekly? }: a scripted task (find_price, find_plans, reach_contact, start_trial, add_to_cart) starting on a page of the site; a product target for find_price and add_to_cart.
PATCH /sites/:id/agent-tasks/:taskId analyst Change an agent task: its name, start page, target, identities, step limit, weekly run or active.
DELETE /sites/:id/agent-tasks/:taskId analyst Remove an agent task and its trials.
POST /sites/:id/agent-tasks/:taskId/run analyst Run a task now under each of its identities; the worker takes it within a minute and the trials appear as they finish.
GET /sites/:id/agent-tasks/:taskId/trials viewer ?limit= (1 to 100, default 30): a task’s trials, newest first, with each step taken.
GET /sites/:id/stale-answers viewer ?status= (open, resolved, dismissed or all; default all): the site’s stale answers, open and most severe first, each with its sentence, cause and fixes; the last scan; the IndexNow state.
POST /sites/:id/stale-answers/scan analyst Scan the site’s latest answers for stale facts now, with what is left of the day’s model reads; answers with the counts. The worker scans every site with new answers anyway.
PATCH /sites/:id/stale-answers/:staleId analyst { status } (“dismissed”: not a problem, or “open” again): a person’s decision on one stale answer.
POST /sites/:id/stale-answers/:staleId/indexnow analyst Submit the stale answer’s page to IndexNow (the site’s key must be set up and checked); recorded with what IndexNow answered.
GET /sites/:id/indexnow viewer The site’s IndexNow key (public by design: it is served on the site), whether its file was found, and the last submissions.
DELETE /sites/:id/indexnow admin (no keys) Remove the site’s IndexNow key (its submissions stay in the record).
POST /sites/:id/indexnow/key admin (no keys) { keyLocation? }: make (or replace) the site’s IndexNow key, to be served at its default address or at keyLocation (an https .txt address on the site’s host).
POST /sites/:id/indexnow/verify analyst Read the key file on the site now (through the egress guard) and record whether it holds the key.
POST /sites/:id/indexnow/submit analyst { urls }: submit up to 10,000 of the site’s own URLs to IndexNow; URLs sent within the hour are left out.
GET /sites/:id/provenance viewer ?engine=&limit=: the site’s answers with alignments, newest first: per answer, how many of its sentences came from the site’s passages, a competitor’s, another site’s, or none.
GET /sites/:id/provenance/answers/:sampleId viewer One answer sentence by sentence, each with the passage it most likely came from (or none).
POST /sites/:id/provenance/answers/:sampleId analyst Align one answer now (the worker aligns every new answer on its own): the cited pages of other sites are fetched, robots.txt honoured.
GET /sites/:id/provenance/page viewer ?url=&days=: one page as answers use it: its passages answers quoted, the passages of other sites that won its questions, and the churn around its latest change.
GET /sites/:id/market-parity viewer ?days=&engines=: the same question across the site’s markets, per engine: which market’s answer cites the site, with the right page, and why not.
GET /sites/:id/move admin (no keys) Where this site can move: every other workspace the caller administers (their own as owner or admin, and an agency’s client workspaces), each with its plan’s sites used and left (a consolidated agency group counts as one, so a move inside it takes no room) and why it cannot take the site, if it cannot; plus whether a run is in progress. Owners and admins, signed in.
POST /sites/:id/move admin (no keys) { targetOrgId, confirm } with the site’s host typed back: move the site, with everything measured on it and its verification, to another workspace the caller administers. 409 while one of its runs is queued or running, when the other workspace already has the host, or when either workspace is closed; 402 at the other plan’s site cap; 403 when the caller does not administer the other workspace. Both audit logs record it (site.move.out, site.move.in), both workspaces get a notice, and both billed site counts follow.
POST /sites/:id/surface-reports analyst Uploads one export for a site, { source, csv, from?, to? }: source is gsc_ai (Search Console’s generative AI report, impressions) or bing_ai (Bing Webmaster Tools AI Performance, citations); csv is the file’s text, at most 5 MB and 50,000 rows. A table without a date column needs from and to, the dates the export covered. Rows of the same source, breakdown and dates are replaced. Answers the kind of table read, its date range, how many rows and what was replaced, or 400 with errors saying what could not be read. Analyst role; audited.
GET /sites/:id/surface-reports viewer The exports uploaded for a site, newest first: which report, the dates it covers, how many rows, who uploaded it and when.
GET /sites/:id/connections viewer The site’s Google connections that lost their grant (broken: product ga4 or gsc, its label and what stops), so a screen can say “reconnect” where the data would be, instead of an empty strip. Empty while every connection works or none is made; a passing sync failure is not listed.
GET /sites/:id/about viewer What Vercatus has understood about the business, which the topics are built from: { business, audience, offerings, source } where source is you (written in the site’s Setup, or from the Topics banner), run (read from the site by the audit) or memory; null fields when nothing says. Also buildNote: why the last build kept nothing (the site could not be read, no topic fit it), or null.
PATCH /sites/:id/about analyst Sets what the business is, { business, audience?, offerings?, rebuild? }: 20 to 1,500 characters. Every topic build is held to it, and it wins over what the audit read. With rebuild: true the topics are built again at once: topics the description does not fit are left out, and built topics a rebuild no longer has are retired (their history kept). Answers { profile, rebuilding }. Analyst role; audited.
GET /sites/:id/progress viewer What the site is waiting for before its figures fill in (LR-058): the run, the topics, the engines’ answers, the competitors and Results, each done, under way, waiting or stopped, with a sentence and where to look. Read only from what is stored. Every member.
GET /sites/:id/mentions viewer GET /api/sites/:id/mentions?days=&market=&topic=&engine=&kind=named|cited|first&group=question&limit=&offset=: where the site’s Answer Share comes from: the window’s totals by topic and engine (with the times an engine showed no AI answer), and a page of the answers that name or cite the site, newest first; with group=question, a page of the questions instead, each with every engine’s answer to it side by side.
GET /sites/:id/seo-health viewer GET /api/sites/:id/seo-health: SEO Health (SEO-HEALTH-PLAN.md): the site’s latest checked pages read against the SEO rulebook: the score with its cap and band, the four questions’ scores, every rule’s result with the pages it fails on and what was measured, and each page’s own score. Read from what the latest run stored; nothing is fetched. plays: each rule’s play by rule id (SEO-HEALTH-PLAN.md §8: its SEO play, or the audit play that has it), for “Open play”. ?format=csv: the issues as a spreadsheet, one row per issue and page with what was measured (§8 step 5).
POST /sites/:id/seo-health/issues/:rule/play analyst POST /api/sites/:id/seo-health/issues/:rule/play: “Add to plays” (SEO-HEALTH-PLAN.md §8): the rule’s play, made now from the latest SEO Health reading (201), or the one it already has (200). 404 when the rule is not measured; 409 ai_owned when AI access owns the issue and the full run has not made its play yet. Analyst role; audited.
POST /sites/:id/seo-health/check analyst POST /api/sites/:id/seo-health/check: “Check again” (SEO-HEALTH-PLAN.md slice B): queues a light SEO check of the site’s important pages, or answers with the one already queued or running (200). A new check waits an hour after the last one finished (429 with nextAllowedAt). Analyst role; audited.
GET /sites/:id/questions-standing viewer GET /api/sites/:id/questions-standing?days=: every active question by topic, with the site’s place in its answers and the rival they name most.
GET /sites/:id/communities viewer The forums, Q&A sites and communities engines cite for the site’s topics, ?days= (28 by default): the share of the site’s search answers that cite any community (with a Wilson range), over all engines and per engine; the number of communities by kind (forum, Q&A, social); and each community (a subreddit, Quora, a Stack Exchange site, a forum) with the answers citing it, the threads, the topics and engines, its language and whether it fits the site’s market (markets lists the markets of the site’s topics). Built from the cited addresses alone, so Reddit and the like (never fetched) are counted. read is true for a community whose pages Vercatus reads.
GET /sites/:id/products viewer The products AI answers name (FEATURE-GAP-PLAN.md item 8), ?days= (28 by default) and ?market= (one market’s topics): each product under its brand, the site’s own and its competitors’, with the share of answers that name it, recommend it and name it the top pick (read like Answer Share, each with its range), per engine, and the topics it is named on; the 40 named most.
GET /sites/:id/brief-themes viewer What the engines agree on (engine advice, EA-05): the latest week’s themes from every engine’s latest brief, consensus first. Each theme is one point the engines make (against the site, what would make it a first choice, how it could be more visible, or why they would pick a named competitor instead), with the engines that make it out of those briefed, whether that is consensus (three engines, or two that are half of those briefed), the topics asked, up to five of the engines’ own phrasings, and corroboratedBy: the lenses whose measurements show the same (sources, search, memory, audit), empty when none does. What the engines say, never a cause. { week: null, themes: [] } before the first brief.
GET /sites/:id/plan viewer The site’s plan, made with its plays: where it stands in five areas (areas: readable, understood, answers, confirmed, authority, each strong, needs_work or blocking, with why), at most three active initiatives and two after this quarter (initiatives, each with its steps: the play’s id, status, headline and why), the step to take this week (next), the technical fixes for the developer (developer) and the claims checked and not shown (checked). 404 before the first plan.
GET /sites/:id/content viewer Content (PLAYS-UX-PLAN.md §0): every piece written for the site, newest first: the pieces written on Content and the pages written for content plays (their latest version), in one shape, without their bodies (?limit=, 1–200).
POST /sites/:id/content analyst Writes a piece now, { kind, subject, ask?, structure?, shape?, length?, playId?, topicId?, targetUrl?, beat? }: kind is article, guide, faq, comparison, listicle, product_page, landing_page, section, email or custom (with ask, what it should be, and shape: page, section or text); beat the pages AI cites that it must do better than. Written in one pass from the site’s evidence, checked, and anything the facts do not hold becomes an [ADD: …]. A page or a section uses one of the plan’s monthly articles (402 with the reason when none is left); plain text is free; 429 past the hourly cap. Answers 201 with { piece, allowance }, or 202 with { piece: { id, status: "pending" } } when it takes longer than the request waits. Analyst role; audited. Vercatus never publishes it.
GET /sites/:id/content/:pieceId viewer One piece, whole: its body, meta, links, the facts to add and its checks. 404 when it is not the site’s.
GET /sites/:id/visibility-feeds viewer A site’s visibility on Google’s own surfaces, by its kind (siteType: shop, publisher, local, software, other; typeFrom says whether it was set or read from the domain’s vertical): serp, the blocks of Google’s results pages for the site’s questions (Top stories, local pack, popular products, shopping, video: on how many questions each showed and on how many the site was in it) and the People Also Ask questions shown most; then the feed its kind reads, each with status (awaiting or ready), asOf and nextBy (the day the next read lands): shopping (shops: listed or not, rank, price against the others’ median), news (publishers: in News results and Top stories, freshness of the listed articles), maps and profile (local businesses: Maps rank, and the Business Profile against the name, address and phone the site states). A feed the kind does not read is null. 404 for a site the caller cannot see.

Cross-site views and the numbers a board reads.

Method Path Role What it does
GET /usage viewer Plan, per-site allowance used this month, runs this month.
GET /board viewer ?month=YYYY-MM reads the board as it stood at the end of that month; decision states ride along, keyed by site and decision text.
GET /board/report viewer The board brief across sites as standalone HTML, a view of Results (PHASE-4.md §6: Answer Share since the start, each shipped play’s verdict, the wins, the plays waiting for a read, the outcomes); ?format=pdf for a PDF; ?month=YYYY-MM the 28 days to the end of that month; ?sites=a,b some sites. Query: ?download= (1 sends it as a file).
GET /overview viewer The overview: what happened since last week, what needs a person, how each site is trending, and the one knowledge-base entry the current worst findings rest on. Sentences first; numbers behind them.
GET /portfolio viewer Portfolio: one row per site with its latest completed run, critical and high counts, AI-blocked pages, coverage, new and resolved findings. Answers “what is wrong and what changed across everything I own” without opening a run.
GET /usage/meters viewer Each metered allowance the plan sets, as used against its limit in the current period, and when it renews. capped: false where nothing is enforced.
GET /usage/history viewer The workspace’s usage: the contract in force (if any), where it stands against the soft limits now, and the last twelve calendar months. ?format=csv for the months as a spreadsheet. Model tokens and cost, the platform’s and those of the workspace’s own provider alike, are an operator’s alone, in the JSON and the CSV (2026-10-15).

An agency workspace’s client workspaces: the portfolio across them, making one, consolidated billing, and a client workspace’s choice of branding.

Method Path Role What it does
GET /agency viewer The agency’s portfolio: every client workspace with its sites, cited share by engine over the last days (30; 7 to 90) with its denominator, critical and high findings in each site’s latest run, the next scheduled run, and plan use; the agency’s own plan, consolidated billing and the seats and sites it counts. Owners and admins of the agency. Query: ?days= (days back from today).
POST /agency/workspaces viewer { name }: make a client workspace. Its branding follows the agency’s; with consolidated billing it is on the agency’s plan, otherwise on the free plan until it takes its own. Owners and admins of the agency, on Portfolio or Enterprise.
PUT /agency/billing viewer { consolidated }: bill the client workspaces on the agency’s subscription (and count them against its plan), or each on its own. Switching on is refused while a client workspace has a live subscription of its own (cancel it there first, so nobody pays twice); switching off returns every client workspace without its own subscription to the free plan until it takes one. The agency’s additional-site quantity follows. Owners of the agency.
DELETE /agency/workspaces/:id viewer Let a client workspace go its own way (a client leaving the agency): the agency’s people no longer enter it, and a consolidated one returns to the free plan until it takes its own. It needs an owner of its own first. Owners of the agency.
PUT /agency/branding viewer { follow } in a client workspace: show the agency’s branding (true; the default) or keep its own (false). Admins of the client workspace.

Reports emailed on a schedule: the board, results, the quarterly review, the monthly brief, the visibility export and the weekly digest, with each send’s history.

Method Path Role What it does
GET /report-schedules viewer The workspace’s schedules the caller may see, newest first, each with its last send; the formats each kind takes.
POST /report-schedules analyst { name, kind, cadence, dayOfWeek?, dayOfMonth?, hour, timezone, siteIds (null: every site), engines (null: every engine), recipients, format }: a new schedule; its first send is the next slot.
PATCH /report-schedules/:id analyst Change a schedule, pause it ({ paused: true }) or resume it; the next send is worked out again.
DELETE /report-schedules/:id analyst Delete a schedule and its history; review links it already sent keep working until they expire or are revoked.
POST /report-schedules/:id/send analyst Send it now for the period ending today, whatever its cadence says; counts against the daily email cap. The next scheduled send stays as it was.
GET /report-schedules/:id/sends viewer Its sends, newest first (the last 50): when, by the schedule or by a person, to whom, the period, and why one did not go.

Settings, members, invitations, API keys and integrations.

Method Path Role What it does
GET /org viewer The caller’s workspace: its name, plan and limits, branding, settings with secrets masked, the caller’s role, and which Google and crawler-traffic connections the plan allows. A client sees only the name, plan and branding.
DELETE /org owner (no keys) { confirm } with the workspace’s name typed back: close the workspace. Owners. It is read-only at once, the subscription is cancelled without a refund of the period paid, and everything in it is deleted after 28 days; the owners are emailed now and when it is gone. It can be reopened until then.
PATCH /org admin (no keys) { name?, branding?, settings? }: change the workspace. Settings and branding merge key by key; a key not sent is left as it is. Integration tokens and channel addresses are kept unless replaced (“set” keeps). Refused: unknown keys, operator keys, values beyond the plan.
GET /org/people analyst Who can be given a finding: every member’s name and email, readable by anyone who can plan work.
GET /org/members admin (no keys) Members, pending invitations (with their id, for revoking) and the seats: owners, admins and analysts take one, and so does an open invitation for those roles; viewers and clients are free. siteIds is the sites a limited member (or invitation) sees, null for every site.
POST /org/invites admin (no keys) { email, role, siteIds? }: invite someone (role owner, admin, analyst, viewer or client; only an owner invites an owner). siteIds limits an analyst, viewer or client to those sites (a client always needs some; null or absent: every site). An invitation for a role that takes a seat holds one and is refused with 402 when none is free; viewers and clients are always free. Inviting the same address again replaces the open invitation. The invitation is emailed when an email provider is configured. A workspace without a plan sends at most 3 invitations a day and 10 in all (402 past the total, 429 past the day, code invites_unpaid_total or invites_unpaid_day); a plan lifts it to 30 an hour and 100 a day.
DELETE /org/members/:userId admin (no keys) Remove a member from the workspace: their sessions here end and their seat is free again. Admins remove analysts and viewers; only an owner removes an admin or an owner; the last owner stays.
PATCH /org/members/:userId owner (no keys) { role, siteIds? }: change a member’s role (owner, admin, analyst, viewer or client). Moving to client needs siteIds unless they already have a site list; moving to admin or owner gives every site. Moving a viewer to a role that takes a seat needs a free seat (402 otherwise); the last owner stays an owner. Sites alone: PUT /api/org/members/:userId/sites.
DELETE /org/invites/:id admin (no keys) Revoke an open invitation; its seat is free again.
GET /org/monthly-brief viewer The monthly brief’s state: on or off, its recipients, when it last went out, and whether it is due now.
GET /org/monthly-brief/preview viewer The monthly brief as it would be sent today, as HTML, for a look before it goes out.
GET /org/api-keys admin (no keys) API keys with prefix, scopes, sites, address allowlist, expiry, state (active, expiring within 14 days, rotating in its grace period, expired, revoked), who made each and when it was last used (to the minute); never the secret.
POST /org/api-keys admin (no keys) { name, scopes?, expiresInDays?, siteIds?, ipAllowlist? }: create a key. Scopes are read (the default), write and export; expiresInDays is 1 to 730 (365 by default) or "never" on Enterprise; siteIds limits the key to those sites (null: every site); ipAllowlist lists addresses or CIDR ranges it may be used from. The secret is returned once.
PATCH /org/api-keys/:id admin (no keys) { name?, siteIds?, ipAllowlist? }: rename a key or change the sites and addresses it is limited to. Scopes and expiry are fixed at creation; rotate for new ones.
DELETE /org/api-keys/:id admin (no keys) Revoke a key at once.
POST /org/api-keys/:id/rotate admin (no keys) { graceHours?, expiresInDays? }: rotate a key. The replacement keeps the name, scopes, sites and addresses and gets a new expiry (365 days by default); the old key keeps working for graceHours (24 by default, 0 to 168), then stops. The new secret is returned once.
GET /org/setup/sites viewer Each site’s setup: the required steps (verified domain, topics, a full run, competitors, Search Console or Analytics) and the recommended ones (crawler traffic, a CMS or deploy hook), each with whether it is done and where. Empty until the workspace has a plan.
GET /org/setup viewer The workspace’s setup checklist: each step (a verified domain, topics, a full run, competitors, Search Console or Analytics, alerts; inviting a teammate is optional and not counted) with whether it is done and where to do it. Empty until the workspace has a plan.
GET /org/audit admin (no keys) ?action= a kind (finding, run, site, report, prompt, goal, traffic, org, member, account, billing, operator) or an action prefix; ?actor=, ?from=, ?to= (ISO dates, UTC), ?q= a search; ?limit= (100, at most 500). Paged by ?cursor= (the x-next-cursor header of the previous page); ?before=<time> still works. The body is the list; ?envelope=1 answers { items, nextCursor } instead.
GET /org/audit.csv admin (no keys) The same filters as a CSV file (UTC, ISO 8601), newest first, every event that matches: streamed in pages, so there is no cap and no silent cut-off (a stream that fails part-way is reset, not ended clean). The download is itself recorded, with the number of rows. Query: ?action= (an action kind or prefix), ?actor= (one person or key), ?from= (start of the period (an ISO date)), ?q= (a search), ?to= (end of the period (an ISO date)).
GET /org/audit/meta admin (no keys) What the filters offer: the kinds, the people and keys that appear in the log, and the retention setting.
PUT /org/audit/retention owner (no keys) { days }: keep audit events this many days (at least 365), or null for the life of the workspace. Owners.
GET /org/lifecycle viewer Whether the workspace is closed, since when, when it is deleted, and whether it can still be reopened.
POST /org/reopen owner (no keys) Reopen a closed workspace before its deletion date. Owners. A subscription cancelled at closing stays cancelled: choose a plan again to run.
POST /org/export admin (no keys) Start a workspace export: sites, runs, findings, verified fixes, citation checks, prompts and their results, goals, the audit log and usage, as CSV and JSON in one zip. Admins. Answers 202 at once; the worker writes the file and emails the person who asked. One at a time, five a day.
GET /org/exports admin (no keys) The workspace’s exports, newest first, each ready one with a download link that works for 15 minutes. Admins.
GET /org/exports/:id/download public The export file behind a signed link from GET /api/org/exports; the link names the workspace and expires. Query: ?exp= (the link’s expiry), ?org= (the workspace), ?sig= (the link’s signature).
POST /org/ticketing/test admin (no keys) { provider } (linear, jira, asana or github): check a ticketing integration’s credentials and target without creating an issue.
POST /org/alerts/test admin (no keys) Post a sample alert to one channel ({ channel: slack | teams | email }, Slack by default) so it can be checked before a run relies on it.
POST /org/webhooks admin (no keys) Outgoing webhooks: { url, events } creates one; the signing secret is returned once, here, and stored sealed.
PATCH /org/webhooks/:id admin (no keys) { active?, events?, url? }: change an endpoint. Unknown event names and unacceptable addresses are refused, not dropped.
DELETE /org/webhooks/:id admin (no keys) Remove an endpoint. Its queued deliveries fail as “removed” when their turn comes.
POST /org/webhooks/:id/test admin (no keys) Send a signed test delivery to one endpoint now and report its status (never its body); it shows in the delivery log.
POST /org/webhooks/:id/rotate admin (no keys) { graceHours? } (0 to 168, default 24): a new signing secret, returned once. Until the grace ends, deliveries are signed with both the new and the old secret (two v1 values), so the receiver can switch without dropping any.
GET /org/webhooks/deliveries admin (no keys) The delivery log: the last deliveries (30 days kept), newest first; ?hook= one endpoint, ?status= pending, delivered or failed, ?limit= up to 200.
POST /org/webhooks/deliveries/:id/redeliver admin (no keys) Send one delivery again: a new entry in the log with the same payload and delivery id, queued now.
POST /org/monthly-brief/send admin (no keys) Send the monthly brief now to its recipients (or to { to }), whatever the date. Only to members and addresses on the workspace’s verified domains, and within the daily email cap. Admins.
PUT /org/members/:userId/sites admin (no keys) { siteIds }: the sites this member sees; null gives an analyst or viewer every site again. A client always sees some. Admins.
GET /org/model-provider admin (no keys) The provider as stored (no secret), whether the plan includes it, the platform model ids it will be asked for, and what a Bedrock role must trust.
PUT /org/model-provider admin (no keys) { kind: "anthropic", apiKey }, { kind: "bedrock", region, auth: "keys", accessKeyId, secretAccessKey } or { kind: "bedrock", region, auth: "role", roleArn }, { kind: "vertex", projectId, region, serviceAccountJson }, each with enabled? and models? ({ platform model id: provider model id }). A secret left empty keeps the stored one.
DELETE /org/model-provider admin (no keys) Remove the provider: the workspace’s model calls go to the platform’s provider again from the next call.
POST /org/model-provider/test admin (no keys) One short call through the provider: the body’s settings when given (before saving them, a secret left empty uses the stored one), else the stored provider, whose last test is then recorded.
GET /org/sites/verified-elsewhere analyst ?url=: whether adding this host here would be verified at once, because the caller verified it in another workspace they administer; names that workspace. Nothing about workspaces the caller does not run is said.

The plan, Stripe’s checkout and portal, plan changes, and the webhook Stripe calls.

Method Path Role What it does
GET /free-report viewer The free report of a workspace that already had one: its run and where it stands. No free report is started any more, so this is null on a paid plan and for every workspace without one.
GET /billing viewer The workspace’s billing state: plan, subscription status, period, what the plans cost at this site count, the next invoice.
POST /billing/checkout admin (no keys) { plan, interval? } (interval month, the default, or year): start a subscription on Stripe’s checkout page; the answer is { url } to send the browser to. Owners and admins.
GET /billing/checkout/:sessionId admin (no keys) The checkout return: read the session and apply its subscription now, so the page does not wait for the webhook.
POST /billing/checkout/cancelled admin (no keys) The person came back from checkout without paying: recorded for the funnel. Nothing was charged.
POST /billing/refund admin (no keys) Cancel now and refund what was paid (§1.8): only while refundState allows it. The paid invoices of the subscription are refunded through Stripe and the subscription ends at once; the workspace keeps its data, back on the free plan. When Stripe does not take the refund, the plan is still cancelled and the operator is told to refund by hand. Owners and admins. V-04: the check and the money movement run under one lock per workspace, and each refund carries an idempotency key, so two calls at once (a double click, a replay) refund once: the second finds the subscription gone and is told so, and no “refund by hand” notice is sent for a refund that was in fact made.
POST /billing/portal admin (no keys) Open Stripe’s customer portal: card, invoices, cancellation. { url }.
POST /billing/change admin (no keys) { plan, interval? }: move a live subscription to another plan in place, prorated; the additional-site item follows the sites list. The subscription keeps its billing interval unless interval names the other one; the same plan on the other interval switches only the interval. Stripe restarts the billing period on an interval change and prorates what was paid, so month to year charges the year now less the unused month, and year to month credits the unused year.
POST /billing/seats admin (no keys) { extra }: the seats bought on top of the plan’s included ones, as the quantity of the subscription’s “Additional seat” item, prorated. Refused below the seats in use (owners, admins, analysts and their open invitations). Owners and admins.
POST /billing/pause admin (no keys) { months } (1 to 3): pause the plan (RETENTION-PLAN.md §7). Nothing is charged, the workspace is read-only, and it resumes on its own on the date; resumesAt is returned.
POST /billing/resume admin (no keys) Resume a paused plan now; the next invoice is on the usual date.
POST /billing/webhook viewer Stripe’s webhook: verified by signature, claimed once per event id, applied, then marked applied. A failed apply releases the claim and answers 500, so Stripe’s retry applies it again instead of being taken for a duplicate; a delivery arriving while the same event is being applied answers 409 and is retried later. Public.

The evidence-graded hypotheses findings cite, what Learn observed of each (supported, not observed or contradicted, with n), their review state and the research agent’s proposals.

Method Path Role What it does
GET /kb viewer The knowledge base with its runtime state: each entry carries its last source check and any curator decision. Curation (confirming entries, accepting proposals) is done by Vercatus; the base is shared by every workspace. Each entry also carries observed (PHASE-5.md §1): what Learn’s verdicts and natural experiments say of the rule (verdict supported, not_observed or contradicted, with n, nSites and the effect; withheld below three sites), or null when no play measures it.
GET /knowledge/:id viewer One knowledge-base entry (by id, a merged id or its rule number, R-068): the entry, its curator state, and observed (PHASE-5.md §5): { verdict: "supported" | "not_observed" | "contradicted", n, nSites, effect } from the priors of the plays and page changes it rests on, withheld below three sites, or null when no play measures it.

Sessions for the workspace. API clients use a key instead and skip these.

Method Path Role What it does
GET /auth/google/start public “Continue with Google”: the platform’s Google app (openid email profile). Links to an existing account by verified email; a new person without an invitation gets a new workspace. ?next= is the app path to return to. Query: ?consent= (1 when product emails were agreed to), ?domain= (the domain typed), ?email= (the address typed), ?from= (start of the period (an ISO date)), ?interval= (month or year), ?plan= (the plan picked).
GET /auth/microsoft/start public “Continue with Microsoft”: the platform’s multi-tenant Entra app (work, school and personal accounts). Links by an address Microsoft verified; a new person without an invitation gets a new workspace. Query: ?consent= (1 when product emails were agreed to), ?domain= (the domain typed), ?email= (the address typed), ?from= (start of the period (an ISO date)), ?interval= (month or year), ?next= (where to go afterwards), ?plan= (the plan picked).
GET /auth/oidc/start public The instance’s own identity provider, when one is configured: the same flow as the workspace connections (PKCE, state, nonce, a verified email, SSO enforcement), members only. Query: ?email= (the address typed), ?next= (where to go afterwards).
GET /auth/callback public The instance provider’s return (its registered redirect URI): the identity is checked as for any connection, then the person is signed in.
PUT /auth/me/pins viewer { siteIds }: the signed-in person’s pinned sites for the rail, in order (at most ten; sites of other workspaces are dropped). Answers the stored list.
GET /auth/config public How this instance signs people in: the instance-wide OIDC provider, local passwords, “Continue with Google” and “Continue with Microsoft”, and whether email-first SSO lookup (POST /api/sso/lookup) is available.
POST /auth/register public Local sign-up: { email, password, terms: true, name?, orgName?, domain?, plan?, interval?, from?, consent?, remember? }. The first user owns a new workspace; an address with an open invitation is sent to the invitation link instead (409 with invited). terms is the accepted terms and privacy notice, required. The answer’s next is the welcome wizard (ONBOARDING-V2.md); a plan and interval from the pricing page are kept as a hint for its recommendation, and a domain is added as the first site. The confirmation email (a code and a link) goes out; nothing waits for it until the exploration and the checkout. from names the public page the click came from; consent is the opt-in to product emails; remember: false makes the session end with the browser.
GET /auth/verify-email public The emailed link: marks the address verified, attaches the invitations waiting for it, starts what waited for it (the exploration of the first domain; a paid workspace’s first run when its domain is verified), and lands on one small page that says so. The setup page already open in another tab ticks itself green; nobody has to go back and forth. Query: ?token= (the emailed token). Holding the link is not proof of being the person: a mail scanner opens every link in a message, and whoever registered somebody else’s address chose its password. So a GET confirms the address only for a browser that already holds a session of that very account (the person who signed up and clicked in the same browser); anyone else lands on a page that asks for the account’s password and confirms with a POST (below). A link opened after the address was confirmed is still “confirmed”.
POST /auth/verify-email public { token, password? } (JSON, or the form on the page the link opens): confirm the address with its emailed link, from a session of the same account or with the account’s password. Answers { ok: true }; a form post is sent on to the “confirmed” page. Attempts are counted before the password is checked.
POST /auth/verify-email/send public { email }: send the confirmation link again without a session (a password sign-in is refused until the address is confirmed). Always answers the same.
POST /auth/login public Local sign-in: { email, password }. Sets the session cookie, or answers { mfa: true } when the account has an authenticator app (then POST /api/auth/mfa/verify). Refused with sso.url for an address whose domain a workspace enforces single sign-on for, with unverified until the address is confirmed, and with 429 after repeated failures from the address or for the account (the account stays locked for up to 15 minutes; a password reset clears it).
GET /auth/mfa/pending public Whether this browser holds a pending two-step sign-in (after a password or an identity provider), and which second factors the account has: the sign-in page then asks for a code or a passkey.
POST /auth/mfa/passkey/options public The second step with a passkey or security key: the options for the browser’s WebAuthn call, limited to the account’s own passkeys.
POST /auth/mfa/passkey/verify public { response }: the browser’s WebAuthn answer; a passkey registered to the account completes the sign-in.
POST /auth/mfa/verify public { code }: the second step of a sign-in: a code from the authenticator app, or a recovery code (spent). Starts the session. Six wrong codes in 15 minutes lock the step.
POST /auth/logout public Ends the session.
POST /auth/password/forgot public { email }: email a reset link (valid for an hour, once). Always answers the same, whether or not the address has an account.
POST /auth/password/reset/check public { token }: whether a reset link is still valid, for the reset page; the link is not spent.
POST /auth/password/reset public { token, password }: set a new password with a reset link. The link works once; every session of the account ends, the address counts as confirmed, and each workspace’s audit log records it.
POST /auth/invite/lookup public { token }: what an invitation link offers: the workspace, the role, the invited address, whether it has an account, and who is signed in here.
POST /auth/invite/accept public { token, password?, name? }: accept an invitation. The emailed link proves the address, so accepting confirms it. Signed in as the invited address: joins at once. No account: password creates one. An account without a session: its password signs in and joins (then the authenticator step, when it has one). Signed in as someone else: refused.
POST /auth/invite/resend public { email }: send the open invitations for an address again, with new links (the old ones stop working). Always answers the same.
GET /auth/me viewer Who is signed in, their role, and the workspaces they belong to; the workspace’s seats (“3 of 5”), and whether single sign-on, SAML, enforcement and a signable DPA are available to it; two-step sign-in (on, required by the workspace) and whether the workspace’s policy confines this session (restricted: mfa or ip).
PATCH /auth/me viewer { name?, usageAnalytics? }: change the signed-in person’s display name, or turn off (false) or back on (true) the recording of how they use the workspace. Turning it off also deletes what was recorded of them.
POST /auth/password viewer { current, next }: change the password. The current one is required when one is set; the new one has at least 10 characters. Other sessions of the account end, and open reset links stop working.
POST /auth/verify-email/resend viewer Send the confirmation email (a new code and link; the old ones stop working) again to the signed-in user.
POST /auth/verify-email/code viewer { code }: the six digits from the confirmation email, typed on the setup page. Confirms the address as the link does, and starts what waited for it. Eight wrong codes in 15 minutes lock the form until the window passes (the email’s button still works); already when the address was confirmed meanwhile (the link in another tab).
POST /auth/switch-org viewer { orgId, code? }: move to another workspace the user belongs to. A new session starts there (with that workspace’s session length) and this one ends. A session that came through one workspace’s single sign-on was vouched for by that workspace’s identity provider only: moving it to any other workspace needs a fresh second step when the person has one (code: an authenticator or recovery code, else 403 mfaRequired), and the new session carries neither the SSO mark nor a passed second step it did not earn, so the other workspace’s own policy (require two-step sign-in) applies to it as to a password session.
POST /auth/mfa/setup viewer { currentPassword }: start setting up an authenticator app: a new secret (shown once, as text and as an otpauth:// link for the QR code). It is off until POST /api/auth/mfa/enable gets a code. Needs re-authentication (see above).
POST /auth/mfa/enable viewer { code }: the first code from the app turns two-step sign-in on. Answers the ten recovery codes, once.
POST /auth/mfa/disable viewer { code }: turn two-step sign-in off with a current code or a recovery code. Refused while the workspace requires it for this session.
POST /auth/mfa/recovery-codes viewer { code }: new recovery codes (the old ones stop working), with a current code.
GET /auth/passkeys viewer The signed-in person’s passkeys and security keys (never the keys themselves).
POST /auth/passkeys viewer { response, name? }: the browser’s answer; the passkey becomes a second factor for sign-in.
POST /auth/passkeys/options viewer { currentPassword }: start adding a passkey: the options for the browser’s WebAuthn call. Needs re-authentication, like setting up the authenticator app.
DELETE /auth/passkeys/:id viewer Remove a passkey. Only from a session that passed two-step sign-in, and not the last second factor while the workspace requires one.
GET /auth/sessions viewer The signed-in person’s sessions across workspaces: where and how each signed in, when it was last used, and which one is this browser.
DELETE /auth/sessions/:id viewer Sign one session out (another browser, or this one).
POST /auth/sessions/revoke-all viewer { includeCurrent? }: sign out every other session; with includeCurrent, this one too (“sign out everywhere”).

Vercatus Intelligence, the assistant: conversations as tickets, answers streamed as server-sent events, closing, rating and asking for a person.

Method Path Role What it does
GET /assistant/threads viewer The signed-in person’s conversations, newest first; stale open ones are closed first.
POST /assistant/threads viewer { message, context? }: open a conversation and stream the first answer (server-sent events: thread, status, delta, done, error).
GET /assistant/threads/:id viewer One of the person’s conversations with its messages.
POST /assistant/threads/:id/messages viewer { message, context? }: ask again in an open conversation; the answer streams as for a new one. A closed one is refused.
POST /assistant/threads/:id/close viewer Close one’s own conversation; it can be rated afterwards.
POST /assistant/threads/:id/rate viewer { rating: 1-5, solved: boolean, feedback? }: rate a closed conversation, once. A rating of 2 or less, or “not solved”, flags it for the team.
POST /assistant/threads/:id/escalate viewer { reason? }: ask for a person; the ticket is flagged for the team, who reply by email.

What MCP clients and code generators read: the caller’s context and this description.

Method Path Role What it does
GET /mcp/context viewer Who this caller is to Vercatus: the workspace, the key’s scopes, sites and expiry (for an API key), and the sites it can see. MCP clients check a key with it.
GET /openapi.json public This API as an OpenAPI 3.1 document, for code generators, API clients and the docs’ reference. Public: it describes routes, not data.

Dated notes the trend charts draw beside the engines’ own changes, and saved views: a page with its scope and filters, one person’s or the workspace’s.

Method Path Role What it does
GET /annotations viewer ?siteId=&from=&to=: the notes in a period (every day by default), oldest first. With a site, its own notes and the workspace-wide ones.
POST /annotations analyst { at, label, siteId? }: add a note; without a site it shows on every site’s charts.
PATCH /annotations/:id analyst { at?, label? }: change a note (its author, or an admin).
DELETE /annotations/:id analyst Remove a note (its author, or an admin).
GET /views viewer The caller’s own views and the workspace’s shared ones, by name.
POST /views viewer { name, path, query, shared? }: save a view, shared with the workspace when shared.
PATCH /views/:id viewer { name?, path?, query?, shared? }: rename, re-point or share a view (its author; an admin for a shared one).
DELETE /views/:id viewer Delete a view (its author; an admin for a shared one).

Unauthenticated: health and branding.

Method Path Role What it does
GET /health public Liveness, and whether a model provider and which profile are configured.
GET /branding public White-label shell: the dashboard reads this before rendering anything branded.
POST /hooks/cms/:siteId/:token public Inbound change events from a site’s CMS or deploys (Shopify, Contentful, Sanity, the WordPress snippet, or the generic shape); authenticated by the per-site token in the URL and the provider’s signature.
POST /hooks/traffic/:siteId/:token public Crawler traffic ingest (OUTCOMES-PLAN.md A.5): the Cloudflare Worker on the customer’s zone, or a Logpush job, posts bot request lines here; the token in the path is the site’s. Answers 202 at once; the lines are aggregated in memory and written once a minute. No session; public like the CMS hook.
GET /goals viewer The workspace’s goals with their current value from the latest runs, the starting value, and whether each is reached, on track, behind or missed.
POST /goals analyst { metric, target, dueAt, siteId?, owner?, note? }: set a goal for a site, or for every site when siteId is null.
PATCH /goals/:id analyst { outcome: "dropped" } closes a goal; { owner?, note?, dueAt?, target? } changes it.
GET /watch viewer What the daily access watch found across the workspace: the Watch tier’s own surface (§9.4 #4). days defaults to 7, at most 30. Query: ?days= (days back from today).
GET /onboarding viewer Where the workspace stands between sign-up and its first report (ONBOARDING-PLAN.md §1.4): the plan and whether it is paid, the plan picked on the pricing page, the site it is about, its proof, its first full run and the step to show. The setup page (/setup) and Home read it.
PUT /board/decisions admin (no keys) { siteId, decision, state, note? }: what the organisation did with a decision the brief asked for; state: null clears it.
GET /value viewer What the subscription did (APP-DESIGN-PASS-3.md §3.6): per site, the first and the latest figures in the window, the fixes the runs confirmed, the pages that became readable and the questions newly answered. Honest when nothing moved: the figures say so and the page points at the open list. Query: ?days= (days back from today).
GET /review/links viewer The workspace’s read-only review links: label, window, expiry, views; the secret is never shown again. A caller limited to some sites sees the links for its sites only.
POST /review/links viewer Create a read-only link for a review window: { from, to, siteIds?, label?, expiresInDays? (1 to 365, default 90) }. The URL is in the answer once; whoever holds it reads the review for that window and those sites (every site when none are named), without an account, until it expires or is revoked. Admins; a client for its own sites (the link then shows those, or the ones named among them).
DELETE /review/links/:id viewer Revoke a read-only link; the URL stops working at once. Admins; a client for a link that shows only its sites.
GET /landscape viewer The knowledge-base movements that touch this workspace: entries under its sites’ open findings that were retired, updated, superseded, or whose sources changed after the run that wrote the finding.
GET /integrations/google/start analyst Start connecting a Google product for a site (?product=gsc for Search Console, the default, or ga4 for Analytics): redirects to Google’s consent screen with that product’s read scope only. Query: ?return= (where to go afterwards), ?siteId= (one site).
GET /integrations/google/callback viewer Google returns here. The refresh token is sealed and stored on the site with the property that matches its host; the first sync runs at once. Query: ?code= (the code Google sends back), ?error= (an error Google sends back), ?state= (the signed state Google sends back).
POST /ui-events viewer { events: [{ kind, route, target?, ms?, at? }] }, at most 50: what the signed-in person did in the workspace (view, drawer, search_empty, back, figure), recorded only when the instance records usage, for a session (never an API key), and when the person has not turned it off in Account; otherwise accepted and dropped. Routes are kept as patterns and targets cut to 80 characters.
GET /engine-changes viewer The trend breaks in a period, oldest first, for chart annotations: [{ engine, at, kind, label }], where kind is model (a new model answered), config (how we ask changed), outage (the engine answered under half of a run’s questions) or methodology (the panel’s method changed; engine “all”). ?from=&to= (dates; the last 90 days by default) and ?engines= (a change of method always comes along).
GET /search viewer ?q= (3+ characters): what the command palette finds by its words, on the caller’s sites only. findings: open items of each site’s latest site audit; prompts: the workspace’s own questions; questions: the questions asked about the sites, from the citation panel’s asks, the tracked panel and the latest full run, one hit per site and question ({ text, siteId, host, sources }, sources among panel, tracked and run; eight at most); knowledge: knowledge-base entries ({ id, title }).
GET /ready public Readiness for a load balancer or an uptime check: 200 when the database answers, the blob store takes a write and gives it back, no run has waited too long in the queue and (in worker mode) a worker is beating; 503 otherwise, with which check failed. /api/health stays the liveness check.
GET /account/deletion viewer What deleting this account would do: the workspaces it would leave, close, and the ones that need a new owner first.
POST /account/delete viewer { confirm, password? } with the account’s email typed back, and the password when the account has one: delete the account. 409 with the workspaces that need another owner first. The session ends; a confirmation is emailed.
GET /search/pages viewer ?q= (3+ characters): pages of each site’s latest completed run whose address or title matches, for the command palette.
GET /notifications viewer ?kind=&siteId=&unread=1&before=&limit=: the caller’s notification centre, newest first: their own notifications (mentions, findings given to them, due dates, watched findings) and the workspace’s notices, each kind only when their preferences show it in the app. unread counts both. before pages by date.
PATCH /notifications/:id viewer { read }: mark one of the caller’s own notifications read or unread (a workspace notice goes through PATCH /api/notices/:id).
POST /notifications/read-all viewer Mark everything in the caller’s centre read: their own notifications and the workspace’s unread notices.
GET /notifications/prefs viewer The caller’s preferences per kind, with the defaults filled in, and whether email is set up on this instance.
PUT /notifications/prefs viewer { kind, inApp, email }: set one kind. Workspace notices are emailed through Settings › Alerts, so their email stays off here.
GET /public/forecast-record public Vercatus’ published forecast record: before each fix, the predicted change in cited share and its 80% interval; after it, what was measured. Hit rate, Brier score and reliability of every workspace’s scored forecasts together, as aggregates that name no workspace, site or finding.
GET /standings viewer Every site the caller can see with its Answer Share over ?days= (28 by default) and the answers it rests on, the figures each site’s standing gives as you.share and measured.answers: { sites: [{ siteId, you: { share }, measured: { answers } }] }. For a list of sites (Home) in one read, without each site’s trend, engines and leaderboard.
GET /progress viewer The same for every site the caller can see that is not measured yet (the rail’s card), newest first: { sites }.