# Epovest recipes for agents

Task recipes for an AI assistant helping a person use Epovest. Each recipe is a goal,
the tools to reach it in order, and what to tell the person. The full tool and endpoint
reference is the API doc: https://epovest.com/docs/api.md.

Epovest is prepaid and the person pays in the app; an agent never buys credits. With the
person's API key (https://app.epovest.com/account/api-keys) or a one-click connection, an agent runs the whole
method: it states the canon of a brand, creates, starts, reads and pauses trackers,
keeps the official pages and the third-party proof in order, tends the quest file and
the logbook, keeps the details of the account right, and hands back to the app
(https://app.epovest.com) for anything involving money or signing in.

The method the recipes follow, in one line: state the reference wording of the brand
(the canon), measure what the engines answer (trackers), align the pages the person
controls (surfaces), obtain and watch the proof on pages they do not (corroborations),
read which sources the engines listen to (the Atlas), and date every move (the
logbook), the quest file saying at any time what is left to do.

A tracker measures one of four things, and the prompts differ from one to the next:
a **reputation** (what the engines say about one name), a **ranking** (who gets
recommended on the buying questions of a market), a **watch** (what the engines
answer on a topic, over time) or a **market study** (which names come back on a market
the person has not entered yet). Settling which one the person wants is the first step of
the setup recipe below, and it changes the prompts, the keywords and the analysts. A
market study changes one more thing, the UNIT: it compares candidate markets, so it takes
one tracker per candidate, and it has its own recipe further down.

## Recipe: connect Epovest to your client

Goal: let the person drive Epovest from the assistant they already use. How depends on the
client.

- Chat connectors that speak OAuth (for example ChatGPT and claude.ai on the web): add
  Epovest as a custom MCP connector with the server URL https://mcp.epovest.com/mcp. The connector finds
  the sign-in on its own; the person signs in to Epovest and approves the access, once. No
  key to paste. The person reviews and revokes connected apps any time in the app
  (https://app.epovest.com).
- CLIs, desktop apps and frameworks that take a header (for example Gemini CLI, Claude
  Desktop config, or your own code): use the same URL https://mcp.epovest.com/mcp with an API key in the
  header, `Authorization: Bearer epo_...`. The person creates the key in the app
  (https://app.epovest.com/account/api-keys).

Both paths reach the same tools and act for the same account. Pick OAuth when the client
offers a one-click connector; use the header key otherwise.

## Recipe: set up the brand file

Goal: give a brand its project, its reference wording (the canon) and its link targets,
so everything that follows has something to measure against.

1. Call `list_projects` first: the project may already exist, and creating one whose
   name is taken answers `project_exists` with the existing project. Reuse its id,
   never duplicate.
2. Call `create_project` with `name` and the canon fields the person can already state:
   `canon_one_liner`, `canon_short`, `canon_long`, `canon_category`, `canon_language`,
   `canon_perks`, `canon_website`, `canon_legal_name`, `canon_address`, `canon_phone`,
   `canon_whatsapp`, `canon_email`. The wording is the person's, gathered in their own
   words and written in one language (the canonical one): never invent a claim.
   Carried at creation, the canon becomes its version 1.
3. To revise later, call `get_canon` first: it reads the current wording and the
   history of every revision the canon went through, each with the fields it touched
   and their before and after. Then call `update_project_canon`: a sent field replaces
   the current
   value, an omitted field keeps it, an empty one clears it. A real change creates the
   next numbered version; the canon is meant to stay stable, so a revision is a rare,
   deliberate decision the person confirms. After one, expect the surfaces registry to
   show pages needing an update: that is the method working, not an error.
4. Call `get_link_targets` to see the addresses of the brand a third-party link can
   point to. `derived` lists what is already covered without any typing (the canon
   website and the surfaces of the registry, each with its `source`); `free` lists the
   extra addresses set by hand. Read before writing: an address already derived has no
   reason to be typed again.
5. Only when an address is missing, call `set_link_targets` with the complete free
   list: the list sent replaces the previous one, so carry over what stays. The setting
   is project-level and serves every corroboration, present and future.
6. `rename_project` corrects the name of the folder, and only that: the project keeps
   its id, its canon and everything filed under it. Reach for it when the person calls
   the project something else than what the list shows, and keep it apart from the
   canon, where a change records a new version. A name another project already goes by
   answers `project_exists` with that project, so read it back and settle another name
   with the person.
7. `archive_project` puts a project away and back (`{"archived": true|false}`): it says
   a state, so replaying it is safe. Putting a project away touches none of its
   trackers; the response carries `tracker_count`, and if the person also wants the
   measurement stopped, follow up tracker by tracker with `archive_tracker`.

## Recipe: set up a tracker, once you know which of the three you are building

Goal: build the measurement the person actually asked for. Three measurements share the
same tool, and the prompts that serve one serve the others badly: settle the objective
before touching `create_tracker`.

### Step 1: name the measurement

The words a person uses rarely name it themselves. The discriminator is a single
question: **does their own name belong inside the prompts?**

- **Reputation**, the name IS in the prompts. The person wants to know what the engines
  say about them: are they described accurately, in what terms, and does anything
  negative surface. One name is watched, its variants with it.
- **Ranking**, NO tracked name is in the prompts. The person wants to be recommended on
  the questions their market asks before choosing, and to see who gets named instead.
  Several names are watched, theirs and the ones they are compared with.
- **Watch**, no name at all: the prompts carry a topic whose answer can change over
  time, a market, a regulation, a technology, a destination. Nothing is watched by
  name at first; the archive of the answers is the deliverable.
- **Market study**, no name either, and the person is not on this market yet: they are
  choosing where to enter. What they want back is not one series but a comparison between
  candidate markets, which names the engines return on each, how concentrated those
  mentions are, and whose pages the answers rest on. One tracker per candidate, and the
  market study recipe below runs the whole thing.

When what the person said fits two of them, ask one question and no more: "do you want
to know what the AIs say about your name, or who they recommend when someone asks for a
provider like you?" What separates a ranking from a market study is asked as plainly:
"are you already selling on this market, or deciding whether to go into it?" Build one
tracker per objective rather than one that blends them: a blended tracker answers neither
question.

### Step 2: configure what the objective changes

#### For a reputation tracker

- `prompts`: the questions a customer asks ABOUT the person, phrased as one speaks to an
  AI, the name inside. For example "What do you know about [brand]?" and "Is [brand]
  trustworthy? What do its customers say?". Three to five are enough, in the language of
  their customers.
- `keywords`: their exact name with `favorite: true`, then the variants the answers use
  to name them: acronym, trade name, domain, the founder's name for a personal brand.
  Not their competitors: tracking those turns this into a ranking, and the rates then
  answer another question.
- `analysts`: `keyword_presence` and `sentiment`. Tone is the point of this objective, so
  the sentiment analyst earns its place here; tell the person it is billed per analyzed
  answer.
- `frequency`: `weekly`. Move to `daily` for the length of a crisis or a launch, then
  come back down.
- `resolution`: `hd` to establish the baseline, higher when a deviation must be a
  certainty before the person acts on it.

#### For a ranking tracker

- `prompts`: the buying questions of their market, with NO tracked name inside. For
  example "Which accountant should I hire in Lyon?" and "What is the best payroll
  software for a small business?". A prompt that names a tracked keyword hands the
  engine its answer, and the rate that comes back measures the prompt rather than the
  market. The one exception is the incumbent of an alternatives prompt ("what should I
  use instead of [name]?"); read that name's line knowing it was invited.
- `keywords`: their name with `favorite: true`, plus every name the market could cite in
  their place. Share of voice is computed across the tracked set, so a set missing the
  strong names flatters the person and teaches them nothing.
- `analysts`: `keyword_presence` and `share_of_voice`, both deterministic and included.
  Add `sentiment` only if how each name gets cited matters in that market.
- `frequency`: `weekly` for a live market, `monthly` for a slow one, `daily` through a
  campaign.
- `resolution`: the objective that asks the most of it. Rates between close names are
  what is being read, so settle the prompts in `hd`, then raise to `full_hd` or `4k`
  once the list of names is stable.

#### For a watch tracker

- `prompts`: questions on the topic whose answer can move. For example "What should a
  business do about [regulation X]?" and "Top 5 [destinations for a team offsite]?".
  List prompts are worth their place: their composition changes, and the series shows
  it. A closed factual question whose answer will never move archives nothing.
- `keywords`: none at the start, and that is the normal case here. They come later, when
  a name keeps returning from answer to answer and deserves a counted series; adding one
  leaves the archive already collected untouched.
- `analysts`: none is required, the tracker archives and the person reads. The lever of
  this objective is `custom_prompt`, the person's own instruction applied by an AI to
  every answer ("list the risks mentioned"); it is billed per analyzed answer.
- `frequency`: the speed of the topic, `monthly` for a regulation, `weekly` for a
  market, `daily` through an event.
- `resolution`: `hd` while the deliverable is the archive itself; raise it the day the
  person starts counting rates rather than reading answers.

#### For a market study tracker

- `prompts`: the buying questions of that market, and the SAME three shapes on every
  candidate, one per intent (a recommendation, a best-for-this-profile, a how-do-I-choose
  and at what price), with only the trade changing from one candidate to the next. Write
  them in the language and for the country the person would sell in: the engines do not
  name the same providers from one market to the next.
- `keywords`: none at the start, and that is deliberate. The person does not know the
  names yet; the first answers are what supply them. They go in at the second survey.
- `analysts`: none on the first survey, there is nothing to count yet. `keyword_presence`
  joins with the keywords at the second.
- `frequency`: `monthly`, the pace of a decision of this size.
- `resolution`: `hd`, and the same one on every candidate. What is compared is the
  markets, so nothing else may vary between them.

### Step 3: the fields that do not change with the objective

- `title`: a short label the person will recognize.
- `engines`: all six by default (`chatgpt`, `claude`, `gemini`, `perplexity`,
  `mistral`, `grok`). Keep the six on a watch in particular: the same topic told
  differently by each engine is itself a reading.
- `project_id`: the project of the brand when one exists (the brand file recipe);
  omitted, the tracker lives outside any project.
- `discovery`: `true` to have the domains the engines keep citing suggested as new
  keywords, which is how a ranking keeps its set honest. It needs at least one keyword
  to hold, so it does not apply to a watch that starts without any: read the value the
  response returns rather than assuming it took.

The cadences above are the ones each objective is built around, and they assume the
person wants a series. When they want a READING instead, one measurement of where they
stand today, set `frequency` to `on_demand`: `start_tracker` runs that survey and
nothing runs afterwards, so the spend is exactly the one reading they asked for. Each
later reading is a `survey_now`, on the day they want it, and the points still form a
series. It is also the value to reach for when the person says they will decide each
time: a tracker in `on_demand` waits at no cost, and moving it onto a pace later is one
`update_tracker` away.

The tracker is created in draft: it measures nothing yet.

### Step 4: launch it

1. Call `start_tracker` with the returned `tracker_id`.
   - If it answers `insufficient_credits`, the balance does not cover one survey. Tell the
     person the cost per survey it returns (`cost_per_survey_minor`, in minor units) and
     hand them `top_up_url`, the page where they add credit: that address is in the
     answer you already have, so it reaches them whatever else you can call. When
     `topup_credits` is available to you, calling it for the amount they name returns a
     `payment_url` that opens straight on that amount. Nothing is charged either way:
     they approve it on the hosted page. Retry `start_tracker` once they have topped up.
2. Tell the person: the first survey runs the moment the tracker starts, whatever the
   frequency, and the series builds up week over week from there. Epovest measures whether
   and how often they are cited; the rest of the method acts on it, and the recipes below
   run it. Read the first results with `get_results` on the `tracker_id` rather than
   promising a delay: the survey is under way, and the page carries it as soon as it
   closes. On an `on_demand` tracker, say the other half too: that survey is the reading
   they asked for, and the next one comes when they ask for it.
3. When the person wants a reading now, call `survey_now` with the `tracker_id`: it runs
   one survey immediately, on top of the schedule. The anchor, the frequency and the date
   of the next scheduled survey all stay as they are, and the point joins the series marked
   `on_demand`. Use it when they have just shipped something and want to see it measured,
   or when they want an extra reading around a moment that matters. On a tracker whose
   frequency is `on_demand`, this is where every survey after the first one comes from.
   There is no cap on how many they can ask for: each one debits the balance like any
   survey, so name the cost (`cost_per_survey_minor`, which `list_trackers` carries) when
   they have not named the spend themselves. While one is still running, calling again
   answers that same survey, so read it back with `get_results` rather than asking for
   another. It answers `tracker_not_active` on a tracker that is not measuring yet:
   `start_tracker` first.
4. Later, `pause_tracker` stops the spend on the person's decision. That pause is
   manual: no top-up resumes it, only `start_tracker` does. A paused tracker still takes
   `update_tracker` in full, engines and frequency included: the call only writes, no
   survey runs and no credit is spent, and what you set takes effect when the person
   starts it again. So when they want the same tracker for less, set the new engines and
   frequency while it is paused, read `cost_per_survey_minor` back to confirm the new
   figure, and start it then. When a story is over, `archive_tracker` puts the tracker
   away (`{"archived": true}`, reversible with `false`): putting an active one away
   pauses it in the same call, the series stays readable, and starting it again is
   refused until it is taken back out.

## Recipe: read and explain the results

Goal: turn a tracker into a plain answer for the person.

1. Call `list_trackers` to find the `tracker_id` if you do not have it.
2. Call `get_results` with the `tracker_id`. Each row is one analyst, keyword, engine and
   survey period:
   - `keyword_presence` rows carry `citation_rate` (how often the name shows up) and
     `weighted_score`.
   - `share_of_voice` rows carry `share_of_voice` (the name's share against the other names
     tracked).
   - `sentiment` rows carry positive, neutral and negative counts.
   Narrow the read with the `analyst` or `engine` filter.
3. To show evidence, call `get_responses` with the `tracker_id` and one `engine`. Each
   answer is the real engine text with its cited sources and the keyword mentions, so the
   person can check a number for themselves. On a tracker that carries the sentiment
   analyst, `tone` reads one side of the count directly: `negative` brings back the very
   answers behind a negative number, so you quote what was said instead of paging through
   the week. Add `include_raw` only when you need the full payload; it is heavy. The pages
   clamp: stop paging when `page` reaches `pages`, never on "the list is not empty".
4. When you explain: read the trend across surveys, which is what the series is built for
   (a higher resolution sharpens it). Compare `citation_rate` across engines and `share_of_voice`
   against competitors. Point to a cited source when the person asks why a number moved.
   Read engine by engine and never as an average across engines: each one has its own
   habits and its own sources, so an average erases the very thing the person can act
   on. Being third on all four engines is a different situation from being first on two
   and absent from the other two, and the second calls for work the first does not.
5. When the person wants to track another name they keep seeing in the answers, or drop
   one, call `update_tracker` with the new `keywords` list: it applies in place and the
   series of the other keywords continues. Only the fields you send change; a sent list
   replaces the previous one, so carry over the keywords that stay.
6. When the tracker has discovery on, call `list_keyword_discoveries` once in a while:
   it lists the domains the engines keep citing that no keyword covers, and `dismissed`,
   the ones already set aside. Relay the suggestions to the person, then sort them in one
   pass: `accept_keyword_discovery` and `dismiss_keyword_discovery` each take a single
   `domain` or a whole `domains` list, so their yes-and-no over a dozen names is two calls,
   not two dozen. Changed their mind on one they dropped? `restore_keyword_discovery` puts
   it back in play.

## Recipe: sound several markets before entering one

Goal: tell the person which of several candidate markets has room for them, on evidence
instead of intuition, and leave the one they pick with a dated baseline.

This is the recipe where the unit is the TRACKER and not the keyword: what gets compared
is markets, so each candidate needs its own project and its own tracker, and they are all
measured together.

1. One project per candidate (`create_project`, named after the market) and one tracker
   inside it (`create_tracker` with its `project_id`). Nothing of the person goes in: no
   canon to state, no page to align, no keyword at all. They hold nothing on this ground
   yet, which is what makes the study cheap to run and quick to set up.
2. The same three buying questions on every candidate, only the trade changing, and the
   same `engines`, `frequency` (`monthly`) and `resolution` (`hd`) everywhere. Vary
   anything else between candidates and the comparison measures the settings rather than
   the markets.
3. Start them the same day: `start_tracker` on each, then `survey_now` on each so the
   first readings land together instead of on the anchor of each tracker. Name the cost
   before firing: each survey debits like any other (`cost_per_survey_minor`, which
   `list_trackers` carries), and a wave is that figure times the number of candidates.
4. Read the first wave with `get_responses`, market by market. This pass NAMES: the
   answers say who is installed, in the order the engines reach for them, and they name
   providers no list would have handed the person. Report a roster per market.
5. Then count. Call `update_tracker` on each with the recurring names as `keywords`, add
   the `keyword_presence` analyst, and set `discovery` to `true` so the domains that keep
   coming back get suggested. Discovery needs at least one keyword to hold, which is
   exactly why it comes now and not at step 1. Keywords and analysts apply in place: no
   new version, the series continues.
6. Four readings decide, in this order, on every candidate:
   - do the answers name anyone at all? A market where the engines explain a category
     without naming a provider has its citation slot open, and it is the strongest thing
     this study finds;
   - how many distinct names come back, counted over the whole survey and never over one
     answer: four names returning every time is a settled market, fifteen different ones
     is a market still sorting itself out;
   - how concentrated the mentions are, engine by engine and never as an average
     (`get_results`, the `share_of_voice` rows, once the names are tracked): a market can
     be held on one engine and open on the next;
   - whose pages the answers rest on: `list_sources` scoped with the `project_id` of that
     market, sorted on one engine. Answers built on the providers' own sites describe a
     market entered by publishing; answers built on independent third parties describe one
     entered through those third parties, and the map names them one by one.
7. Run the wave again a month later before deciding: a second point adds the movement to
   the picture, which markets are filling up and which are standing still.
8. Then the decision, and the instrument survives it. On the market the person enters,
   leave the tracker running: it already holds the answers as they stood before they
   existed, the dated zero a launch gets measured from. On the ones they set aside,
   `pause_tracker` stops the spend and `archive_project` puts the file away
   (`{"archived": true}`, reversible), record intact for the day the question comes back.
   Date the decision itself with `create_logbook_entry` in the project they kept, with
   what made it: six months later the series reads with its reason in front of it.

## Recipe: keep the official pages aligned

Goal: make every page the person controls tell the same story as the canon.

The `status` of a surface DERIVES from its checklist: nobody declares a page aligned,
the cells prove it. The checklist of a page holds two things: the template of its type,
and the checks the person added of their own (`add_surface_check`). `checklist.required`
lists exactly what holds the status, and it is the list to read: the `canon` items of
the template, which restate the canon (a bio, a JSON-LD block, the contact details), plus
EVERY check of the person. Verifying an item that restates the canon stamps the canon
wording of the moment, so the verification stays fresh until the wording moves and
perishes when it does; `presence` items of the template (a link, a pinned post) guide
without aligning. When the wording moves, `get_logbook` names what moved: the
`canon_version` item carries `changed`, the canon keys that revision touched
(`perks`, `one_liner`, `website`...). Read it before a re-propagation and work the
pages that restate those fields. `get_canon` completes the picture on that project:
the current wording to put in place, and the wording each revision replaced, which is
the string still sitting on a page that has not been brought in line.
A cell that does not apply on THIS page (an author bio, a field the
platform does not offer) is SET ASIDE with a short reason: it leaves the denominator
without being hidden. Setting aside the LAST canon cell is refused (`not_a_surface`): a page that carries
none of the canon is not a surface, so turn it into a corroboration if someone else
has the final say on it, or take it out of the registry. The
surface is `aligned` when every required cell not set aside is verified at the current
wording; verifying the last one dates the event in the journal on its own. The aim is a
surface with no required cell left undecided: work each one to a verdict, verified when the
page carries it, or set aside with its reason when the item genuinely does not
apply here. Leaving a cell untouched is not a neutral skip; it holds the page in
`needs_update` and hides whether the page was ever checked. The steps
below are a list of moves, not an order to follow.

1. Call `list_surfaces` with the `project_id`: the registry of the pages about the
   brand where the person has the final say (their site, their profiles, their
   listings). The test that settles which registry a page belongs to: if they change
   the page, does the change stay? Yes is a surface, no is a corroboration, and being
   able to edit is not the test, since a wiki anyone edits is a corroboration while a
   directory listing they hold is a surface. Each line carries a derived `status`: `aligned` when every canon cell holds at
   the current wording, `needs_update` when a verification is missing or has
   perished, `never_aligned` when none exists. Each line also carries
   `domain_authority`, what the AIs grant the DOMAIN it sits on, read from the
   Atlas: `engines` gives the AI Authority per AI over the last 30 days, `domain`
   names what was measured, and `source_id` opens its entry on the map. The unit is
   the domain, so it says that PLACE is read; an AI absent from `engines` has not
   cited it lately, which is not a zero, and a `source_id` of `null` says the domain
   is not on the map of this account, which grows as the account measures more.
   Order the registry by it with `sort` set to `authority`, the most read first:
   that is how you tell the person which page pays back the work, instead of taking
   them in the order of the binder. It orders and never filters, so nothing
   disappears, and a page on a quiet domain is sometimes exactly the one to write.
2. To register a page, call `create_surface` with the `url` alone: the type and the
   label derive from the address, so only send them to correct a derivation. The type
   follows the canon, not a guess: a page on the canon website is `website`, a known
   place carries its own kind, and any other host is `other` with the generic
   checklist. Add `languages` when the page exists in several.
3. Work a page with its `checklist`: the model of what to bring in line for that type of
   page, plus the checks the person added of their own (step 5). Once they have done a
   pass, consign it with `tick_surface_checklist`:
   send only the cells constated (`item`, plus the `language` column when the surface
   has several; a site-scoped item such as `llms_txt` is one cell, without language).
   Verifying a canon cell needs the canon set first, since it stamps the current
   wording. An item that does not apply on this page is set aside with `dismissed`
   true and a short `reason`, the person's call to make; `dismissed` false brings it
   back. Go through the whole checklist, not only the cells easy to confirm: each item
   is either verified or set aside, so none stays blank by omission. The lot is all or
   nothing, and a refused move names what exists. Verifying the last canon cell aligns
   the surface on its own, journal line included.
4. There is no tool that declares a page aligned, and nothing is missing: the status is
   EARNED through step 3, and one call carries as many cells as you want. So a page
   brought fully in phase is recorded by listing what you verified on it, which is both
   faster to write and worth more later, since each cell keeps its date and its canon
   version. `no_canon` on a canon cell means the project has no canon yet: state it
   first (the brand file recipe). Clearing a cell is the same call with `done: false`.
5. When the person states a requirement of their own on a page, put it IN the checklist
   with `add_surface_check` rather than leaving it in the conversation: the pricing block
   quotes the canon boilerplate, the OG image is the current one, the footer carries the
   legal name. The check joins the checklist of that page, becomes REQUIRED for it to
   count as aligned, and ticks with `tick_surface_checklist` at the key returned. One
   judgement to make, `restates_canon`: does the tick become FALSE when the wording of
   the canon changes? True for a check that restates the canon, and it will perish with
   the wording; false, the default, for anything else, and the tick then holds. A check
   whose requirement no longer applies leaves with `delete_surface_check`, keeping its
   ticks, and `restore_surface_check` brings both back; `update_surface_check` corrects a
   label without losing a tick. Read them back in `checklist.custom`.
6. Keep the sheet right with `update_surface`: only the fields you send change, and a
   sent `languages` list replaces the previous one. Languages are the columns of the
   checklist, so declaring the language of a single-language page carries its verified
   cells along, and any column that leaves the model comes back in
   `dropped_checklist_columns` with what it held, its cells kept for the day that
   language is declared again. Read the `status` of the answer rather than assuming the
   one you had: it derives from the cells that read now.
7. A page that no longer belongs to the working list is removed with `delete_surface`:
   it leaves the registry and every count, sheet and journal kept. The removed lines
   read back with `list_surfaces` and `deleted: "only"`, and `restore_surface` brings
   one back. A page whose final say turns out to belong to someone else converts with
   `convert_surface_to_corroboration`: the line changes register instead of being
   retyped.

## Recipe: build and watch the proof on pages the person does not control

Goal: record where third parties talk about the brand, keep those mentions watched, and
grow the list from what the engines already show.

1. Call `list_corroborations` with the `project_id`: the pages about the brand where
   someone else has the final say (an article, a directory, a review, a comparison).
   The frontier with surfaces is control, never who wrote the page or who paid for it.
2. To record one, call `create_corroboration` with the `url` of the exact page, never
   the home page of the site. The label and the source domain derive from the address.
   Add `published_on` only when the person knows the date: it is never guessed. Three
   named refusals: `unplaceable_url` (no usable domain), `own_domain` (the brand's own
   site belongs to the surfaces registry), `duplicate` (already recorded, nothing to
   retry).
3. The page is then read on its own: the excerpt around the brand is archived, and the
   links to the brand's addresses are read in the same pass. Call
   `verify_corroboration` to run that reading now; it is included, no credit is
   debited. The answer carries the finding on the mention (`unchanged`, `changed`,
   `figures` when only numbers moved in the passage, such as a rank or a counter,
   `gone`) and on the link, and `readability` names the cause when the text did not
   come: `blocked` (an anti-bot stands in front of the page), `unreachable` (the page
   did not answer: worth a retry), `no_page`, `unreadable`. On a `changed`, say what
   moved: `excerpt_before` carries the passage as it stood before `changed_at`, so
   compare it with `excerpt` and report the difference in words rather than sending
   the person back to the page.
4. To keep a line watched on a cadence, call `update_corroboration` with `monitoring`:
   `daily`, `weekly` or `monthly`, and `off` to stop. Each executed check is billed
   (the grid: https://epovest.com/en/pricing), so confirm the cadence with the
   person before arming. Arming refuses on an archived line
   (`corroboration_archived`) and on a page that does not read (`page_not_readable`);
   a line just recorded is still being read, so verify first, arm after. `off` always
   passes.
5. Grow the list from `list_corroboration_candidates`: the pages where an engine
   showed the brand in its own extract. `matched_by` says the strength of the proof
   (`website` is near-proof; `name` alone is weaker, homonyms exist) and `page_check`
   what the page itself said when it was read. Accepting has no tool of its own: it is
   `create_corroboration` with the address when someone else has the final say, and
   `create_surface` when the person has it, which `suggested` proposes and never
   decides. On `suggested: "ask"` the address alone does not settle it, so put the test
   to the person in their own terms: if they change that page, does the change stay?
   Yes is a surface, no is a corroboration. Ask it rather than reading it off the host,
   because two pages of one host differ: a product listing on a software directory is
   held by the vendor, the comparison page beside it is not.
   `dismiss_corroboration_candidate` sets a page aside for good. An empty list means
   the recent surveys surfaced nothing to sort, never that nobody mentions the brand.
6. When a page is taken down, `archive_corroboration` says the state
   (`{"archived": true}`, reversible with `false`): the line and its history stay, and
   it stops counting as a presence. When the person takes the final say over the page,
   `convert_corroboration_to_surface` moves it to the surfaces registry, sheet intact.

## Recipe: read the map of sources

Goal: tell the person which sources the engines listen to, and where a mention would
carry.

1. Call `list_sources`: the Atlas, the sources the engines cited across the surveys
   the account paid for. Each row carries the domain and, per engine, `aa`: the AI
   Authority, 0 to 100, what that source weighs for that engine over the last 30 days.
   An engine absent from a row has not cited it recently, which is not a zero.
2. Sort on one engine with `aa_chatgpt`, `aa_claude`, `aa_gemini`, `aa_perplexity`, `aa_mistral` or `aa_grok`:
   the list restricts to the sources that engine cites, the strongest first. Filter
   with `domain` (contains) or scope with `project_id`.
3. What you look for depends on the objective the tracker serves:
   - **Ranking**: take the engine where the person's citation rate is weakest and sort
     by its authority. That list is what that engine reads. A source at the top where no
     corroboration mentions the person is the priority, a directory to be listed in, a
     comparison to enter, a publication to talk to. This is the concrete way share of
     voice is won, one source at a time.
   - **Reputation**: call `get_responses` with `tone` set to `negative` to bring back the
     answers that describe the person badly, read the sources cited next to them, and check
     whether a page of theirs sits on those domains. Fixing what an engine repeats means
     reaching the source it repeats it from.
   - **Watch**: the map is a deliverable in itself, the working bibliography of the
     topic. Sort by freshness to see the sources entering, since a domain that surges
     often announces a story that is changing, and compare the maps of two engines on
     the same question.
   - **Market study**: scope the map with the `project_id` of each candidate market and
     compare the maps rather than the rows. Who the top sources BELONG to is the reading:
     the providers themselves, or third parties that sell nothing. The first describes a
     market entered by publishing, the second a market entered through those third
     parties, and the names of the second are the outreach list.
4. Cross the map with the registers: a strong source where the brand has no recorded
   corroboration is where a mention would carry the most. Relay those names to the
   person, and once they obtain the mention, record its page with the proof recipe
   above. The surface registry carries the same crossing already made: every line of
   `list_surfaces` comes with the `domain_authority` of its own domain, and `sort`
   set to `authority` puts the pages posted where the AIs read first. Use it to
   answer the question a registry alone cannot: of the pages the person controls,
   which one is worth an afternoon.
5. `get_source` reads one source by id, same payload as the list.
6. On a source where several people publish under one domain, go a level down with
   `list_source_channels`: it names the channels that published the videos the AIs cited
   on the person's own questions, with the questions that surfaced each one. This is the
   step where a row of the map turns into names to contact, since a domain cannot be
   written to and a channel can. Read the order rather than the volume: the ranking is how
   many of the person's questions each channel came back on, and a channel that answers
   two of them with a single well-titled video is worth more of the person's time than a
   busy one cited twice on the same question. `you_are_there` tells you which of those
   names the person has already obtained a mention from, so relay the others; and once a
   mention is obtained, record its page with the proof recipe above. The answer also says
   what it is drawn from (`videos`, `named`, `unnamed`, `pending`), so tell the person the
   figure you read it on rather than presenting the list as the whole of a market.

## Recipe: find the places that corroborate the competitors

Goal: turn a handful of competitor names into the list of places to go and get
corroborated, and into quests the person can work through.

The competitor is not the subject here, it is the instrument. Asking an engine about a
company makes it go and cite the pages that talk about that company, and those pages are
the places that carry weight on that market. The list cannot be read anywhere: it is
provoked. It pays off best on a trade vertical, where the places are specialised sites
nobody names from memory.

1. Compose the basket with the person: up to 5 companies of the same market, each with
   its `website`. Three or more is what the ranking needs, since a place is ranked by how
   many of the basket it covers; one company alone still measures, and its places then
   rank by AI Authority. The site is required, and it is what tells two companies with the same
   name apart. Agree on the `category` that qualifies them in the questions ("welding
   equipment manufacturer") and the `usage` buyers buy for ("hobby welding"): both shape
   what is asked, so take them from the market being measured rather than from a guess.
2. Call `create_competitor_scan`. It comes back as a DRAFT, nothing charged, with the
   questions it derived. Read them back to the person: they are what will be asked, and
   the wording is where this measurement is won or lost. Ask them in the language of the
   market being measured, `language`, not in the one you are talking in.
3. Correct them while it is still a draft, with `update_competitor_scan` and `questions`:
   send the ones to KEEP, with the wording to ask. One left out is removed, and
   `estimated_cost_minor` follows, so this is also how someone measures the same market
   for less. Two rules carry the wording: a question is a SENTENCE and never keywords,
   and it names the company AND what qualifies it, since a question without that anchor
   answers about another market.
4. Name `estimated_cost_minor` to the person and get their go-ahead, then call
   `start_competitor_scan`. It spends. The answers land within a few minutes.
5. Call `get_competitor_scan` and read `places`, top first. `subjects` is the number that
   carries the whole thing: how many of the basket that place corroborates. A place cited
   for four competitors out of four is a crossroads of the market; a place cited for one
   is an article. `reach` says by which move the person takes their spot there:
   `self_serve` to open their own page, `participate` for a forum or a community, `ask`
   for a third-party editorial site, `registry` when the page follows an official filing.
   `client_present` says whether the account already recorded a corroboration there.
6. Turn the top of the list into work: for each place where `client_present` is false,
   add a quest with `create_quest` carrying its URL in the notes, worded as the move its
   `reach` calls for. That is what makes the list a workday rather than a curiosity, and
   the quest file is where the person resumes.
7. Offer the cadence: `update_competitor_scan` with `rescan_cadence` set to `weekly` or
   `monthly` runs it again on that cadence, and each pass WIDENS the list instead of
   refreshing it, because two checks of the same question do not cite the same pages.
   Weekly widens it faster and spends as often; monthly follows the pace a corpus of
   third-party pages moves at. It spends the same amount every pass, so name both the
   amount and the cadence before setting it. `seen_in_checks` then tells the steady
   places from the one-off finds.
8. Once the person obtains a mention on one of those places, record its page with the
   proof recipe above: `create_corroboration` with the exact address.

`list_competitor_scans` brings back the scans the account already holds, newest first,
with the state of each one: start there when the person asks about a market they have
already sounded, rather than paying for a basket they already have.

## Recipe: run the workday

Goal: open the file of what is left to do, act on it, and leave a dated trace on the
move it advances.

1. Call `list_quests`: the open file, all projects by default. `quests` is what the
   person and their agents posed; `pending` is what the measurement derives on its
   own: `surfaces_to_align` (pages that no longer carry the current canon),
   `corroborations_to_refresh` (proof older than the current canon, worth asking the
   third party for a refresh), `corroborations_to_reread` (the last page reading found
   something to say), `keyword_discoveries` (domains waiting for a decision), and,
   scoped to one project, `corroboration_candidates`. A derived line disappears by
   itself once the native gesture is done; absent from the account-wide view, the
   candidates are not examined, which is never a zero.
2. Pose what the person decides with `create_quest` (`project_id`, `title`, optional
   `notes`). Posting the same open title twice returns the quest already there:
   replaying is safe.
3. Close with `complete_quest` (the work happened) or `dismiss_quest` (the person
   renounces). Both say a state and replay safely; `reopen_quest` is the inverse
   gesture, and `update_quest` fixes a title or notes without touching the state.
   Before working a quest, read what was already tried on it: `journal_entries` on
   the quest says how many actions were recorded against it, `last_entry_at` when
   the last one happened, and `get_logbook` with that `quest_id` reads them, newest
   first. A quest whose trail says the directory was written to twice this month
   calls for a different move than one nobody has touched.
4. Call `get_logbook` for the composed journal of the project: what the method records
   on its own (canon versions, alignment markings, corroborations and their removals)
   next to what was noted by hand. A `canon_version` item carries `changed`, the canon
   keys that revision touched, which is what a re-propagation is worked from.
   `deleted: "only"` reads its trash.
5. Every action worth reading against the curves gets an entry: `create_logbook_entry`
   with a `category` (content, technical, translation, canon, press, other), a short
   `label`, and `occurred_at`, the date of the action: a retroactive date is the
   normal case, and replaying the same entry returns the one already consigned, never
   a second copy. When the action moves a quest forward, pass its `quest_id` as well:
   the entry stays one entry of the logbook of the project, and it also becomes the
   dated trail of that quest, which is what the next session reads before deciding
   the next move. Record the step that was actually taken, on the day it was taken,
   rather than one entry at the end: "sent the listing form" and "they asked for our
   figures" are two moments of the same quest, and the dates between them are the
   information.
6. `update_logbook_entry` corrects a note, and files an entry under a quest after the
   fact with `quest_id` (or back under the project alone with `null`).
   `delete_logbook_entry` moves an entry to the trash on the person's request, and
   `restore_logbook_entry` brings it back.

## Recipe: keep the balance funded

Goal: unblock a refused start, or top up before the balance runs dry.

1. Call `get_credits`. Compare `available_minor` with the `cost_per_survey_minor` of the
   trackers to run (`list_trackers` carries it, with a `monthly_estimate_minor`). `held_minor`
   is not missing money: it is reserved by surveys in progress and settles when they
   complete. The same answer carries `monthly_estimate_minor` for the whole account and
   `suggested_topups[]`, three amounts derived from that pace: you do not have to add up
   trackers to know what a top-up is worth here.
2. Call `topup_credits` with the amount. Two ways in, and the person's own words come first:
   - They named an amount ("top me up 50"): pass `amount`, in major units, exactly as they
     said it. 50 means 50.00. Never convert it in your head.
   - They left it to you: take one of the `suggested_topups[]` and pass its `amount_minor`.
     Each one says the runway it buys in `covers_months`, so name the one you picked and
     what it covers ("250, about three months at your current pace"). When the list is
     empty, nothing is set up to consume yet: ask what they want to measure rather than
     picking a figure for them.

   Send one of the two, not both. The minimum top-up is 10.00, and `min_topup_minor` says
   it in the same answer.
3. Hand the returned `payment_url` to the person: the payment happens on that hosted page,
   never in this conversation, and nothing is charged until they approve it there. The
   response echoes `amount` and `currency`: confirm those to them before they click.
4. Once paid, everything follows on its own: the balance updates and the trackers that ran out
   of credit resume. Call `get_credits` again to confirm. A tracker the person paused on
   purpose stays paused: only `start_tracker` brings that one back.

Credits never expire, and reading stays open however long an account sits idle: a person who
comes back after two years reads their whole series, and spends the balance they already have.

## Recipe: read the spend

Goal: answer "what is this costing" with the right figure.

1. Call `get_usage`. It answers three things that must not be confused:
   `this_month_forecast` is a forecast of the current month (already debited plus what
   the current configuration will consume by its end); `by_month` is the actual spend,
   month by month; and the window totals (`total_spent_minor` with its breakdowns) are
   actuals bounded by `period`. Name which of the three you are giving, and restate
   the period with the figure: serving a forecast for a spend is the wrong number.
2. The spend breaks down by line of work (`survey`, `ai_analyst`, `competitor_scan`,
   `corroboration_check`, `other`) and by tracker (`by_tracker`, the biggest spend
   first): that is the grain where the person decides what to adjust. A competitor
   scan carries its own line and stays out of `by_tracker`, so quote it by its line.
   `get_credits` says what remains; `get_usage` says where it went.

## Recipe: keep the details of the account right

Goal: set the language Epovest writes in, or fix the details that go on the invoices.

1. Call `get_account_settings` first. It answers with every setting as it stands: the legal
   `name`, the `billing_country`, `billing_address` and `intra_eu_vat_number` printed on the
   invoices, and the `language` and `timezone`. `member` names the person the language and
   the time zone belong to, so you can say whose preference you are about to change.
2. Call `update_account_settings` with only what the person is changing. Everything you
   leave out keeps its value, so there is nothing to resend.
   - "Put my account in French": `language: "fr"`. It sets the language of the emails
     Epovest sends and of the hosted payment page of a top-up.
   - "We moved": send `billing_address`, one string per line, together with
     `billing_country`. The address and the VAT number always travel with the country.
   - `null` clears a billing field. The legal name, the language and the time zone always
     hold a value, so name the one you want rather than clearing it.
3. Read the answer back to the person: it carries the settings as they now stand. The new
   details apply to the invoices issued from then on, and the ones already issued keep the
   details they carry.

Writing the billing identity takes an owner on a one-click connection. The language and the
time zone are each person's own, so a manager sets theirs.

## Recipe: when a tool refuses, or the person wants to reach us

Goal: get a human on the line without leaving the conversation.

1. When a tool refuses what should work, when something seems missing, or when the
   person asks to tell us something: call `contact_support` with the `message`, and a
   `kind` (`problem`, `suggestion` or `question`). It opens a thread a human answers.
2. Reply in the same thread by passing its `thread_id`; read the answers back with
   `get_support_thread`, and find past threads with `list_support_threads`.
3. The person finds the same threads in their app: nothing said here is lost.

## What stays in the app

An agent does not pay, add a payment method, set up auto-reload, manage members or touch
sign-in security; send the person to the app (https://app.epovest.com) for those. Signing in is
set up there too, where the person confirms their email address, their password and
their second factor. Everything else lives in the tools: the brand file, trackers from
draft to archive, surveys run on demand, competitor scans from draft to a weekly or
monthly cadence,
surfaces, corroborations and their watch, the
Atlas, the quest file, the logbook, the spend, and top-ups the person approves. Mind the versioning boundary of
`update_tracker`: changing what is collected (prompts, engines, frequency, resolution)
on a tracker that has measured opens a new version (the series continues under a new id:
follow it), while keywords, analysts, the title and the project change in place. Prices
and the cost grid are published at https://epovest.com/en/pricing.
