# API keys Source: https://docs.attensira.com/account/api-keys Mint an Attensira API key, choose its scope, store the one-time reveal, and revoke it when you are done. An Attensira API key authenticates a program — an MCP client, a script, an agent — as your organisation. You mint it in the app, copy it once, and send it as a bearer token. A key names one workspace as its default, and every tool and endpoint takes an optional `project` to reach any of the others. One key reaches both programmatic surfaces: the [MCP server](/mcp/overview) and the [REST API](/api/overview). You do not need a second credential to add the other, and that has not changed — the MCP server accepts keys permanently, alongside OAuth. ## When a key, and when OAuth The MCP server takes either. The REST API takes a key only. Use a **key** when nothing can open a browser: an n8n workflow that fires at 3am, a CI job, a scheduled script, a server-side agent, anything calling the REST API. Keys do not expire, so unattended work keeps running without anyone re-approving it. Use **OAuth** when a human is present at first connect — Claude, Claude Code, Cursor, VS Code, ChatGPT. It is nicer there for three reasons: no secret ever lands in a config file you might commit, access tokens are short-lived so a stolen one is worth little, and each client is its own connection you can remove without breaking the rest. Use a **key** when nothing can open a browser, or when you need access you can cut off instantly: a revoked key stops working on the very next request, while a token already issued survives until it expires. Neither is deprecated and there is no cutover. See [Connect a client](/mcp/connect) for both paths and [Connections](/account/connections) for managing OAuth grants. ## Mint a key Go to [app.attensira.com/settings/access](https://app.attensira.com/settings/access) in the workspace the key should belong to. If you work across several workspaces, check the workspace name before you continue — the key you mint here cannot be moved to another one later. Give it a name that says where it will run, such as `claude-desktop-laptop` or `ci-nightly-report`. The name is the only thing you will see in the list afterwards, so a vague name makes revoking the right key harder. Under **Access**, pick **Read only**, or **Read and write**. See below for what each allows. The full key is shown once, on this screen, and never again. Paste it into your password manager or your client's configuration before you navigate away. ## Choosing a scope **Read** lets a key retrieve what already exists: metrics, prompts, pages, sessions, account details, and the list of automations. It cannot change anything in the workspace. **Read and write** adds the actions that alter state or spend credits: adding prompts and competitors, creating and running automations, removing prompts, deleting automations, and asking the agent. Give a key the narrower scope unless it needs the wider one. A dashboard, a reporting script, or a read-only agent integration should be read-only — then a leaked key cannot delete an automation or burn credits. There is no equivalent split over OAuth: a signed-in connection always carries read and write, so a key is the only credential that can be made read-only. See [the tool reference](/mcp/tools) for which tools need write, and [REST API authentication](/api/authentication) for the equivalent over HTTP. `remove_prompt` and `delete_automation` are destructive and are reachable with a read-and-write key. There is no separate confirmation step at the API level. ## The key format A key looks like this: ```text theme={null} atn_live_0123456789abcdef0123456789abcdef ``` That is the prefix `atn_live_` followed by exactly 32 hexadecimal characters. Both the MCP server and the REST API check the token against `^atn_live_[0-9a-fA-F]{32}$` before making any upstream call, so a truncated paste or a stray space fails immediately with a `401` carrying `WWW-Authenticate: Bearer`. The MCP server answers in the OAuth error shape, which is the same for a malformed key and an unusable OAuth token: ```json MCP 401 theme={null} {"error":"invalid_token","error_description":"The access token is invalid or expired"} ``` The REST API answers in its standard [error envelope](/api/errors): ```json REST 401 theme={null} {"error":{"type":"unauthorized","message":"missing or malformed Authorization header. Send: Authorization: Bearer atn_live_<32 hex>","status":401}} ``` That shape check is not authentication. Whichever surface you use, the token is forwarded to Attensira, which owns the real validation — hash lookup, revocation, expiry, scopes and rate limiting. Send it as a bearer token: ```http theme={null} Authorization: Bearer atn_live_0123456789abcdef0123456789abcdef ``` ## Why you only see it once We store a hash of your key, not the key. When a request arrives, we hash the token you sent and compare hashes. That means the plaintext value exists in our systems only during the moment you mint it. The consequence is not a policy choice you can appeal: we **cannot** show you the key again, and we **cannot** recover it for you. If you lose it, no one at Attensira can read it back. Revoke the lost key and mint a new one. Before closing the reveal screen, paste the key somewhere durable and confirm you can read it back. That is the only opportunity. ## Revoking a key Revoke from the same Settings > Your assistant screen. Revocation takes effect for new requests; anything already using that key starts failing authentication. OAuth connections are not keys and are not listed there as rows: how to remove one is in [Connections](/account/connections), and revoking a key never touches them. Nothing else in the workspace changes — data collected through the key stays, and automations it created keep running on their schedules. Revoke when someone leaves, when a key has been pasted anywhere it should not have been, when a machine is decommissioned, or whenever you cannot say with confidence where a key is stored. Minting a replacement costs nothing, so rotate rather than wonder. ## One key, every workspace in the organisation A key belongs to your organisation and names one workspace as its default. Every MCP tool that works on a workspace takes an optional `project`, and every REST endpoint an optional `?project=`, so a single key reaches every workspace in the organisation — running three domains does not mean minting three keys. Omit it and the default is used. An OAuth connection works the same way, and reaches the same set. What cannot be widened is the organisation. A workspace outside it is a `404`, the same answer as one that never existed, and no scope or setting changes that. `get_account` lists what a credential can actually reach. Next: [Connect over MCP](/mcp/connect), or [the REST API](/api/overview) if your own code is doing the work. # Connected applications Source: https://docs.attensira.com/account/connections How signing an AI client in to Attensira works, what it can reach, and how to disconnect it. When you connect an AI client to the [MCP server](/mcp/overview) by signing in, the approval you give creates a **connection**: one application, one organisation, one set of scopes. Everything about connecting your assistant is on one page: **Settings → Your assistant**, at [app.attensira.com/settings/access](https://app.attensira.com/settings/access). It has the steps for your assistant, the **Connected clients** section that explains what a connection can reach and how to take it away, and your API keys. Static `atn_live_` keys are not connections; they are minted and revoked in the same place. See [API keys](/account/api-keys). ## GitHub is not on this list [GitHub](/agent/github) is a first-party **GitHub App** that you install into your organisation or account. It is not an OAuth connection and it is not brokered through a third-party connector, so it does not appear on this page and cannot be revoked from it. Manage it from the project's GitHub settings, or from GitHub's own installation page. Disconnecting an AI client here has no effect on the App, and uninstalling the App has no effect on anything here. ## Google Ads is not on this list either [Google Ads](/agent/google-ads) is connected as a connector on the workspace, where you also choose which Google Ads account it uses. It is not an AI-client connection, so it does not appear here, and disconnecting a client here does not disconnect it. A connection here never reaches your ad budget. Even with `Read and write`, an AI client cannot approve a Google Ads change that spends money or cannot be undone: that yes is given only by a person signed in to the Attensira app, and a client asking gets `403 Forbidden` with the reason `spend_approval_in_app`. See [Approval modes](/agent/approval#google-ads-spend). ## What a connection can reach A connection names your **organisation**, not a single workspace. Every MCP tool that works on a workspace takes an optional `project`, so one connection reaches every workspace the organisation owns — you do not connect a client once per domain. Omit it and your only workspace is used; when the organisation has several, there is no default and the client names one. `get_account` lists the ids, and answers without a workspace. A connection always carries read and write: there is no read-only consent, and write is the one that spends credits. Where you need a credential that cannot write, mint a read-only [API key](/account/api-keys) instead. Anyone can register a client and choose its display name. Nothing stops someone registering one called "Attensira Sync" and hoping the name is enough. Read the name and the requested scopes on the approval screen, and be sure about anything unfamiliar asking for write access. ## Disconnecting Remove the server in the client that holds it. In Claude Code that is `claude mcp remove attensira`; in Cursor or Codex it is the entry you pasted into the config file. That stops the client asking for anything further. An access token the client already holds keeps working until it expires, because the MCP server verifies tokens locally and never asks us whether one is still wanted. If you need access cut off **now** rather than at the next expiry, use a key rather than a sign-in: a key is revoked from Settings → Your assistant and stops working on the very next request. See [API keys](/account/api-keys). If a signed-in client may be compromised, contact [team@attensira.com](mailto:team@attensira.com) to have it cut off. Disconnect when a laptop is lost or decommissioned, when someone leaves, or when you were testing a client you no longer use. Reconnecting takes one browser prompt, so disconnecting first and asking later is the cheap direction. ## Reading the wrong workspace Approving a client fixes the organisation, never a workspace. If a client is reading the wrong workspace, ask it to name the right one rather than reconnecting — every tool takes an optional `project`, and `get_account` lists the ids. The organisation cannot be moved afterwards. To reach a different one, disconnect and sign in again. ## Seeing what a client did Anything a connected client files in your workspace is marked with its name, such as **via Claude**, and the Inbox gathers those rows under **From your assistant**. An approval it makes is recorded the same way in the item's history. The name is what the client calls itself, so it is a label and grants nothing. See [how your assistant's work is marked](/mcp/connect#marked-via-your-assistant). ## Reconnecting Connections do not last forever on their own. Access tokens are short-lived and the client refreshes them quietly; eventually the refresh runs out and the client asks you to sign in again. That is normal, takes one browser prompt, and nothing is lost — no configuration changes and no data goes away. If a client asks for consent far more often than that, it is not storing the refresh token it was given. See [MCP troubleshooting](/mcp/troubleshooting). Next: [OAuth reference](/mcp/oauth) for what happens underneath, or [Connect a client](/mcp/connect) to add another one. # Costs Source: https://docs.attensira.com/account/costs What the month has cost in dollars, where it went, how it reads against your budget, and what the budget committed us to deliver. The **Costs** page answers one question: what has this month cost, and where did it go. Open it from the spend gauge in the top bar of every screen. Everything on it is in **dollars**. A credit is one cent at list price, so \$12.40 on Costs is 1,240 credits anywhere that still counts in credits. The gauge, the Costs page, the banner under the composer, **Settings → Billing** and the CSV export all read the same figures, so none of them can disagree with another. A month here is a **UTC calendar month**: it starts at 00:00 UTC on the 1st. Billing, the [budget](/account/plans#your-monthly-budget) and the workspace and project caps all count the same month. ## The spend gauge The gauge sits at the top right of every screen and shows this month's spend against your budget. * **Below 80% of the budget** it is neutral: spend is a number, not an alarm. * **From 80%** it turns amber and says how much of the budget is used. * **When the budget is reached** (110% of it if you allow 10% over) it stays amber and says new work is paused. * **With no budget set** it shows the month's spend and **Set a budget**. It opens Costs in every state. Nothing is drawn until the month has been read, so you never see a \$0 that is only a page still loading. Under the composer, a banner appears when the month reaches 80% of the budget, with the month-end forecast and a link to Costs; you can dismiss it. When the budget is reached the banner says new work is paused, including the chat you were about to start, and offers **Raise budget**. ## What the page shows Choose **This month** or **Last month** at the top. **Export CSV** downloads the month, and **Top up** adds to your balance. ### The four numbers | Tile | What it is | | - | - | | **Spent this month** | Everything charged this month, less refunds, against your budget when one is set | | **Forecast** | Where the month is heading at its current pace. See [How the forecast works](#how-the-forecast-works) | | **Plan** | Your plan's monthly fee. It is separate from the budget and is not counted in spend | | **Balance** | What is left to pay for work as it runs. Marked **Low** when it is nearly empty, or when it covers fewer than three days of this month's pace and auto top-up is off | When a budget is set but nothing can fund it, a line under the tiles says so: **Add a card so your budget can fund work** when there is no card on file, or that auto top-up is off. Until then, work stops when the balance runs out, before the budget does. ### By project Each project's spend this month, its share of the total, and its own monthly cap if it has one. A project is a piece of work inside a workspace with its own crew of agents; the API calls it a work area (`work_area_id`). A project whose new work is stopped for money (the budget, its workspace's cap or its own cap) is marked **Paused**. * **Unassigned** is a workspace's spend that no project was named for: chats and the Chief of staff. * **Removed project** is spend from a project that has since been deleted. The money was still spent. ### By kind of work Spend by what the work produced, with how many were delivered this month: | Kind | Delivered counts | | - | - | | Articles | When the article is completed | | Page fixes | When the fix is completed | | Outreach pitches | When the pitch is sent | | Listing fixes | **not tracked yet** | | Monitoring day, and Other (spend on no particular deliverable) | **not tracked yet**: these are not counted as deliverables | **not tracked yet** means we do not count that kind of work. It never means zero delivered. Below the table, **Where it went** splits the month by where the money was spent: | Line | Spend | | - | - | | Agent work | Agent runs: chats, automations, `ask`, and the tools they call | | Data and monitoring | Reads nobody asked an agent for: monitoring's reads of your own pages, and background crawls | | Not yet attributed | A charge we could not yet place | A line with nothing on it this month is left out. ### By workspace Shown when the month's spend falls on more than one line: several workspaces, as an agency has, or one workspace beside spend that belongs to none. Each workspace's spend and its monthly cap. * **Across workspaces** is spend that belongs to the organisation rather than to one workspace, such as background crawls. * **Removed workspace** is spend from a workspace that has since been deleted. ### Committed vs delivered When a budget is set, it commits us to a volume of work for the month, and this table holds us to it. For each kind of work: | Column | Meaning | | - | - | | **Committed** | What the budget committed us to deliver this month. **not tracked yet** for a kind we cannot count, and nothing is committed for it | | **Delivered** | Delivered this month | | **Waiting on you** | Finished on our side and waiting for you to put live: an article to paste, a Notion page to publish, a pull request to merge, a pitch drafted into your mailbox. It counts as delivered | | **Not charged** | The credit for committed work we did not deliver. For the current month, what it would be if the month closed now | How the credit is worked out is on [Plans](/account/plans#if-we-deliver-less-than-the-budget-committed). ### Ad spend Ad spend is shown on its own line under the tables. It is paid to the ad platform directly, so it is **never part of your spend, your budget, any share bar or any total** on this page. With no ad account connected, the line reads **Not connected**. ## How the forecast works The forecast is a straight line you can check by hand: the month's spend so far, divided by the days elapsed, times the days in the month. * It is blank for the **first three days** of a month. Too few days make a line a guess. * For a month that is over, the forecast is what the month cost. ## Caps A budget bounds everything the organisation spends in a month. Below it, two caps stop one part of the account from spending another's share: | Cap | Bounds | Set in | | - | - | - | | **Budget** | The whole organisation, every workspace | **Settings → Billing** | | **Workspace cap** | One workspace, which is one client for an agency | **Settings → Billing** | | **Project cap** | One project inside a workspace | **Settings → Billing** | Every cap is a monthly dollar amount, and a cap of zero means no cap. A workspace cap is the same value as the workspace's [monthly credit cap](/account/credits#monthly-credit-cap), shown in dollars rather than credits. A cap counts everything charged to its scope in the month: agent work and the data reads it pays for. Daily prompt readings use your plan's response budget, not credits, and are not counted. Only an organisation admin can change the budget or a cap, from the dashboard. An agent cannot change them, and neither can an API key or an MCP connection: an agent that needs more room has to ask you. ## When a cap or the budget is reached New work is checked against each wall before it starts, never while it runs. In order: 1. **The balance.** At zero, nothing new starts. Top up, or turn on auto top-up. 2. **The budget.** Once the month's spend reaches the budget, nothing new starts until you raise the budget or the month turns. This holds chats and `ask` as well as automations. 3. **The project's cap.** New work for that project stops, chats filed under it included; other projects carry on. 4. **The workspace's cap.** Automation runs in that workspace stop, whether scheduled or started by hand. Chats and `ask` are not held by it. A run already under way always finishes and is charged in full. So a month can pass a budget or a cap by at most one run, and runs that start at the same moment can each pass on the same reading. When scheduled work is stopped, the workspace gets one row in the [Inbox](/agent/inbox) for that wall, and one email. The budget's row and a project's row carry a **Raise cap** button that opens the setting. The row closes itself when the work runs again. Topping up does not lift the budget or a cap; raising it does. The full list is on [Credits](/account/credits#what-stops-new-work). ### Pausing at the budget Under **Settings → Billing → When the budget runs out**, choose what happens at the budget: * **Pause and ask me** (the default): new work stops at the budget. * **Allow 10% over**: new work keeps starting until spend reaches 110% of the budget, then stops. This changes where new work stops. It does not change the commitment, which is always worked out from the budget itself. ## Export **Export CSV** downloads the month as one spreadsheet built from the same figures as the page. Each row names its section in the `section` column: `total`, `by_project`, `by_workspace`, `by_kind`, `by_type`, `committed`, `credited_back` or `ad_spend`. Amounts are in whole cents (`spent_cents`) and in dollars (`spent_usd`). The `spent_cents` of each spend section (`by_project`, `by_workspace`, `by_kind`, `by_type`) adds up to the `total` row. Committed work, credits back and ad spend are rows of their own and are never part of that total. To read the same month from your own code or an assistant, use [`GET /v1/costs`](/api/endpoints#get-v1costs) or the [`get_costs`](/mcp/tools#get_costs) tool. Both leave out the budget, the caps, the balance and how it is funded: those are yours to set, in the dashboard. # Credits Source: https://docs.attensira.com/account/credits What a credit is, which agent actions spend one, and what happens as a workspace balance falls to zero. A credit is the unit Attensira uses to meter **agent work** — the actions where a language model does something on your behalf rather than returning data you already have. Your workspace holds a credit balance. Agent actions draw it down. Reading numbers you have already collected does not. ## What spends a credit Three kinds of action spend credits, whether you start them in the app or over [MCP](/mcp/overview): * **Chats.** A conversation with the Attensira agent. Each turn is agent work. * **Automation runs.** Every execution of an automation, whether you trigger it by hand or it runs on its schedule. * **Ask calls.** The `ask` tool, which puts a question to the agent and returns an answer or a running session. Over MCP, the tools that cost credits are `add_prompts`, `run_automation` and `ask`, plus `add_context` when called with `run: true`, `answer_inbox_question` when the run that asked has already finished, and the agent run that answers a `steer_project` post. The read-only tools — `search`, `get_analytics`, `get_prompt`, `get_page`, `query_evidence`, `get_session`, `list_sessions`, `get_account`, `get_ai_traffic_install`, `list_inbox`, `get_inbox_item`, `list_automations`, `get_automation`, `deciding_sources`, `win_plan`, `get_brief`, `list_wins`, `get_costs`, `get_loop_health`, `get_goals`, `list_projects`, `list_deliverables`, `get_report`, `get_markets`, `preview_markets`, `get_brand_family`, and the [deep reads](/agent/deep-read) `answer_history`, `answer_slice`, `citation_timeline` and `page_history` — do not, and neither do the writes that only change settings, memory, goals or sessions (`approve_work_item`, `decline_work_item`, `approve_ask`, `decline_ask`, `resolve_inbox_task`, `update_automation`, `update_settings`, `record_observation`, `write_skill`, `set_goal`, `connect_github`, `cancel_session`, `delete_session`, `add_context` without `run`). `start_workspace` and `confirm_setup` charge nothing: payment happens in the person's browser. Within a run, reading the open web (`web_fetch`, each page `find_contact` reads) is metered by the work the read actually took, up to a fixed ceiling per read. Reddit threads read through `web_fetch` are free. Opening a dashboard, filtering a chart, or exporting numbers you already have does not spend credits. If you are watching your balance move while doing only those things, something else in the workspace is running — check your automation schedules. ## Google Ads Credits pay for the agent's work on your [Google Ads](/agent/google-ads), never for the ads. * **Researching keywords** in a chat or automation is metered by the keywords looked up. * **Drafting a campaign** is metered by the work it took: reading the landing page, writing the campaign (and its one repair pass, when one is needed), and the images it generated. * **Filing a campaign for approval**, approving it, the daily sync, the hourly spend guard and the optimizer's daily pass spend no credits. **Ad spend is not credits.** What Google charges for your ads is paid to Google from your own Google Ads billing. It is not our fee, it never comes out of your credit balance, and it does not count toward the [monthly credit cap](#monthly-credit-cap). A spent credit balance does not pause a running campaign, and does not stop the [spend guard](/agent/google-ads#the-spend-guard) watching it. ## How a credit is consumed Consumption is per action, not per second and not per seat. Two people in the same workspace draw from one shared balance, so a busy automation schedule and a busy chat habit compete for the same pool. Cost scales with how much work the action actually does. A short chat turn costs less than an automation that fans out across many prompts and pages. This is why an automation you run by hand can feel cheap while the same automation on an hourly schedule is not. When you trigger an automation over MCP, pass an `idempotency_key` to `run_automation`. It is sent upstream as the `Idempotency-Key` header, so a retried call does not produce a second billed run. ## Plan credits and bought credits Your balance holds two kinds of credit, and they follow different rules. **Plan credits** come with your plan every month (see [Plans](/account/plans)). Credits you do not use roll over: up to **100%** of one month's grant carries into the next, so a balance of plan credits can reach two months' worth and no more. Rolled-over credits expire **2 months** after they roll over. Rollover is the only expiry in the product. **Bought credits** are the ones you top up, by hand from **Buy credits** or automatically with auto top-up. They **never expire**. The smallest purchase is **5,000** credits, and the more you buy at once, the less each credit costs. The discount is by volume, not graduated: the whole purchase is priced at the band it lands in. | Credits in one purchase | Price per credit | Off the 1¢ list price | | - | - | - | | 5,000 – 25,000 | 0.9¢ | 10% | | 25,001 – 250,000 | 0.8¢ | 20% | | 250,001 and up | 0.6¢ | 40% | So 5,000 credits cost $45, 100,000 cost $800 and 500,000 cost \$3,000. **No plan counts seats.** Invite as many people as you need; they all draw from the workspace's one balance. Every plan, Starter included, gets the same support from a person, at [team@attensira.com](mailto:team@attensira.com). ## What happens as the balance falls Nothing degrades quietly. Your balance is in the top bar on every screen, in credits, and the agent surfaces remain fully functional right up to the point where a request cannot be covered. The usage chip turns amber when the balance is low, and when your plan's prompt slots or this month's responses are used up. Open it for the balance with **Buy credits**, and for prompt slots and responses against your plan's limits. It shows nothing until the balance has been read, rather than a 0 it has not confirmed. As the balance gets low, the practical effect is that long-running agent work becomes the first thing at risk: an automation that fans out widely needs more headroom than a single chat turn, so it is the kind of action most likely to be the one that cannot start. ## What a workspace sees at zero At zero, credit-consuming actions stop. Starting a chat, running an automation, calling `ask`, or adding prompts through the agent will be refused rather than partially performed. You are told the workspace is out of credits; you do not get a half-finished automation run. In the dashboard, the box you start a chat from says **You're out of budget · Top up**, links to Billing, and keeps what you typed so you can send it after topping up. Everything that does not consume credits keeps working. You can still open the workspace, read every metric already collected, filter and compare, and use the read-only MCP tools. **Readings keep running**: the daily answers to your prompts use your plan's monthly response budget, not credits. ## When the balance is spent When the credit balance is spent, **scheduled automations do not run**. Nothing is charged for runs that did not start. Instead of disappearing quietly: * The workspace gets **one** row in [Work](/agent/inbox), **Scheduled work is paused: the credit balance is spent**. It names the automations that are paused and has a **Top up credits** button. * It also gets **one email** when the pause starts. A pause that lasts a week is one row and one email, not seven. * Each skipped run is recorded in [loop health](/agent/loop-health) with the reason `no_credits`. After a top-up, or with auto top-up on, the automations run again on their **next schedule**; missed runs are not replayed. The row closes itself when the first one runs. ## What stops new work The balance is the first of four checks every new piece of work passes before it starts. A [monthly budget](/account/plans#your-monthly-budget) and the caps below it sit on top of the balance: work needs both a balance to pay for it and room under the budget and caps. | Order | Check | Holds | Lifted by | Loop health reason | | - | - | - | - | - | | 1 | The balance is spent | Everything that spends credits | A top-up, or auto top-up | `no_credits` | | 2 | The organisation spent this month's budget (or 110% of it, if you allow 10% over) | Automations, chats and `ask`, in every workspace | Raising the budget, or the 1st of the month | `budget_reached` | | 3 | A project spent its own monthly cap | That project's automations, and chats filed under it | Raising the project's cap, or the 1st | `work_area_cap` | | 4 | A workspace spent its monthly credit cap | That workspace's automation runs, scheduled or started by hand. Not chats or `ask` | Raising the cap, or the 1st | `workspace_cap` | The first check that fails is the answer. Topping up lifts only the first: a budget or a cap is a limit you set, and more balance does not move it. Each check happens before the work starts, never during it. A run already under way always finishes and is charged in full, so a month can pass the budget or a cap by at most one run, and runs that start at the same moment can each pass on the same reading. **What you see.** A refused request answers `402` (payment required), and its message names the check: the balance is spent, this month's budget is spent, the project has spent its monthly cap, or the workspace has spent its monthly cap. Nothing is half-done. In the dashboard, a chat refused at the budget shows the budget banner with **Raise budget**; see [Costs](/account/costs#the-spend-gauge). **Scheduled work** that is stopped files one row per workspace and wall in [Work](/agent/inbox), and sends one email, as the balance does. The budget's row and a project's row carry a **Raise cap** button; the workspace cap's row says where the setting is. Each closes itself when the work runs again. In [loop health](/agent/loop-health) each skipped run carries the reason in the table above. If this month's spend cannot be read when a scheduled run is due, the run is skipped rather than started against a cap it could not check. Nothing is paused and nothing is filed in Work; loop health records `spend_unreadable`, and the next scheduled check reads again. A chat or `ask` started at that moment is refused with a `503` rather than a `402`; nothing was charged, and it can be sent again. ## Monthly credit cap Each workspace has an optional **Monthly credit cap** (`monthly_credit_cap`, in credits; `0` means no cap). It caps what that workspace may spend in a calendar month, counted in UTC. One credit is one cent, so a cap of 20,000 credits is \$200; [Costs](/account/costs#caps) shows it in dollars. * It counts **everything charged to the workspace** in the month: agent work, and the reads of your own pages that monitoring makes. Daily prompt readings use your plan's response budget, not credits, so they are not in it. * It is checked **before** a run starts, never during one. A month's spend can therefore go over the cap by at most one run's cost. * It holds the workspace's automation runs, scheduled or started by hand. Chats and `ask` are held by the budget and by a project's cap, not by the workspace cap. * When the cap is reached, scheduled runs pause with a row in Work and an email that name the cap. They resume on the 1st of the next month, or when you raise the cap, and the row closes itself when they do. On an [Agency](/account/plans#agency) account, where credits are shared across every client workspace, the cap is how you stop one client from spending another's month. A [project cap](/account/costs#caps) does the same for one project inside a workspace. ## Project monthly cap A [project](/agent/projects) can have a monthly cap of its own, shown and set **in dollars**, to the cent. Empty means the project has no cap of its own and only the workspace's limits apply. Only an org admin can change it, in the project's settings or in billing settings; both edit the same cap, and the project's settings show what it has spent this month against it. Everything the project's crew does counts toward it: its scheduled and event runs, tests, and the Chief of staff's replies to posts in the project. Like the workspace cap, it is checked **before** a run starts and never during one, so a month can go over it by at most one run's spend. Months are calendar months in UTC. When the project reaches its cap, its new work stops and the rest of the workspace carries on: * The project's scheduled and event runs do not start, and each skipped run is recorded in [loop health](/agent/loop-health) with the reason `work_area_cap`. A test of one of its automations is refused too. * The Chief of staff does not start on new posts in the project. The post is saved, the feed says the reply could not start, and a steer is still kept as the project's direction. * The workspace gets **one** row in the Inbox per capped project, **Scheduled work is paused: this project reached its monthly cap**, naming the scheduled automations that did not run, with a **Raise cap** button, and one email when the pause starts. * The project's feed gets one line for the month saying the cap was reached and when work resumes. Work resumes on the 1st of the next month, or as soon as the cap is raised. The row closes itself once a run of that project is paid for again. When more than one limit stands in the way, the first one met is the one you are told about, in this order: the credit balance, the account's monthly budget where one is set, the project's cap, then the workspace's monthly credit cap. A chat or a post in a project meets every limit except the workspace's cap, which bounds automation runs only. Restore a balance by topping up or changing plan in your workspace settings. See [Plans](/account/plans) for what each plan includes and how the budget works, and [Costs](/account/costs) for what the month has cost. # Notifications Source: https://docs.attensira.com/account/notifications What Attensira emails you about, which messages can go to a Slack channel instead, and how to stop any one of them without stopping the rest. ## What we email you about Email is on by default for every workspace, grouped into eight categories: | Category | What it covers | | - | - | | Onboarding | A welcome email, a note when your [first reading](/start/first-reading) is ready, and a note when the set-up session's first work is waiting in your [Inbox](/agent/inbox) | | Brief | The [morning brief](/agent/morning-brief), at 07:00 in your workspace's own time zone, including its Monday edition | | Attention | Something in your [Inbox](/agent/inbox) has gone unseen for 24 hours (at most [one email a day](/agent/inbox#one-email-a-day-at-most) per workspace, listing the top three), or an approval expires in 2 days | | Runs | An [automation](/agent/automations) failed or paused, including scheduled work paused by a reached [monthly credit cap](/account/credits#monthly-credit-cap), and the result of each [re-read](/measure/before-and-after) after a change went out | | Credits | Your workspace has 20% of its credits left, or none left. While the balance is spent, one email a day says scheduled work is paused. Also a note when work a month's budget paid for was not delivered and that amount is back on your balance | | Wins | The win message: an AI answer named your brand, or linked to your site, for the first time since we started reading that prompt, on a prompt that does not already name you. At most one message per workspace per day, listing every new win | | Client report | A [client report](/agencies/client-reports) was published, sent to the recipients set under Results → Client reports | | Account | Your workspace's [approval mode](/agent/approval) was moved to Draft; this category cannot be unsubscribed from | ## Unsubscribing Every email carries a one-click unsubscribe link for its own category, and only that category. Turning off Runs emails does not touch Credits or Attention, and turning off Wins emails does not touch the brief. The client report category is per workspace, not per recipient: unsubscribing from a client report email stops client report emails for the whole workspace, for every recipient. The link opens a page with a confirm button. Opening the link alone does not unsubscribe you; nothing changes until you press confirm on that page. Account email cannot be unsubscribed from. It only ever fires for changes that affect what the agent is allowed to do without you, and we treat that as something every workspace owner needs to see. The **Email** switch under **Settings → Notifications → Delivery** turns off every email for the workspace at once, except Account email. An unsubscribe link only stops email. A message you also send to Slack keeps posting there until you switch Slack off for it; see [Stopping a message](#stopping-a-message). ## Slack Two messages can go to a Slack channel, as well as or instead of email: * **The Monday note**, the Monday edition of the [morning brief](/agent/morning-brief). The daily editions are email only whatever you choose here, because a channel that hears from us every morning stops being read. * **The win message**, the same message as the Wins email. Both post to **one channel** that you pick. Every other notification is email only. The Slack post says what the email says: the subject in bold, then every paragraph of the email in order, and the email's link into Attensira when it has one. It has no unsubscribe line, because Slack is switched off in settings, not from a link. ### Setting it up Open **Customize → Connectors** and connect Slack. The connection is per workspace. Under **Settings → Notifications → Monday note**, choose the **Slack channel**. The list shows the public and private channels the connection can see, with archived channels left out. A channel marked **not a member** does not have the Attensira app in it, and posts there fail: add the app to the channel in Slack, or pick another. Under **Monday note** and under **Wins**, switch **Email**, **Slack** or both. A few rules hold the choice together: * **Every message goes somewhere.** You cannot switch off both Email and Slack for one message. Switching Slack off when it was the only place sends the message by email instead. * **Slack needs a live connection.** Slack can only be switched on, and a channel picked, while Slack is connected. Otherwise the settings refuse with "Slack is not connected for this workspace. Connect it in Customize → Connectors first." * **Slack and email are delivered separately.** A Slack post that fails never holds up the email, and an [email address that bounces](#if-mail-to-you-starts-bouncing) does not stop the Slack posts. * **Renaming the channel is fine.** The channel is stored by its Slack id, so a renamed channel still receives the note. ### What connecting Slack allows Connecting Slack lets Attensira do two things in your Slack: read the names of your channels, so you can pick one, and post the Monday note and the win message to the channel you picked. Nothing else. The connection gives the agent no Slack tools. No run can read your Slack messages or post in your Slack workspace; the only posts are the messages you chose on this page. ## If Slack disconnects Slack can stop working for a workspace in a few ways: the connection is disconnected or revoked, the channel is archived, or the Attensira app is removed from the channel. In every case: * **Your settings stay as they are.** You can still change any other setting, including switching Slack off. When the connection itself is gone, Settings → Notifications says "Slack is not connected, so this is not reaching Slack." under each message still set to Slack. * **The email copy still goes**, for a message that also goes by email. A message set to Slack only reaches nobody until Slack works again, so switch its email on if you need it in the meantime. While Slack is not connected, changing where a message goes also takes Slack off that message, so switch Slack back on for it once you reconnect. * **One Inbox item asks you to fix it**: "Reconnect Slack to keep getting the Monday note". However many posts fail, it stays one item. It closes on its own when you reconnect Slack, or when the next post to the channel goes through. What happens to the post that failed depends on the cause: | Cause | The failed post | The fix | | - | - | - | | The connection was disconnected or revoked | Not retried, and not posted later. The next Monday note or win message goes out on its own once Slack is back | Reconnect Slack in Customize → Connectors | | The connection is still there but not working | Retried for about a day. If you reconnect within that time, the post still goes out | Reconnect Slack in Customize → Connectors | | Slack refused the post: the channel was archived, or the app is not in it | Retried for about a day, and the Inbox item appears on the first refusal | Add the Attensira app to the channel in Slack, or pick another channel in settings | If a post fails for any other reason on its last attempt, you get the same Inbox item. ## Stopping a message | To stop | Do this | | - | - | | One category of email | Use the unsubscribe link in any email of that category | | Every email except Account | Switch off **Email** under Settings → Notifications → Delivery | | The Monday note or the win message in Slack | Switch off **Slack** for that message under Settings → Notifications. If Slack was its only place, it moves to email | | The Monday note or the win message everywhere | Unsubscribe from its email (Brief or Wins), and switch Slack off for it | Unsubscribing from Brief stops every edition of the brief by email, daily and Monday. ## If mail to you starts bouncing A bounced delivery or a complaint marks your address undeliverable, and the app shows a banner until it is fixed. We do not keep retrying a broken address, and we do not silently drop you off the list either — the banner is there so you notice and correct it. This is about email only. Posts to your Slack channel carry on. ## What we never email Attensira never sends anything outreach-related on your behalf. When the agent drafts outreach, it creates the draft in your own Gmail or Outlook — see [Inbox](/agent/inbox) — so what goes out to a third party goes out from your account, under your review, never ours. # Plans Source: https://docs.attensira.com/account/plans The plan fee and the monthly work budget: what each plan includes, what a budget buys, what happens when we deliver less than it committed, and how prompt slots and the monthly response budget differ. What you pay is two things: a **plan fee** for the platform, and a **monthly budget** for the work the agents do. * **The plan** pays for the platform: monitoring and analytics. It comes with a monthly allowance of credits, and it sets how many prompts you can track, how deeply each one is sampled, how long history is kept, and which features are on. * **The budget** pays for the work: articles, page fixes, outreach and the rest. It is shown as the work it buys ("≈ 4 articles · 12 page fixes · 20 outreach pitches · daily monitoring" for \$1,500), charged from your balance as the work is done, and it is the most that work can be charged in a month. Work we don't deliver isn't charged. See [Your monthly budget](#your-monthly-budget). So what a month costs is the plan's fee plus the top-ups the work needed beyond the credits the plan includes, and the budget caps what the work can be charged. ## Which plan Signing up in the dashboard starts on **Starter**, \$129 a month, and asks for a monthly budget beside it. You can change it any time under **Settings → Billing**. Starter comes with a **7-day free trial**: checkout takes a card and states the charge before it takes it, and the plan fee is not charged until the trial ends unless you cancel first. Only Starter has a trial, and it is given once. **Growth** ($349 a month) and **Business** ($699 a month) are larger monitoring allowances: more prompts, three samples per reading, Gemini, longer history, writing your own skills, and AI traffic. The business profile, and checking every draft against your rules, are on every plan. They do not buy more work; the budget does. In the dashboard they are offered under **Settings → Billing** once you are set up, not at signup. Setting up from [your own assistant](/start/quickstart#set-up-from-your-own-assistant), the plans `start_workspace` lists are every plan checkout sells. Past Business, **Enterprise** is a conversation: custom limits and terms, through **Talk to sales**. Paying yearly costs ten months' price for twelve. There is no Pro plan. If you have seen one named anywhere, it is not ours. ## What each plan includes | | Starter | Growth | Business | Agency | | - | - | - | - | - | | Price per month | \$129 | \$349 | \$699 | \$2,000 | | Yearly | \$1,290 (10 months' price) | \$3,490 (10 months' price) | \$6,990 (10 months' price) | \$20,000 (10 months' price) | | Where it is offered | Signup and Settings → Billing | Settings → Billing | Settings → Billing | No longer offered in the dashboard; accounts on it keep it | | Free trial | 7 days, card required | — | — | — | | Prompt slots | 50 | 150 | 350 | 750 | | Responses a month | 6,000 | 67,500 | 157,500 | 337,500 | | Agent credits included a month | 300 | 600 | 1,200 | 50,000 | | Sampling depth | n=1 | n=3 | n=3 | n=3 | | Models | 4 — ChatGPT, Perplexity, Claude, Google AI | 5 — those plus Gemini | 5 — those plus Gemini | 5 — those plus Gemini | | History retention | 3 months | 12 months | Unlimited | Unlimited | | Business profile, and reading the rules | Included | Included | Included | Included | | Writing custom skills (rules) | — | Included | Included | Included | | Crawler Logs (AI traffic) | — | Included | Included | Included | | White label | — | — | — | Included | Models are gated by plan. Starter reads four surfaces — `chatgpt`, `perplexity`, `claude` and `google_ai`; Growth, Business and Agency add `gemini`. Turning Gemini on in a Starter workspace does not make it run: the scheduler queues only the engines the plan includes, so the reading never happens and the metric comes back untracked rather than as a zero. Which of a plan's models a workspace actually reads is still yours to choose. At signup you pick where you want to be found, and when your plan starts the workspace reads the assistants you left on that the plan includes. With the signup defaults on Starter, that is all four Starter engines — `chatgpt`, `perplexity`, `claude` and `google_ai`. On Growth and above, Gemini is not in the signup defaults: a new workspace starts on the same four, and you switch Gemini on in settings. See [Models](/tracking/models) for which surfaces have readers. ## Agency The Agency plan is no longer offered in the dashboard. Accounts already on it keep it, at **$2,000 per month** or **$20,000 per year** (two months free), with everything below. An agency starting now talks to sales. * **750** prompt slots. * **337,500** responses a month. * Sampling depth **n=3**, on all five engines including Gemini. * **50,000** credits a month, with the same [rollover](/account/credits#plan-credits-and-bought-credits) as the other plans. * Unlimited history, Knowledge & Skills, and Crawler Logs. * **White label**: your name, logo and colour on client reports and share links, with Attensira removed. Only Agency has it; on lower plans the setting shows an upgrade prompt that names Agency. See [Branding and white label](/agencies/branding). **Unlimited client workspaces.** No plan counts workspaces. An Agency account's prompts, responses and credits are shared across every client workspace in the org. The pages built for agencies are [Clients](/agencies/clients), the [client reviewer link](/agencies/client-reviewer-link) and [client reports](/agencies/client-reports). For more, see [attensira.com/solutions/agencies](https://attensira.com/solutions/agencies). ## Your monthly budget The budget is a **monthly ceiling** on what work may be charged, not a prepayment. Nothing is charged up front: work is metered as it runs, and work that never ran is never charged. Once the month's spend reaches the budget, new work stops until you raise it or the month turns; work already running finishes. See [Costs](/account/costs#when-a-cap-or-the-budget-is-reached) for exactly what stops. Set it under **Settings → Billing**, or when you sign up. * **Range.** From $500 to $5,000 a month, in steps of \$100. * **The month.** A UTC calendar month, the same month billing counts. * **What it counts.** Everything work is charged in the month: agent work, and the data reads monitoring makes. The plan's fee is not part of it, and neither is ad spend. * **Who can change it.** An organisation admin, in the dashboard. No agent, API key or MCP connection can set or raise a budget; an agent that needs more room has to ask you. * **At the budget.** By default new work pauses at the budget. You can instead allow 10% over, so new work stops at 110% of it. See [Pausing at the budget](/account/costs#pausing-at-the-budget). This is not the [monthly response budget](#the-monthly-response-budget-is-a-flow), which is the plan's allowance of AI answers read for you. ### How the budget is funded Setting a budget turns on [auto top-up](/account/credits) and sets its monthly cap to the budget (110% of it if you allow 10% over), in the same change. Your balance is then refilled from your card as work is done, and never by more than that cap in a month. Before you save, the confirmation names the most a month can charge: "Up to \$X/month will be charged to your card as work is done". * A top-up amount and threshold you already chose are kept. If you never chose them, the smallest top-up (5,000 credits, $50 at the one-cent list price) is used, bought when the balance falls to $10. * Removing the budget leaves auto top-up as it is. Turning off a ceiling is not a request to stop paying for work. * **With no card on file** the budget is still saved, and every workspace gets an Inbox row, **Add a card so your budget can fund work**. Until a card is added, work stops when the balance runs out, before the budget does. The row closes itself once a card is on file. ### The top-up rule, and your yes to it The budget is paid by auto top-up, so the plan step states the rule in one sentence with three numbers in it, for example: > We'll top up $50 when your balance falls below $10, never more than \$1,500 a month. $50 is the smallest top-up we sell: 5,000 credits at the one-cent list price. Bought in one purchase, 5,000 credits fall in the 0.9¢ volume band, so the card is charged $45 (see [Credits](/account/credits)). $10 is a fifth of the $50, and the last number is the cap: the budget, or 110% of it if you chose to let work run up to 10% over. The button reads **Agree and choose Starter**. Pressing it saves your yes, before checkout asks for a card: the budget, whether work pauses at it or may run 10% over, the sentence's three numbers, who agreed and when. If that save fails, the step says so and nothing is charged: try again, or skip and set a budget later in Settings. Nothing is set up by the page you land on after checkout. When your plan starts, the rule you agreed to is set once, on our side, and the budget is set as you chose it. A $1,500 budget with 10% over is a $1,500 budget that new work may run to $1,650, and auto top-up never charges more than $1,650 in the month. If you set a different budget in Settings in the meantime, yours stands. If the rule cannot be set, you get one **blocked** row in your [Inbox](/agent/inbox), "Your monthly budget rule wasn't set", saying where to set it. ### What a budget buys A budget is turned into a volume of work at these list prices: | Work | Price | | - | - | | Article | \$150 | | Page fix | \$25 | | Listing fix | \$25 | | Outreach pitch | \$25 | The first $100 of every budget is set aside for monitoring and data: readings beyond your plan's own, page reads, crawls and search data. The rest is spread in a fixed mix of 4 articles to 12 page fixes to 20 outreach pitches. Every count rounds down, so the line never promises a deliverable the budget does not fully pay for. The plan step at signup starts at $1,500: | Budget | Articles | Page fixes | Outreach pitches | | - | - | - | - | | \$500 | 1 | 3 | 5 | | \$1,000 | 2 | 7 | 12 | | \$1,500 | 4 | 12 | 20 | | \$3,000 | 8 | 24 | 41 | | \$5,000 | 14 | 42 | 70 | Listing fixes have a list price but no share of that mix. An agent can still propose one, at its list price. These prices turn a budget into the volume we commit to, and value the [credit](#if-we-deliver-less-than-the-budget-committed) when we fall short. They are not a price per item: what a month is charged is still metered by the work actually done, so one article can be charged more or less than \$150. ### If we deliver less than the budget committed Work we don't deliver isn't charged. The budget commits us to the volume in the table above for the month, and on the 1st we check what was delivered against it. **What is committed.** The month's volume, for the kinds of work we can count: articles, page fixes and outreach pitches. A budget set or changed during the month commits a share prorated by the days it was in force, so a budget set with a week left commits about a week's work. Nothing is committed for listing fixes, because we cannot count them yet. **What counts as delivered.** An article or a page fix when it is completed, an outreach pitch when it is sent. Work finished on our side and **waiting on you** to put live (an article to paste, a Notion page to publish, a pull request to merge, a pitch drafted into your own mailbox) counts as delivered. It is never a shortfall. **The credit.** For each kind of work, the shortfall is what was committed less what was delivered or is waiting on you. The credit is the smaller of two amounts: 1. the shortfall, valued at the list price in force when that work was committed; 2. what you spent in the month, less the list-price value of everything delivered or waiting on you. In plain words: undelivered work is credited back at its list price, but never more than you paid beyond the value of what you got. A budget you did not use up is not credited, because it was never charged. **How it reaches you.** The month is closed at 00:15 UTC on the 1st. The credit goes back on your **balance**, not to your card, as one credit for the month. You get an email and an Inbox notice, **We credited \$X back for September**. A month with no shortfall closes quietly. The credit is shown against the month it is for, as **Not charged** on [Costs](/account/costs#committed-vs-delivered), and never lowers the spend of the month it lands in. A month is credited at most once. If closing a month fails, it is tried again daily until it succeeds, and it shows in [loop health](/agent/loop-health) as `month_close_failed` until then. An organisation that cancelled is still closed, and its credit lands on its balance. ### What a budget does not buy A budget pays for work, not for results. We do not promise any ranking, citation, mention or change in AI answers. What the work did is on your [receipt](/account/receipt), which says plainly when nothing is proven yet. ## Prompt slots are a stock, not a flow A prompt slot is a place in your workspace where one tracked prompt lives. Slots are a **stock**: the count goes down when you add a prompt and back up when you remove one. Nothing about slots resets at the start of a month, and unused slots do not accumulate. So on Growth, 150 slots means "at most 150 prompts tracked at any one moment", not "150 prompts per month". If you fill all 150 and want a new prompt, you remove one first, or move up a plan. Removing a prompt frees its slot immediately. Removing a prompt stops future readings for it. What has already been measured stays subject to your plan's retention window. ## The monthly response budget is a flow The monthly response budget is the other quota, and it works the opposite way. It is a **flow**: an allowance that is consumed as readings are collected during the month, and that resets when the billing period rolls over. Spending it does not reduce your prompt slots, and freeing a slot does not refund budget you already used. These are two independent numbers measuring two different things. Slots limit *how many* prompts exist. The response budget limits *how much answering work* happens across them in a month. A workspace can be well inside its slot count and still be spending its budget quickly, because depth and model count multiply: each prompt is read on each tracked model, and each of those readings is sampled at your plan's depth. Both are in the usage chip in the top bar, next to your credit balance: open it for **Responses this month** and **Prompt slots**, each against your plan's limit, with **Upgrade** beside them. The chip turns amber when either is used up. See [Home and the dashboard](/start/today#the-top-bar). ## How sampling depth changes what you see Sampling depth is the number of times the same prompt is put to the same model in a single reading. Starter samples once (n=1). Growth, Business and Agency sample three times (n=3), issued concurrently. Depth is a measurement decision, not a cosmetic one. Models are not deterministic, so a single answer is a single draw. At n=1, most movements in your numbers will not clear the 95% significance gate and will be reported as no proven change rather than as a rise or a fall. At n=3 you get three draws per reading, so real movements clear that gate sooner. Read [Limitations](/reference/limitations) before you act on a small sample. It explains exactly what the gate does and what a low n can and cannot tell you. ## Changing plan Change plan under **Settings → Billing**. Nothing changes on the click: a confirmation names the new price first. Upgrading raises your slot count and, on Growth and above, switches on writing custom skills and Crawler Logs; on Agency it also switches on white label. The business profile, and the agent reading your rules, are on every plan. Downgrading lowers your slot count and shortens retention, so plan for both: if you drop below the number of prompts you currently track, you choose which prompts to keep, and history older than the new retention window stops being available. Billing, invoices, plan changes and the budget live in **Settings → Billing**. For what the month has cost and where it went, see [Costs](/account/costs). For what a credit is and which actions spend one, see [Credits](/account/credits). # Your receipt Source: https://docs.attensira.com/account/receipt One page that says what we actually did, what is waiting or failed, and whether we can prove anything moved — built to forward, not to interpret. Your receipt lives at [app.attensira.com/receipt](https://app.attensira.com/receipt), linked from **Settings → Billing**. It answers one question: what did we do with the money, and can we prove any of it worked. ## Recorded since The date the action log started for your workspace. This is never filled in backwards — work done before that date is not on the receipt, because there is no record of it to show, and every count on the page reads against this date rather than against "always". ## Everything we did One dated row per act: a pull request opened, a page rewritten, a pitch drafted, a directory listing filled in, an answer published, a page changed. Each row carries a status — **shipped**, **waiting**, **failed**, or **cancelled** — or no status at all, when the act itself has none to report. Failed attempts are listed, not hidden. A row that was withdrawn is marked **cancelled**, never removed — what we tried and pulled back is as much a fact about the work as what shipped. ## Counts **Shipped**, **waiting** and **failed**, tallied for the window. Per model surface, a count reads like "named in 1 of 8 answers on ChatGPT" — a count and its sample size, never a bare percentage, and never compared against a previous window. A window this narrow does not support a comparison; see [How we collect data](/measure/how-we-collect-data) for why. ## What changed Until an answer is **re-read** after a change lands, nothing is proven, and the page says exactly that rather than implying credit for work still unverified. Once a verified re-read has run, this section lists the citations we can show we caused, and the date the next re-read is scheduled. ## Choosing a window The default window is 30 days. Pass `?since=2026-08-01` in the URL for another window — any date on or after Recorded since. ## "Copy what to tell your boss" A button that assembles three bullets, meant to be forwarded exactly as written: * What shipped, by kind (pull requests, rewrites, pitches, listings, answers, page changes). * What is waiting, and any failed attempt worth naming. * The honest line: no change is proven yet, and when the first verified re-read lands — or that none is scheduled. ## Sharing a receipt without an account **Share** mints a private link at `app.attensira.com/r/` that opens the same page with no login. It lasts **30 days**, is not indexed or listed anywhere, and there is no export button — the link is the only way out of the page. **Rotate** (`/receipt/share?rotate=true`) mints a fresh link and stops every previously shared link for this workspace at once: receipts, shared [rewrites](/agent/inbox#rewrites), [client reports](/agencies/client-reports) and shared [boards](/measure/boards#sharing-a-board-with-someone-who-has-no-account), because one secret covers them all. It does not touch the [client reviewer link](/agencies/client-reviewer-link), which is separate. Apart from a board, which has its own **Turn off link**, there is no way to revoke one shared link while leaving another live; rotating is all-or-nothing by design, because a receipt that leaked is a receipt you want to kill everywhere, not edit around. A shared receipt carries your org's logo, name and colour. Without white label it ends with "Made with Attensira"; with [white label](/agencies/branding) nothing on it names Attensira. Its link preview is neutral: the title and "A page shared with you." For a periodic report to send a client, with visibility counts, deciding sources and next steps, see [Client reports](/agencies/client-reports). ## What it will not do * Report movement before a verified re-read. * Turn a small sample into a percentage. * Compare windows before there is enough history to compare honestly. * Show work from before the action log started. # Referrals: give a month, get a month Source: https://docs.attensira.com/account/referrals After your first win, Attensira offers you a link to give another team a month. When they start a plan with it, you each get a month. Attensira offers you a referral link only after a [win](/agent/wins), never before. A win is the first time an engine names your brand, or links to your site, on a prompt it had been answering without you. Until your organisation has one, there is no offer and no link. The offer reads: > **Give a month, get a month.** Send your link to a team that should be in AI answers too. When they start a plan with it, you each get a month. ## Who is offered a link Your organisation is offered a link once it has **at least one win** in any of its workspaces. Nothing else qualifies you: not your plan, how long you have used Attensira, or how much work it has done for you. * A **baseline** never counts. A naming recorded when detection started, or one we could not call a first, is not a win. See [what is never announced](/agent/wins#what-is-never-announced). * A **dismissed** win still counts. Putting the card away does not undo that the answer named you. * A spent credit balance does not hide the offer. Whether you qualify is decided by Attensira's servers, not the browser. If your organisation has no win, the offer does not appear, and no code is created. ## When the offer appears The offer comes with a win, at one of two moments: 1. **After you share a win.** When a share goes through, the offer appears under the share buttons. 2. **When you close the win card.** Closing the card, or dismissing it, puts the offer in the card's place. It is never shown when a page loads and never on its own. Closing the offer with its close button (**Not now**) hides it for the rest of that browser tab's visit, whichever win comes next. It comes back at a later win, in a new tab or on a later visit. ## Your link The link is your organisation's sign-up link with your code on the end: ``` https://app.attensira.com/signup?ref= ``` Press **Copy link** to put it on your clipboard. If your browser does not allow the copy, the link is selected in the field so you can copy it by hand. Your organisation has **one code**. It is created the first time you are offered the link and stays the same after that, so a link you sent last month still works. The code belongs to your organisation owner's billing account, because that is where the month is applied. If the organisation's owner changes, the organisation gets a new code for the new owner, and the offer shows the new link. A link sent before the change still carries the previous owner's code, so a month earned through it goes to the previous owner's billing account. ## What happens when someone uses it Their browser keeps your code for **30 days**. It survives signing up, signing in with Google, opening the email verification link in a new tab, and closing the tab and coming back later to finish. At the plan step, the code goes with their checkout. It is redeemed on their billing account before the checkout is created. The referral counts when their first paid checkout goes through, and the month is applied from there, as the offer says. Once their checkout goes through, the code is removed from their browser. ## The rules * **The last link wins.** If someone opens your link and then another person's, the second code is the one used. * **One referral per billing account.** Once a code is redeemed on an account, a later link does not replace it. * **You cannot refer yourself.** A code used on its own owner's billing account is refused. * **A code is used only at the setup plan step.** Choosing or changing a plan later from settings does not carry a code. * **A refused code never blocks a sign-up.** An unknown code, your own code, or a second referral is refused, and the plan starts as normal without the month. A refused code is not tried again. * **An outage is retried.** If the billing service cannot be reached when the code is redeemed, the plan still starts. The redemption is tried again the next time that account starts a checkout. Every failed redemption is reported to us, so a month that was promised and not applied is noticed on our side and not only on yours. ## What you cannot do yet * **See who used your link.** The dashboard does not list redemptions or the months applied to your account. * **Get your link over MCP or the API.** The offer is shown only in the dashboard. No [MCP tool](/mcp/tools) or [REST endpoint](/api/endpoints) returns your code or link. # Branding and white label Source: https://docs.attensira.com/agencies/branding Put your name, logo and colour on shared receipts, rewrites and client reports, and on the Agency plan remove Attensira from them entirely. ## Where to set it **Settings → Branding** is org-wide: it applies to every workspace in the org. | Field | What it takes | | - | - | | Brand name | Up to 80 characters | | Logo URL | An absolute `https://` URL to your logo. There is no upload | | Accent colour | A hex colour, `#rrggbb` | | Remove Attensira from shared pages | The white-label switch. It needs a brand name first | Branding is an [Agency plan](/account/plans#agency) capability: the name, logo, colour and the white-label switch are all saved only on Agency. On other plans the pane says so, and the first change you make is refused with a message that names Agency; the form then locks and links to your plan. ## What it changes Shared pages, which open without an account, wear the sharing org's branding: * [Shared receipts](/account/receipt#sharing-a-receipt-without-an-account) at `/r/` * Shared [rewrites](/agent/inbox#rewrites) at `/rw/` * [Client reports](/agencies/client-reports) at `/r/report/` Each shows your logo, your name and an accent bar in your colour. **Without white label**, the page ends with "Made with Attensira". **With white label**, nothing on the page names Attensira: not the footer, not the AI notice, not the browser tab title, not the favicon, and not the link preview. The AI notice itself stays, without our name in it. ### Link previews are neutral for everyone When a shared link is pasted into Slack, email or a chat app, the preview (OpenGraph and Twitter card) shows the page title and "A page shared with you." It names no sender, for every org. The brand is only known once the page reads the token, which a link preview never does. ## Over the API The dashboard uses `GET` and `PATCH /v2/org/branding`, with the fields `brand_name`, `brand_logo_url`, `brand_color` and `white_label`. `PATCH` is partial: a field left out keeps its value, and an empty string clears it. It answers `400` for a logo URL that is not `https`, a colour that is not hex, a name over 80 characters, or `white_label` without a `brand_name`. The public reads of shared pages, `GET /v1/receipt/{token}` and `GET /v1/rewrites/{token}`, carry a `branding` object: `{name?, logo_url?, color?, white_label}`. Every other field is unchanged. # Client reports Source: https://docs.attensira.com/agencies/client-reports Weekly, monthly and quarterly reports for a workspace: visibility as counts, the work shipped, before-and-after verdicts and next steps, on a signed link you can forward and print. A client report is one page that says, for a closed period, what the answers said, what we did, and what we can prove it did. It is built for forwarding to a client or a boss without editing. ## What a report covers A report covers the **last complete period**: | Kind | Period | | - | - | | Weekly | The ISO week, Monday to Sunday | | Monthly | The calendar month | | Quarterly | The calendar quarter | The period is counted in UTC days. A scheduled report picks the period that just ended by the automation's own time zone, so a Monday-morning report in Sydney reports the week that ended there on Sunday. ## What is on it * **Visibility per engine**, as "X of N answers". When only one answer was read it says "read once". Never a percentage, and never a comparison with the previous period. * **Work** shipped, waiting and failed, from the action log. * **Before-and-after** [re-read verdicts](/measure/before-and-after). This section and **Page changes** below are the only places movement language appears. * **Page changes**: up to 10 [changes seen on the site](/measure/page-changes) in the period that were matched to our work, worded as their evidence card words them. A rate appears only for a change whose re-reads proved it moved. The section is left out when there are none. * **Deciding sources**: up to 5. * **Competitors**, counted over the same answers as your own visibility. * **Next steps**: up to 3, from the [win plan](/measure/win-this-answer). * **Markets**, only when the workspace reads two or more [markets](/tracking/markets) or a market went unread: each market's visibility with its `n`, the differences between markets that clear the evidence floor, and each market an engine could not be read from, with the reason ("Not measured in Kenya on ChatGPT: that engine can't be asked from a location"). When no difference clears the floor, the section says so instead of naming one. * **Brand family**, only when the workspace has a confirmed [brand property](/tracking/brand-family): "you" rolled up, then how often each property counted as you and was cited. * The agent's words: a summary, highlights, the next period, and notes. The numbers come from the workspace's data for the closed period. The agent writes only the words, and a sentence claiming movement or giving a percentage is refused unless a re-read proved it. A section that could not be read is named on the report under **Not read for this report**, rather than left out. Publishing the same period again replaces its words and keeps its link. ## Your reporting standard The business profile has a **Reporting** group with one free-text field, **Reporting standard**: the tone, who reads the report, words to avoid. Every client report follows it and stores a copy of the standard it was written under. It never overrides the rules above: no percentages, and no movement that was not proven. ## Making reports Three automation templates publish them on a schedule: | Template | Runs | | - | - | | Weekly client report | Weekly, Monday 08:00 | | Monthly client report | Monthly, on the 1st | | Quarterly client review | Quarterly, on the 1st of January, April, July and October | Monthly and quarterly automations run at the automation's local time. A missed run catches up later in the same period. The existing **Write the quarter in review** template is now quarterly instead of manual. A report can also be made by hand with **Make one now** under **Results → Client reports**, or asked for in a chat. The automation needs the **Publish the client report** tool group in its allowlist. See [The agent's tools](/agent/agent-tools#client-reports). ## Sharing Each report has a signed link at `app.attensira.com/r/report/` that opens without an account. * The link lasts **180 days**. * It is revoked by rotating the workspace's receipt link (**Rotate** on the [receipt](/account/receipt#sharing-a-receipt-without-an-account)). One secret covers every link the workspace has shared, so rotating stops all of them at once. * The page prints to PDF. * It carries your org's [branding](/agencies/branding). Under white label it does not name Attensira. ## Email delivery Set the recipients under **Results → Client reports**: at most 10 addresses. Empty means the workspace owner. * **Email each scheduled report** switches off email for scheduled reports only. * A scheduled report automation emails each period **once**, even if the period is republished. * **Make one now** with **Email it to the recipients** ticked always sends a new email. * A report published from a chat is not emailed. Every report email has an unsubscribe link for the **client report** [notification category](/account/notifications). Unsubscribing stops client report emails for the whole workspace, not only for the recipient who clicked. ## Over the API The dashboard reads and publishes reports through its `/v2` report routes. A shared report's data is readable without an account at `GET /v1/report/{token}`, with the token as the only credential. The latest published report for a cadence is `get_report` over [MCP](/mcp/tools#get_report) and [`GET /v1/reports/latest`](/api/endpoints#get-v1reportslatest) over the REST API, which returns the markdown in full. # Client reviewer link Source: https://docs.attensira.com/agencies/client-reviewer-link One private link per workspace that lets your client read and approve or decline the suggested changes, and nothing else. An agency often needs the client's yes before a change goes out, and the client should not need an Attensira account to give it. The client reviewer link is that yes. ## What the reviewer sees There is one link per workspace. Whoever holds it can see that workspace's **suggested changes**: what the change is, why, where it lands, the current and proposed text, and the source links (only `http` and `https` links are shown). For each one they can **approve**, or **decline** with a reason: wrong, not now, already done, or never touch this. They cannot see settings, billing, analytics, tasks, outreach contacts, or any other workspace. ## Creating and revoking it Create it from the workspace's card on [Clients](/agencies/clients), under **Client link**. * The link is valid for **90 days**. * **New link** replaces it. The old link stops working immediately. * **Turn off** revokes it. It is separate from the [shared receipt link](/account/receipt#sharing-a-receipt-without-an-account). Rotating either one does not affect the other. ## What an approval does An approval through the link is the same approval as one in the workspace, recorded as **Client reviewer (shared link)** so you can tell who said yes. * Approvals are refused when the org's credits are used up. * Declines always go through. ## Over the API Dashboard routes (signed-in session): * `GET`, `POST` and `DELETE /v2/projects/{id}/reviewer-link` read, create or replace, and revoke the link. Each answers `{active, token?, url?, issued_at?, expires_at?}`. Public routes, where the token is the credential: * `GET /v1/review/{token}`: the suggested changes. * `POST /v1/review/{token}/items/{itemId}/approve` * `POST /v1/review/{token}/items/{itemId}/decline` with `{"reason": "wrong" | "not_now" | "already_done" | "never_touch"}`. These review routes are not MCP tools. # Clients: every workspace on one page Source: https://docs.attensira.com/agencies/clients The org home at /clients: each workspace's loop status, work shipped, what waits on approval and visibility as counts, with bulk approval across workspaces. If you run Attensira for several brands, each brand is a workspace in one org. **Clients** (at `/clients`, in the sidebar when the org has more than one workspace) is the org home: one card per workspace. On the [Agency plan](/account/plans#agency) there is no limit on client workspaces. No plan counts workspaces; an org's prompt slots, responses and credits are shared across all of its workspaces. ## What each card shows * **Loop status**: working, idle or blocked, with the reasons. * **Shipped**: work shipped in the last 7 days. * **Waiting**: items waiting on approval, with open tasks counted separately. * **Visibility**: "named in X of N answers" over the last 7 days, summed across engines. There is no week-over-week comparison on the page. ### Loop status on the card For now the card's status is a summary derived from the workspace's automations: | Status | When | | - | - | | **Blocked** | No scheduled automation, all of them paused, or the credits are used up | | **Idle** | An automation is scheduled but has never run, or nothing has run in 48 hours | | **Working** | Otherwise | It will read [loop health](/agent/loop-health) instead once that is wired into the card; loop health is the fuller answer, and each workspace's Home already says when its loop is blocked. ## Approving across workspaces Select suggested changes on several cards and approve them in one action. * **Select all** covers only the changes each card shows, up to 5 per card. * A selection over 100 changes is sent in batches. * Each change is approved exactly as it would be in that workspace's Work page: the same approval mode, change caps and checks apply. * Failures are reported per item, so one refused change does not hide the others that went through. ## Client link Each card has a **Client link** control that creates the [client reviewer link](/agencies/client-reviewer-link) for that workspace. ## Over the API These are dashboard routes (signed-in session): * `GET /v2/org/overview`: the cards. * `POST /v2/org/inbox/approve` with `{"items": [{"project_id": "...", "item_id": "..."}]}`, at most 100 items. It answers `{approved, failed, results: [{project_id, item_id, ok, state?, error?}]}`. # The agent's tools Source: https://docs.attensira.com/agent/agent-tools The tool groups an automation can be given, and the tools inside them that deliver work: reading the web, pull requests, articles, LinkedIn posts, Notion posts, community replies and client reports. These are the tools the Attensira agent itself uses inside a chat or an automation run. They are different from the [MCP tools](/mcp/tools), which are how your own assistant calls Attensira. ## Tool groups An automation's reach is set by **tool groups**, shown as "What it can look at" and "What it can change" on the automation's Configuration tab. A tool is available to a scheduled run only if the automation's allowlist includes its group. The groups that deliver work: | Group | Tools | What it can change | | - | - | - | | Your own data | Readings, pages, deciding sources, `win_plan`, the business profile, and (when a Notion blog is mapped) `list_blog_posts`, `read_blog_post` | Nothing | | The open web | `web_search`, `web_fetch` | Nothing | | Your site's code on GitHub | `list_repo_files`, `read_repo_file`, `search_repo_code`, `resolve_url_to_file`, `propose_fix` | Opens a pull request | | Make things | `write_document`, `read_document`, `draft_social_post`, `generate_image`, `write_skill` | Files an article or post for your approval | | Rewrite a passage on your site | `propose_edit` | Files a before/after. Publishes nothing | | Update a post on your Notion blog | `propose_blog_update` | Files a before/after of a Notion post | | Find who to contact | `find_outreach_targets`, `find_contact`, `draft_outreach` | Files an email draft. Never sends | | Answer questions where people already argue | `find_community_threads`, `propose_community_answer` | Files a reply draft. Never posts | | Get listed on directories | `propose_listing` | Files a prefilled listing. Never submits | | Publish the client report (`reports`) | `publish_client_report` | Publishes a [client report](/agencies/client-reports) | A connected plugin, such as Notion, is its own group named after the plugin. If a scheduled run needs an integration that the automation's tool list does not include, nothing is added to your Inbox, because connecting it would not help that run. It is recorded on the run's transcript instead. To fix it, add the tool's group to the automation. ## Reading the web **`web_fetch`** reads one page. **`find_contact`** reads the pages of a site to find who runs it. Both, and the refresh of your site's page map, go through our web reader. * Each open-web read is metered in credits. See [Credits](/account/credits). * `web_fetch` reads Reddit threads free of charge, and returns the post, the top comments, whether the thread still takes replies, and the subreddit's rules. * When the web reader is unavailable, reads fall back to a plain HTTP fetch with **no JavaScript rendering**. Pages built entirely in JavaScript may come back empty then, and the agent says so instead of guessing what the page says. **`find_community_threads`** lists the community threads (Reddit, Hacker News, Stack Overflow and Stack Exchange, Quora) that answers to your tracked prompts cite. It is free and read-only. See [Community answers](/agent/community-answers). ## Pull requests **`resolve_url_to_file`** finds the file behind a URL in your connected repositories. It also reports where the page's title and description are defined, and where a new page at that URL would go. **`propose_fix`** proposes a change to a file as a pull request (see [GitHub](/agent/github)). * Proposing a path that does not exist creates a **new file**, such as a new post under a content root. * `prompt_ids` (optional) names the tracked prompts the change is meant to move. Those are the prompts [re-read after it ships](/measure/before-and-after). Without them no before-and-after is measured. ## Articles and posts **`write_document`** writes an article. It also appears as a work item under [Inbox → Waiting on you](/agent/inbox#written-for-you) and as a draft on the content plan. Where it goes on approval depends on what is connected: 1. A connected GitHub repository with a blog directory: approving opens a pull request that adds the post as a new file, with front matter that follows your existing posts. 2. A mapped [Notion blog](/agent/notion): approving sends it to Notion as a draft page. 3. Otherwise, a copy handoff: you paste it into your site and [mark it shipped](/agent/inbox#mark-shipped) with the live URL. Articles always wait for a person's yes, in every mode. Revising an article that is still waiting updates the same work item. Briefs, emails and notes the agent writes are not filed as work. **`draft_social_post`** leaves a **LinkedIn post** in your [Inbox](/agent/inbox), ready to copy. Attensira never posts to LinkedIn: you post it from your own account and mark it shipped with the post's link. The post always carries the link to its source page. ## Notion posts **`list_blog_posts`** and **`read_blog_post`** list and read the posts in your mapped Notion blog. **`propose_blog_update`** proposes a new version of one as a before/after. All three are offered only when a Notion blog database is mapped. See [Notion blog](/agent/notion). ## Client reports **`publish_client_report`** publishes the [client report](/agencies/client-reports) for the period that just ended. It runs only when the automation's allowlist includes **Publish the client report**. The agent writes only the words (summary, highlights, next period, notes); every number on the report is read from the workspace's data for the closed period. ## House skills The agent follows written procedures for recurring jobs, called house skills. Among them: | Skill | Used by | | - | - | | `orchestrate/daily-win-plan` | The [Daily win plan](/agent/autopilot#the-daily-win-plan) and the Event responder | | `report/client-weekly` | The Weekly client report | | `report/client-monthly` | The Monthly client report | | `report/client-quarterly` | The Quarterly client review | # Approval modes Source: https://docs.attensira.com/agent/approval One workspace setting decides how much the agent does on its own before anything leaves the workspace — Ask, Draft or Act. ## One setting, every automation and chat Approval mode is a single workspace setting: **Ask**, **Draft**, or **Act**. It applies to every [automation](/agent/automations) run and every [chat](/agent/chats) session in the workspace — there is no per-automation override. A [project](/agent/projects) can make its own crew stricter than the workspace, never looser; see [Projects can be stricter](#projects-can-be-stricter). When the Chief of staff hands work to a project, the hand-off is gated too. See [Handing work to projects](#handing-work-to-projects). ## Ask The agent stops and asks before anything that leaves the workspace — publishing a page, sending mail, spending with a third party. A card shows exactly what it wants to do. The run then waits for you, for up to **7 days**. You can answer it on the card in the run, or from its row in your [Inbox](/agent/inbox): **Approve once** lets the run make that one call, and **Decline** takes a reason and an optional note, which the run reads before it carries on without the call. If nothing is decided in that window, the run closes without doing the thing, and files a row in your Inbox saying so. See [what the agent shows you](/agent/what-the-agent-shows). ### Approvals are answered as asked An approval cannot be edited, in the Inbox, the API or MCP. Your yes is bound to the exact call the run asked about, down to every argument, so a changed argument would be a different call that nobody approved. If the call is not quite right, decline it and say what to change in the note; the run reads your note before it decides what to do instead. This is different from a proposed article, post or rewritten passage, which you can edit before approving. An edited change always counts as a claim, so it waits for your yes in every mode and never goes out on its own. See [Editing before you approve](/agent/inbox#editing-before-you-approve). In Ask, a goal that falls behind its pace line does not start a re-plan run on its own. One row in your Inbox offers the re-plan instead. In Draft and Act the re-plan run starts, and the work it plans follows the mode like any other work. See [Goal re-plans](/agent/decisions#goal-re-plans). ## Draft (default) The agent does everything inside the workspace on its own, and files everything else as work awaiting your approval. Nothing is published, sent, or written outside the workspace until you approve it. This is the mode every new workspace starts in. ## Act The agent carries **safe changes** through on its own, without stopping. Safe means hygiene work: structured data, meta descriptions, alt text, internal links, heading structure. Anything touching a claim — a number, a price, a product or competitor name, a commitment — is filed for your approval **in every mode**, Act included. A change you edited before approving is always a claim. Code blocks and code samples are never edited, in any mode. A safe change goes ahead on its own only when the [draft check](#the-draft-check) passes it. A change the check holds waits for you, whatever the mode. ## Projects can be stricter Each [project](/agent/projects) has its own setting, **On its own**, with the same three modes under the names its settings use: **Ask first** (Ask), **Leave drafts** (Draft) and **Act**. Every run in the project works under the **stricter** of the project's setting and the workspace's: | Workspace | Project | The crew works as | | - | - | - | | Act | Ask first | Ask | | Act | Leave drafts | Draft | | Draft | Act | Draft | | Ask | Act | Ask | So a project can hold one area of work to a tighter rule, such as a project that writes about pricing in an otherwise Act workspace, but it can never loosen the workspace. In the project's settings, the options looser than the workspace are shown switched off, with the reason: "Your workspace is set to Ask first; a project can be stricter, not looser." If the workspace is tightened after a project was set, the project shows what it was saved as and that the crew now works at the workspace's setting. If the project's setting cannot be read when a run starts, that run works as **Ask**: a fault never gives the crew more freedom than you set. Everything else on this page holds inside a project too: the classes below, the change caps, and anything that cannot be undone, like sending an email or spending money, asking first whatever either setting says. ## What Attensira changes on its own, and what it always asks about Every proposed change is put into one of three classes before anything happens to it. The class is worked out from the change itself — the before-and-after of the file, and where it lives — never decided by the agent about its own work. JSON-LD and FAQ structured-data blocks; meta descriptions and `og:`/`twitter:` tags; image alt text; internal links to your own site; heading levels (not heading words); whitespace. A change is safe only if **every line it touches** is on this list. One line that is not makes the whole change a claim. Every structured-data addition carries the note: "hygiene, not a lever: we do not expect this alone to move an answer." Any added or removed line containing: a price or currency amount; a percentage; a number with a unit ("40 ms", "50,000 events per second"); a promise word (guarantee, guaranteed, always, never, uptime, SLA, refund, unlimited, free, fastest, best, leading, "#1", a "99." figure); a competitor's name; one of your product names; rewritten heading words; a rating, review count or price inside structured data; anything that is a sentence rather than markup. Wrapping does not help — a price inside a schema block is still a price. Approving a claim publishes it, whatever the mode. **Words in source files (rule `source-copy`).** In `.ts`, `.tsx` and `.js` files, a change may touch only the words inside string literals: titles, descriptions, copy in data files. Such a change is always a claim and always waits for a person. Code in fenced blocks, `
` or ``; source files (`.js`, `.ts`, `.tsx`, `.go`, `.py` and the rest), apart from the words inside string literals described under Claim; CI configuration, workflows, dot-directories; lockfiles and dependency manifests; dotfiles; anything outside the directories you have said hold your pages.

    In a `.ts`, `.tsx` or `.js` file, a string literal that is really code is refused as source: an `import` or `require` path, a `'use client'` or `'use server'` directive, a raw-HTML (`__html`) payload, or an argument to an evaluator. Any other change to a source file is refused too.

    A proposal that turns out to be one of these is refused, and a blocked row in the [Inbox](/agent/inbox) names the rule that stopped it.
  


**Editable directories** default to `/docs/**` and `/content/**`, plus any content roots you named when connecting the repository, plus `llms.txt`, `robots.txt` and sitemaps at the root. Changeable in project settings. Every change is recorded with who and when.

Every decision carries the rule that decided it, in plain words — the same rule you see above, not a paraphrase of it. The rules are scored against a set of 42 adversarial changes on every change to the classifier; the current miss rate is 0%.

## The draft check

Every draft the agent writes in your name is checked against your rules and ours before anyone can approve it. The agent does the checking and the rewriting. You write a rule once, and every draft after it is held to that rule.

### What is checked

| Draft | What is read |
| - | - |
| Articles, rewrites, social posts, ad campaigns | All of it. Every word of the proposal is ours. |
| A fix to a page or file | Only the lines it adds. The rest of the page is your own words, so a fix to a meta description is never stopped by something the page said before we touched it. |
| Outreach emails | The subject and the body. |
| Community replies | The reply. |
| Ad changes (a pause, negative keywords, a budget move) | Nothing. They carry no copy. |

### Which rules

* **Your compliance rules** (**Customize → Guardrails → Compliance**): what may never be said in your name. A draft that clearly breaks one is **stopped**. One that may break it goes to **review**.
* **Your voice rules** (**How agents write**): how the agents should sound. A voice rule never stops a draft. One that may not follow it goes to **review**.
* **Words to avoid**: phrases listed on a rule of either kind, forbidden outright. They are matched exactly, ignoring case, on whole words. One hit **stops** the draft, wherever it appears in what is read.
* **Our house rules**: run on every draft in every workspace, on every plan, whether or not you have written any rules of your own.

A rule applies to every kind of draft unless you pick where it applies: pages, articles, emails, community replies, social posts or ads. Only rules that are switched on are checked.

| House rule | What it holds a draft to |
| - | - |
| Our costs stay ours | The draft never states what producing it takes on our side. What you charge, a competitor's public figures and the credits you spend are fine. |
| Quote only what was read | No quotation, testimonial, statistic, study or customer story without a named source a reader could check. No claim about what a page, a review or an AI answer says unless the draft quotes or cites it. |
| Noise is not change | No claimed change in rankings, traffic, visibility or AI answers without the numbers behind it: before and after, and how many searches or answers were measured. No credit to a piece of work without that measurement. |
| No action claimed without a yes | The draft never says something was published, sent, posted, paid, deleted or signed up for on your behalf unless it was. It never asks a reader to treat a reply, or no reply, as approval. |
| Text that tries to instruct | No instruction aimed at an AI system or an automated reader, and no instruction found on a page, in a reply or in search results repeated as if you had said it. |
| Internal details stay internal | No internal tool names, raw `field=value` pairs or record ids in prose, and no stray footnote markers or half-finished self-corrections. Applies to articles, emails, community replies and social posts, not to page fixes or ads, where HTML and settings are the content itself. |

Some house rules have a certain part that needs no judgement, such as an internal field name in an article. It is matched first, on the words a reader sees. Code blocks, inline code and link addresses are set aside, because in a tutorial they are the subject, not a leak.

### The verdict

The check gives each draft one verdict, recorded on the draft with the exact text and the exact rules it was checked against.

| Verdict | What it means | What happens |
| - | - | - |
| **Checked against N rules** | Every rule that applies was checked, and none is broken. | The draft waits for whatever yes it needed before. The check never removes a step. |
| **Review** | A rule may be broken. | You can approve it, after reading it. In Act, a safe change marked for review is not carried out on its own: it waits for you. |
| **Stopped** | A compliance rule is broken, or the draft contains a phrase a rule forbids. | It cannot be approved as written, by anyone. Revise it first. |
| **Not checked** | The check could not finish. | It cannot be approved, published or sent until it is checked. |

N counts your rules and our house rules together.

A draft that could not be checked is never treated as passed, because a draft nobody checked is not a draft that is fine. The check says why it could not finish:

* your rules could not be read;
* the check did not answer for every one of your rules;
* the draft is too long to check in one go. Split it, then check it again.

One row in your [Inbox](/agent/inbox) says **We could not check N drafts against your rules**. Its **Retry** opens the draft, and approving it checks it again. The row clears itself once no draft in the workspace is still waiting on a check. If a stop was already found before the check broke off, the stop stands.

Our house rules work differently when the check cannot answer. A house rule it could not answer is left out of N and does not change the verdict, and its certain part still runs. Before the house rules existed, a draft was not checked against them at all, so a gap in the check does not hold every draft in every workspace.

### When the agent hits a stop

* **An article** that is stopped is not filed at all. The agent is told which rule it breaks and revises it once, and the revision is checked again. If the same article is stopped a second time, it is never filed for approval. The agent asks you instead, in your Inbox: what the article should say instead, or what source backs it. You can also tell it to leave that part out and write it again, or to drop the article. The draft is in the conversation beside the question.
* **Any other draft**, and an article that is not stopped, is filed with its verdict. A stopped one cannot be approved or sent until it is revised.
* On a **review**, the agent revises once if it can keep the rule without losing the point. Otherwise it says which rule and why.

### Where the verdict is enforced

* **Approving.** A stopped draft is refused, and so is one that still cannot be checked. The draft is checked once more first; when neither the draft nor the rules changed, the last verdict answers. An edited draft you approve, in the dashboard or with `edited_value` over MCP, is checked as your words before it is saved. This holds wherever the yes comes from: the dashboard, the [MCP server](/mcp/tools#approve_work_item) or the [REST API](/api/endpoints). A refusal is a `conflict` (`409`) whose message names the rules.
* **Publishing.** What was approved is what was checked. A draft whose verdict is stopped or not checked is refused. So is a draft that changed after its verdict, with "this draft changed after it was checked against your rules". Check it again, then approve it.
* **Sending.** An outreach email is checked once more just before Attensira sends it from your mailbox. A stopped email, or one that still cannot be checked, is not sent. An email edited between that check and the send is not sent either.
* **Act mode.** A safe change goes ahead on its own only on a passed verdict. Anything else waits for your approval, with the note "Held by the draft check:" and the rules it is held on.
* **Notion.** An article whose verdict is anything but passed is kept as a draft row in [Notion](/agent/notion), even in Act mode, with the same note. Your team publishes it by changing its status.

The check only ever adds a stop. It never makes a change safe, never approves anything, and never changes which class a change is in. A passed draft still needs every yes it needed before.

Changing a rule changes the verdict for every draft after it. A verdict given under the old rules is checked again when the draft is next approved.

Something approved before drafts were checked has no verdict. It publishes under the rules it was approved under.

## Handing work to projects

When the Chief of staff [hands work to a project](/agent/delegation), the hand-off itself is an action and is gated like one:

* **Ask**: an approval card first. No task is filed until you approve the hand-off.
* **Draft** and **Act**: filed without asking. A task stays inside the workspace, and cancelling its session undoes it.

Work you hand off yourself is your own action and is not put through the gate.

That covers only the filing. Each task's work runs in its own session, under the stricter of the workspace's mode and the project's, and every change it proposes is classified and gated exactly as below. Routing a line to a project never approves anything and never changes a mode.

## Articles and social posts

**A new article** the agent writes is filed in your [Inbox](/agent/inbox) for approval, and goes where your connections allow, in this order: a pull request in a connected repository with a blog directory; a draft in your connected [Webflow](/agent/cms) or [Shopify](/agent/shopify) site (WordPress cannot take new articles yet); a draft page in [Notion](/agent/notion); otherwise a copy you paste yourself. Approving it creates the draft. Attensira never publishes a new article in your CMS: it stays a draft until you publish it there.

**A LinkedIn post** is never posted by Attensira, in any mode. It waits in your Inbox to copy, with its image and alt text when it has one. The time it is planned for, the time you asked for or the first of three suggested weekday mornings, brings a reminder back to your Inbox; you post it from your own account and mark it shipped with the post's link.

## Google Ads spend

[Google Ads](/agent/google-ads) changes are classed by the same idea, applied to money: what each change does to the campaign, read from the campaign's state before and after it, never from what the agent says about it.

| Class | Google Ads changes | In Act | In Ask and Draft |
| - | - | - | - |
| Safe | Adding negative keywords; pausing keywords or a campaign; lowering the daily budget or the target cost per conversion; promoting an ad variant that already ran | Goes out on its own, within the [change caps](#change-caps) | Waits for your yes |
| Claim: spends money, says something new, or cannot be undone | Creating or turning on a campaign; raising a budget or a target, or removing the target; adding keywords or turning one on; removing a negative keyword; new ad copy; anything not recognised | Waits for a signed-in person | Waits for a signed-in person |

Two rules apply to Google Ads alone:

* **Spend is approved in the app.** A claim is approved only by a person signed in to the Attensira app. An API key, an assistant over MCP and a client reviewer link all get `403 Forbidden` with the reason `spend_approval_in_app`. Safe changes can be approved from anywhere you approve other work.
* **The spend guard pauses without asking.** When a campaign's spend this month nears the monthly budget you approved, the [spend guard](/agent/google-ads#the-spend-guard) pauses it, in every mode, with no approval behind it. It can only pause, never turn anything on.

Ad spend is paid to Google from your own Google Ads billing. It is not our fee, and it does not count toward the credit cap below.

## What no mode changes

These hold regardless of the mode you pick:

* We merge a [pull request](/agent/github) only when you press **Merge** in your Inbox, or when you switched on [auto-merge for safe changes](#auto-merge-for-safe-changes), and then only safe changes. Approving a change opens its pull request; it never merges it. A pull request is a proposal; closing it undoes it.
* We never delete anything irreversibly without an explicit yes.
* We never send mail from your domain on your behalf without one.
* A draft the [draft check](#the-draft-check) stopped is never approved as written. One it could not check is never approved, published or sent.
* We never start or raise [Google Ads](/agent/google-ads) spend without a signed-in person's yes in the app.

## Changing your mind is retroactive

Mode is checked again when the work actually runs, not only when it was proposed. If you tighten the setting between a chat suggesting something and an automation acting on it, the tighter rule applies at run time.

Your own approval of one specific change still goes ahead even if you have not changed the mode — approving an action and changing the workspace default are different decisions.

Every change to the setting is recorded: who changed it, and when.

## Why your workspace is in Draft

New workspaces start in **Draft**: the agent does the work and waits for your yes before anything leaves the workspace.

Workspaces created before September 2026 had a setting with this same name, but it was only a default applied to new automations — it did not govern outward writes. When it began to govern real permissions, every existing workspace was moved to **Draft** and its owner was emailed once about the change. Workspaces created since do not get that notice.

If you want the agent to carry hygiene changes through on its own, choose **Act** — Draft does not change on its own.

## Auto-merge for safe changes

`auto_merge_safe_changes` is off by default. When you switch it on, a **safe** change (structured data, meta tags, alt text, internal links) that went out unattended under **Act** and within the weekly caps is squash-merged by Attensira, at exactly the commit Attensira pushed, once GitHub reports the pull request mergeable with every required check and review satisfied.

Titles and headings are claims and never auto-merge.

The pull request is handed back to you, with a note on the work item, and not merged if any of these happens:

* anyone else pushes to the branch;
* it is converted to a draft;
* it conflicts;
* a check fails;
* GitHub refuses the merge;
* the setting is switched off;
* it has waited more than 14 days since it opened.

The pull request's body says whether it will auto-merge.

## Change caps

Act mode is not unlimited. Each kind of change carries its own limits, and they are yours to set under **Settings → Autopilot** (at `/settings/agents`).

| Limit | Default | What it counts |
| - | - | - |
| Changes a week | 2 | Changes we applied on our own, across the whole workspace |
| Changes a week, per page | 1 | The same count, for one page, so one page cannot take the whole allowance |
| Credits a month | No cap | Credits this workspace spent this month. Optional, and secondary to the two counts above |

Only changes we made without you count. A change you approved yourself is not rationed; the caps exist to bound what happens while you are not looking.

Limits are set per change class. Hygiene changes (structured data, meta descriptions, alt text, internal links, heading structure) can be set to act on their own. Claims, anything asserting something a customer could hold you to, never act on their own, whatever the workspace default is set to. There is no setting that changes that.

### When a cap is reached

The change stays approved and waits:

* it shows in your Inbox as **Waiting: weekly cap**, on the in-flight tab rather than in "needs you"; you already said yes, and we will not ask twice;
* one item appears in your Inbox that week, and one message reaches you, however many changes are waiting behind the cap;
* it goes out on its own once the week rolls over or you raise the cap. Nothing has to be un-paused by hand and nothing is thrown away.

A cap is never a silent stop. If your agent has gone quiet, the Inbox says why.

### Weeks and months

A week starts on Monday in your workspace's own time zone, and a month on the first, also in your workspace's time zone.

The credit cap is set per change class but counted **per workspace**: it counts what this workspace was charged this month, less refunds, and nothing another workspace in the organisation spent. That is agent work and the reads of your own pages that monitoring makes, the same spend [Costs](/account/costs) shows for the workspace. One credit is one cent.

This cap is not the workspace's [monthly credit cap](/account/credits#monthly-credit-cap), which pauses the workspace's automations and counts a UTC month. Both count the same spend; only where their month starts differs.


# Automations
Source: https://docs.attensira.com/agent/automations

A standing instruction the agent runs on a schedule or when something changes, with a run history — and an idempotency key so a retry never bills twice.

## What is an automation?

An automation is an instruction you write once and the agent carries out on a schedule, or when something happens. It is the same kind of request you would type into a [chat](/agent/chats) — "summarise which prompts gained or lost citations this week and name the pages involved" — attached to a trigger so you stop having to ask.

Each execution is a **run**. Runs are stored, so an automation accumulates a history you can read back and compare.

Every workspace has one automation from the start: the **Daily win plan**, which does up to three pieces of work toward your prompts every morning. See [The autopilot loop](/agent/autopilot).

### Re-plans when a goal falls behind

When a goal falls behind its pace line, the agents can run the Daily win plan once more, focused on that goal. That run is a **re-plan**: it plans at most three pieces of work toward the goal and files them under your [approval mode](/agent/approval), like any other work. It is not an automation you create or pause; it comes from the 05:00 goal check. It happens at most once per goal per week. The run says why it started: **Because "your goal" is behind pace.** In Ask mode nothing starts, and a row in [Work](/agent/inbox) offers the re-plan instead. See [Goal re-plans](/agent/decisions#goal-re-plans) for when the check fires.

## Creating one

The Automations page has one **New automation** button, which opens a dialog. Five starter cards open the same dialog pre-filled:

| Starter | When it runs |
| - | - |
| Daily win plan | Every day, 06:00 |
| Event responder | On a change |
| Weekly client report | Every week, Monday 08:00 |
| Reddit watch | Every week, Tuesday 09:00 |
| Content refresh | Every week, Wednesday 07:00 |

A starter you already use shows **Running** or **Paused** and links to your automation instead.

Other built-in templates include the **Monthly client report**, the **Quarterly client review** and **Write the quarter in review**; see [Client reports](/agencies/client-reports).

## When it runs

| Trigger | Wire value | Runs |
| - | - | - |
| Every day | `daily` | Once a day at the set time |
| Every week | `weekly` | Once a week at the set time, on the day you pick (`run_on_weekday`, 0 = Sunday to 6 = Saturday). With no day chosen, at the start of the week |
| On a change | `event` | When one of the chosen events happens to the workspace. See [Event automations](/agent/autopilot#event-automations) |
| When I ask | `manual` | Only when started by hand or with `run_automation` |
| Monthly | `monthly` | On the 1st of the month, at the automation's local time |
| Quarterly | `quarterly` | On the 1st of January, April, July and October, at the automation's local time |
| Once | `once` | One time when fired with `run_automation`, then it retires |

The trigger picker offers **Every day**, **Every week**, **On a change** and **When I ask**. Monthly and quarterly schedules come with the client-report templates; a missed monthly or quarterly fire catches up later in the same period. **Once** is no longer offered for new automations; existing run-once automations keep it, and one that has run shows as **Finished**.

Over MCP and the REST API, `trigger` takes `daily`, `weekly`, `event`, `manual` or `once`.

The **default trigger** setting under **Settings → Autopilot** does not offer **On a change**.

Times are in the automation's time zone. Created over MCP or the API with no time zone, an automation uses the workspace's zone (UTC only if the workspace has none), and with no trigger it runs **daily at 09:00**.

## The agent can offer one

You do not have to build an automation from scratch. When you ask the agent for something recurring in a chat — "check this every week", "let me know when this slips" — or when it judges that an answer it just gave is only useful if somebody re-reads it later, it will offer to save the work as an automation: a name, a cadence, the tools it needs, and the instruction it would run.

That offer is a card in the chat, not a saved automation. Nothing is created until you confirm it, and confirming drops you into the same automation you would have written by hand — editable and deletable afterwards like any other.

A chat cannot propose an **On a change** automation. Create those from the Automations page, the API or MCP.

## Writing the instruction

Write it as a task, not a topic. "Weekly visibility" gives the agent nothing to do; "compare share of voice for the Pricing topic against the previous 7 days, list every prompt whose change was significant, and say which were not measured" tells it exactly what to produce and how to handle gaps.

Be explicit about the window, the scope, and the shape of the output you want. The agent reads your workspace's Prompts, Topics, Models, Answers, and citations — the same data as a chat, under the same rules. Null still means not measured, and a delta below the 95% threshold still means no change we can prove, not zero.

Whatever the automation writes outward from the workspace — publishing, sending, spending — is still governed by your workspace's [approval mode](/agent/approval), the same as a chat. An automation in a [project](/agent/projects) runs under the stricter of the workspace's mode and the project's.


  Pin the automation's window to the cadence. A weekly automation that reads the last 7 days produces comparable runs; one that reads "recently" does not.


## Cadence, and what actually bounds freshness

The cadence sets how often the automation runs. Set it against how fast the underlying data can change, which is bounded by measurement, not by scheduling.

Each Prompt × Model × country is measured at most once per workspace-local calendar day. Sampling depth is n=1 on Starter and n=3 on Growth, Business and Agency. Running an automation more often than the data is refreshed produces repeated runs over the same measurements and spends credits doing it.


  An hourly automation over daily-sampled data is a recurring bill for the same answer. Match the cadence to the sampling, not to your reporting habit.


## The automations list

Each row shows:

* **Last result**: the newest run's summary, its failure reason, or "Waiting on your approval".
* **Prompts moved**: the tracked prompts the automation's runs worked on in the window, shown as "N worked on", or an em dash when the runs have not reported what they touched. Whether an answer actually moved is confirmed on the prompt after its next reading and its [re-reads](/measure/before-and-after), never inferred here.
* **Next run**: the next scheduled time. Event automations show "On the next change".

Runs, credits and failures are on each automation's own page. Paused and finished automations are listed too; filter for them with **Paused** and **Finished**.

## The automation page

The **Configuration** tab uses plain labels: **What it does** (the instruction), **When it runs** (the trigger, and the event types for an event automation), **Speed** (how soon a run starts once it is due; we pick the models and tools), **What it can look at** and **What it can change** (its [tool groups](/agent/agent-tools#tool-groups)), **How much it does alone** (the workspace's [approval mode](/agent/approval)), and **Monthly spending limit**.

For an automation in a project, **Instructions** takes the place of **What it does** at the top and shows the instruction word for word, because it is the crew's standing work and is written to be read and edited. **How much it does alone** then says it is the stricter of the workspace's setting and the project's.

Outcomes are read at topic level, and the summary row is labelled **All models**.

## Automations in a project

A [project](/agent/projects)'s crew does its standing work through ordinary automations. Each one belongs to one project, for good, and carries an author:

* **By Attensira**: created when the project was set up, one for each thing that wakes a crew member. The project's Chief of staff may change these when you steer the project, and every change it makes is posted to the feed as **Plan updated**.
* **By you**: written by a person. The Chief of staff never changes one of these on its own; it proposes the change and you confirm it.

Either kind is yours to edit, test, switch off or delete. The workspace's Automations page lists every automation; once the workspace has a project, a **Project** column says which project each belongs to, and a project's automation opens inside its project.

A project's automations do not run while the project is paused or archived, or once the project has reached its [monthly cap](/agent/projects#the-monthly-cap).

## Testing an automation

**Test** on the automation's page runs it once, now, so you can see what it does before you rely on it. A test is a real run: it does the work, it is paid for like any other run, and it meets the same approval mode and caps.

What a test does not do is count as the automation having run. **Test runs don't count as this week's run**:

* the schedule is untouched, so a test this morning does not stop today's scheduled run;
* a run-once automation is not used up by a test;
* a test works on an automation that is switched off, so you can try it before turning it on. A run-once automation that has already run is finished and cannot be tested.

Test runs are marked **Test** in the run history, and their [Because line](/agent/projects#because-lines) says they were started as a test. Over MCP and the REST API there is no test mode: `run_automation` and `POST /v1/automations/{id}/runs` start an ordinary run.

## Runs and run history

Every run records when it fired, what the agent did, and what it produced. Open the history to compare this week's output with last week's, or to check whether a gap in a report came from missing measurements rather than from the agent. **Replay** on a run opens it [watched back step by step](/agent/what-the-agent-shows#watching-a-run-back).

Run history is also where you look when an automation seems to have stopped saying anything interesting. A run that reports no significant change is doing its job — it is telling you the movement was below the noise floor.

### Why a run started: "Because…"

Every run of an automation, and every run a finding starts, carries one sentence saying why it started, beginning **Because**. It shows at the top of the run's replay and, for a run in a project, on its post in the project's feed.

| What started it | The sentence reads |
| - | - |
| Its schedule | **Because "Weekly client report" runs every Monday at 08:00 BST** |
| Events | **Because competitor.example changed pages on its site, and 2 more changes** |
| A [finding](/agent/findings) | **Because example.com stopped being cited** |
| You, by hand | **Because "Weekly client report" was started by hand** |
| A test | **Because "Weekly client report" was started as a test** |

The sentence is built from facts we recorded: the automation's schedule, in its own time zone, or the event that woke it. It is never a model's summary. For an event run it names the oldest event in the batch and counts the rest. It never quotes what a competitor's page said; that goes to the run as material to read.

### When a run does not happen

A scheduled run that did not happen is not silent. A paused, retired or manual automation, a run skipped for credits, and a run still waiting on your answer each show up in [loop health](/agent/loop-health#why-a-scheduled-run-was-skipped) with the reason. So does an automation whose [project](/agent/projects#paused-and-archived-projects) is paused or archived (`work_area_inactive`) or has reached its monthly cap (`work_area_cap`).

An event automation can also skip a competitor's change as **not worth a run**. Before a competitor's page change wakes the automation, a [decision](/agent/decisions#worth-a-run) asks whether the change is worth a run of this automation, given its instruction, its project's direction and its recent runs. A confident no closes that event as skipped, with the reason "not worth a run:" and the event, and the other events in the batch still fire. When every event in a batch is skipped, nothing runs, and loop health records the skip as `not_worth_run`. A skip starts no run, so it spends no credits and does not count toward the automation's three event runs in 24 hours. Every other kind of event wakes the automation as before, and so does a competitor's change when no decision can be made.

If a scheduled run needs an integration that the automation's tool list does not include, nothing is added to your Inbox, because connecting the integration would not help that run. It is recorded on the run's transcript instead. To fix it, add the tool to the automation.

## Runs cost credits — and the idempotency key

A run spends **Credits**, because it runs a model and the tools it needs. Scheduled runs and manual runs cost the same. Credits are the workspace's unit of metered agent work; creating the automation is free, executing it is not.

Manual and API-triggered runs accept an **idempotency key**. Send the same key twice and the second request does not start a second billed run — it resolves to the first one. This is what makes a retry safe when a network call times out or a script re-fires.

Over MCP, `run_automation` takes an `idempotency_key` parameter, which is sent upstream as the `Idempotency-Key` header:

```json MCP theme={null}
{
  "name": "run_automation",
  "arguments": {
    "id": "aut_7f3c",
    "idempotency_key": "weekly-pricing-2026-09-01"
  }
}
```

Over the [REST API](/api/endpoints) you send the header directly:

```bash REST theme={null}
curl -X POST https://api.attensira.com/v1/automations/aut_7f3c/runs \
  -H "Authorization: Bearer atn_live_" \
  -H "Idempotency-Key: weekly-pricing-2026-09-01"
```


  Derive the key from the thing you mean to run once — the automation id plus the period — so a repeat of the same intent reuses it and a genuinely new run does not.


## When the credits run out

With a spent credit balance, scheduled automations do not run, and nothing is charged for runs that did not start. The workspace gets one Inbox row and one email until it is topped up; see [Credits](/account/credits#when-the-balance-is-spent). A workspace's **monthly credit cap** pauses scheduled runs the same way; see [Monthly credit cap](/account/credits#monthly-credit-cap). A project's own monthly cap pauses that project's runs; see [Project monthly cap](/account/credits#project-monthly-cap).

## Managing automations over MCP

The [MCP server](/mcp/overview) exposes `list_automations`, `get_automation`, `create_automation`, `update_automation`, `run_automation`, and `delete_automation`. `delete_automation` is marked destructive and removes the automation; its past runs are kept as sessions. `run_automation` is the only one that costs credits.

`list_automations` and `get_automation` always include paused and retired automations. When the product paused one on its own, it carries `retired_reason` and `retired_at`.

No tool takes a workspace id. The API key you authenticate with names the workspace.


# The autopilot loop
Source: https://docs.attensira.com/agent/autopilot

Every morning the Daily win plan does up to three pieces of work toward the prompts you want to win, and event automations react when an answer or a page changes. Nothing leaves the workspace without your yes.

## What runs on its own

Two kinds of [automation](/agent/automations) keep the work moving without you asking:

* **The Daily win plan**, a scheduled automation every workspace has. Once a day it picks the next pieces of work that bring you closer to the prompts you track.
* **Event automations**, which run when something happens to the workspace (you drop out of an answer, a competitor changes its pricing page) instead of on a clock. The built-in one is the **Event responder**.

Both do the work and then wait. Every piece they produce lands in [Work](/agent/inbox) for your approval or review. The loop never posts, sends or merges on its own; what may go out unattended is set by your [approval mode](/agent/approval), and claims never do.

To check that the loop is actually running, open [loop health](/agent/loop-health).

## The Daily win plan

Every workspace has a **Daily win plan** automation that runs at **06:00 in the workspace's time zone**, an hour before the [morning brief](/agent/morning-brief).

Each morning it reads the prompts you are losing and the ones you are winning, and does **at most three** pieces of work. Each one is one of:

| Work | Where it lands |
| - | - |
| A GitHub pull request: a fix to a page, or a new blog post | A pull request on your connected [repository](/agent/github), opened as a draft by default |
| A page rewrite suggestion | A before/after in your [Inbox](/agent/inbox), when no repository holds the page |
| An article draft | The Inbox, and a draft page in [Notion](/agent/notion) after your yes when a Notion blog is connected |
| A directory listing | The Inbox, prefilled from your business profile ([off-site](/agent/off-site)) |
| A community thread reply | The Inbox, as a draft for you to post ([community answers](/agent/community-answers)) |
| An outreach draft | The Inbox, for you to send from your own mailbox |

All of it waits for your approval or review. A morning where you are winning everything you track is a legitimate outcome: the plan says so rather than inventing work.

### What it can reach

The Daily win plan starts with this reach (tool groups): **Your own data**, **The open web**, **Remember what we learn**, **Your site's code on GitHub**, **Make things**, **Rewrite a passage on your site**, **Find who to contact**, **Answer questions where people already argue**, **Get listed on directories**, and **Notion**.

A group whose connection you have not made yet (GitHub or Notion, say) is skipped until you connect it. You do not need to edit the automation afterwards: the next morning's run picks the new connection up.

### It replaced the Daily check-in

Workspaces used to start with a **Daily check-in** automation that read the numbers each morning and reported them. The Daily win plan replaces it. Existing workspaces were given the Daily win plan once, in one of four ways:

* **Created**, if the workspace had neither.
* **Converted** from a Daily check-in you had never edited. Its run history is kept.
* **Resumed**, if [detection](/agent/findings) had paused the old check-in.
* **Added beside** a check-in you had rewritten in your own words, which is left as you wrote it.

That was a one-time step. If you delete or pause the Daily win plan afterwards, it stays deleted or paused; nothing puts it back.

## Event automations

An automation with the **On a change** trigger (`event` on the wire) runs when something happens instead of at a time. It listens for one or more of these events:

| Event | When it fires |
| - | - |
| `prompt_lost` | You drop out of an answer on an engine, or a rival is newly named on a prompt |
| `citation_lost` | An answer stops citing a page of yours |
| `new_deciding_source` | A new source starts deciding an answer (a new domain in an engine's top ten) |
| `competitor_changed` | A tracked competitor changes a key page (see [Change monitoring](#change-monitoring)) |
| `page_changed` | One of your own pages changes |

The dashboard, MCP and REST each ask you to pick at least one of these (see [Creating one](#creating-one)). An automation saved with an empty list listens for every event type, including [`win_detected`](/agent/wins#the-win_detected-event). `prompt_lost`, `citation_lost` and `new_deciding_source` come from the hourly [detection](/agent/findings) pass. `page_changed` comes from detection and from the homepage monitor, and `competitor_changed` from competitor monitoring (see [Change monitoring](#change-monitoring)).

The nightly [claim check](/measure/claims#the-claim_wrong-event) also records a `claim_wrong` event when an answer states something about you that a fact you confirmed contradicts. It is not one of the events you can pick yet, in the dashboard, over MCP or over REST, and the Event responder does not listen for it, so only an automation saved with an empty event list acts on it.

An event is picked up within about **5 minutes** of being recorded.

### What an event run is given

The run gets the automation's saved instruction plus the list of events that woke it. Each event is dated and described as something seen once, not as a trend: "an answer on Perplexity stopped citing /pricing on 2026-09-22", never "citations are falling".

### Limits

* **One run covers up to 10 events.** More than that and the rest wait for the next run.
* **At most 3 runs per automation per rolling 24 hours.** Events past that cap are recorded as skipped, with the reason, and are visible in [loop health](/agent/loop-health).
* **One run at a time.** If a run of the same automation is still in progress, new events wait for it to finish.
* **Events older than 48 hours are not acted on.**
* **A competitor's change can be judged not worth a run.** It is skipped with the reason, and the rest of the batch still fires. See [Worth a run](/agent/decisions#worth-a-run).
* **Out of credits:** the event is skipped and the reason says the balance is spent.

### Creating one

Event automations cannot be proposed from a chat. Create one from the Automations page (the **Event responder** starter, or **New automation** with **On a change**), with `create_automation` over [MCP](/mcp/tools#create_automation), or with `POST /v1/automations` over the [REST API](/api/endpoints#post-v1automations). Over MCP and REST an `event` trigger needs a non-empty `event_types` list.

### The Event responder

The built-in **Event responder** template (id `event-responder`) listens for the five event types in the table above and runs the same procedure as the Daily win plan, scoped to what changed. It takes **at most one action per event**: propose a fix, draft content, or raise a task.

## Change monitoring

Two of the events need something to watch pages on a schedule. Attensira sets that up for you:

* **Competitors.** When any automation listens for `competitor_changed`, Attensira watches up to **5** of your tracked competitors' pricing, feature and comparison or alternatives pages, once a day.
* **Your homepage.** When any automation listens for `page_changed`, Attensira watches the workspace's homepage daily. Changes to navigation, the footer and cosmetic details are ignored.

A detected change becomes a `competitor_changed` or `page_changed` event. Monitors are added and removed automatically as your competitors, automations and workspaces change: removing a competitor, or the last automation that listens for the event, stops the watch, and deleting a workspace stops all of its monitoring.

## Who gets this without asking

Paid workspaces are enrolled in the [morning brief](/agent/morning-brief), hourly [detection](/agent/findings) and the [off-site](/agent/off-site) work streams automatically, the first time their subscription is active.


  New workspaces start in the **draft** [approval mode](/agent/approval): the loop does the work and waits for your yes before anything leaves the workspace.



# Chats
Source: https://docs.attensira.com/agent/chats

Talk to your Chief of staff: ask about your workspace or hand it work, the box with its modes and dictation, the few questions it asks you, and why there is no model to pick.

## What is a chat?

A chat is a conversation with your **Chief of staff**, the agent that runs your workspace. You ask in plain language, "which prompts lost citations this month?" or "write a comparison page for our pricing", and it does one of three things: answers from your tracked data with the numbers behind it, hands the work to the [project](/agent/delegation) whose crew does that kind of work, or reports what was done. Each chat is a session you can reopen and continue.

The Chief of staff commits to the work, never to an outcome. What an answer engine does with a page is not ours to promise, so it tells you what was done and what it changed, not what it will achieve. Attensira guarantees the volume of work, not visibility.

Chats are scoped to one Workspace. The agent answers from that workspace's Prompts, Topics, Models, Answers and citations, and cannot read another workspace's data.

You start a chat from the box on [Home](/start/today#home), or with Cmd-N (Ctrl-N) from anywhere. Your recent chats are listed in the sidebar, pinned ones first, and **All chats** opens the full list.

## The box

The same box starts a chat on Home and replies inside one. **Enter** sends; **Shift-Enter** starts a new line. A message of several lines is sent as one message.

### Modes

Type `/` at the start of a new chat to pick a mode. The menu lists the modes first. A mode holds the whole chat to a set of tools, so what you asked for is all it can do:

| Mode | What the chat may do |
| - | - |
| **Plan** | Read your data, read the web, read a connected repository, draw charts and ask you questions. Nothing that changes the workspace, files work or leaves a mark outside it. Use it for "tell me what you would do": nothing changes until you agree. |
| **Ask** | Everything the workspace can reach. This is an ordinary chat, and what you get with no mode. |
| **Report** | Everything Plan may do, plus publishing a [report](/agent/reports). Nothing else that acts. |

Typing the mode works too: `/plan rewrite the pricing page` starts a Plan chat about rewriting the pricing page. The mode shows as a chip in the box; Backspace in an empty box removes it. Modes are chosen when a chat starts, so a reply inside a chat does not offer them.

### Adding things

The **+** button offers three things:

* **Add a link** puts the address into your message, adding `https://` if you left it off. The agent reads it as part of what you wrote.
* **Add file** and **Add from a connector** (also `@` in the box) add a chip for a file, a Topic, or a connected integration.

The `/` menu also lists your [skills](/agent/knowledge-and-skills) under the modes.


  Chips for skills, files and connector items show in the box, but they are not yet sent to the agent with your message. Until they are, say it in words: name the skill, paste the text, or say which integration to use.


### Dictation

The microphone in the box dictates: press it, speak, and the words are added to the box as you go; press it again to stop. Nothing is sent until you send it.

Dictation uses your browser's own speech recognition, so your browser's speech service does the transcription, not Attensira. Where the browser has none, the microphone is shown switched off with "Dictation isn't available in this browser". If the microphone is blocked for the site, missing, or the speech service cannot be reached, the box says which.

### Output

On Home, an **Output** pill under the box says what you would like back: **Let us pick**, **Article**, **Page change**, **Report**, **Social post** or **Image**. A chat started from Home does not use it yet, so say the format in your message as well: "as an article", "as a LinkedIn post". When the Chief of staff [hands that work to a project](/agent/delegation), it passes the form on, and the crew delivers it that way.

### When a chat cannot start

What you typed always stays in the box:

* If the credit balance is spent, the box says **You're out of budget · Top up**, which opens Billing. See [Credits](/account/credits#what-a-workspace-sees-at-zero).
* If the chat could not be started for any other reason, you see **Couldn't start. Try again.**

Other screens sometimes offer a question for you to ask. It is put in the box and waits there: nothing is sent until you send it.

## There is no model to pick

Attensira picks the LLM that runs each turn of a chat. There is no model or effort picker in the app: choosing the right model for a question is our job, not a decision you should have to make before asking it. A short lookup runs on a quick model and a hard analysis on a capable one, and the credits a turn spends follow what it actually used.

### An agent model is not a Model

Two different things get called "model", so Attensira keeps them apart:

* A **Model** is a surface you track: ChatGPT, Perplexity, Claude, Gemini, Google AI. It is the thing being measured. Each has a stable channel id that survives the underlying model being swapped out.
* An **agent model** is the LLM that answers you in a chat. It changes how the agent reasons and writes, and nothing about your data.

Every agent model reads the same stored runs, so a different one would not give you a second opinion on your numbers. If a number looks wrong, check `n` and the tracked flags.

## What can the agent reach?

The agent reads the same measurements the dashboard shows: your Prompts and the Topics they sit in, the Models tracked for the workspace, the Answers those models returned, the Mentions and Citations inside them, and the Sources those citations roll up into. It can also act: adding prompts, adding a competitor, creating or running Automations, drafting work for your approval, and handing work to a project.

It cannot reach anything you have not tracked. If a Model is not configured for the workspace it was never queried, so there is no data to summarise; the agent should say that rather than report zero. If a Prompt does not exist, no answer about it exists either.


  The agent inherits the measurement rules, not only the data. Null means not measured, a measured zero means runs happened and found nothing, and a delta below the 95% significance threshold means no change we can prove. Ask the agent for `n` when a number surprises you.


## When the agent asks you something

Some work cannot be done well without something only you know: who a piece is for, which page, which of two directions. The agent then stops and asks, before it starts rather than halfway through.

* **At most three questions**, each with up to **four one-click options**. You can always type your own answer instead.
* It asks everything it needs at once, never one question at a time, and does not ask what a tool can find out or where a sensible default exists.
* The chat waits for your answer, for up to **7 days**, then carries on from where it stopped with your answers in hand.

Asking for permission is separate. When the agent wants to do something your [approval mode](/agent/approval) says needs a yes, you get an approval card, not a question.

Automations never stop to ask questions: nobody is in front of a scheduled run to answer.

## Handing work to the agents

Ask for work rather than an answer, "write an article on X, fix the meta descriptions on the pricing pages, pitch two directories", and the Chief of staff hands each piece to the project whose crew does it, one task per line, instead of doing it inside the chat. A card shows where each line went. See [Delegation](/agent/delegation).

When a chat turns out to be about ongoing work, something needed every week or a channel to build, the Chief of staff may offer a **Make this a project** card: a name, what it is for, the crew it needs, and what wakes it up. Nothing is created until you press **Create project**; **Not now** dismisses it.

## Asking a good question


  
    "Last 30 days" or "since the start of the quarter". Without a window the agent picks one, and short windows at low sampling depth are mostly noise.
  

  
    A Topic, a Prompt, a Model, or a competitor. Broad questions return broad answers.
  

  
    Rates share a denominator across brands, so competitor rates do not sum to 1. Ask for the run counts if you plan to quote the figure.
  

  
    The session keeps context, so you can narrow without restating everything.
  


## Costs and limits

Asking the agent spends Credits, because each question runs a model and may run tools on your behalf. A long analytical question costs more than a short lookup. A chat that triggers an Automation run spends credits for that run as well, and work handed to a project is metered in that project's own session.

Some questions take long enough that the agent returns a running status and finishes in the background; reopen the session to read the completed answer. The same behaviour is exposed over MCP through `ask` and `get_session`.

When a chat stops to ask you something, it counts in the Inbox badge in the top bar and is listed under **Needs you** on Home until you answer. See [Inbox](/agent/inbox#needs-you-on-home).


  
    How handed-off work is routed to projects, and when you are asked.
  

  
    What the agent draws, and what a run made.
  



# Webflow and WordPress
Source: https://docs.attensira.com/agent/cms

How Attensira changes a page, or adds a new article, when your site is not in a repository: a draft in your own editor, never a silent publish.

If your site's pages live in Webflow or WordPress rather than in a repository, Attensira can make the fix itself instead of describing it. What it writes is a **draft**, an unpublished Webflow item or a WordPress draft revision, sitting in your own editor, where you approve it or throw it away.

## What we change

On a page that already exists, one field at a time: a meta description, a title, an Open Graph tag, the body of a post.

New articles are created as drafts, in Webflow (see [New articles](#new-articles)). We never publish a new article, and we never delete a page: a URL somebody links to is not something an automation gets to remove.

## Drafts, and when something goes live

* **Ask or draft mode.** Every change we propose is written as a CMS draft and waits in your Inbox. Nothing a reader sees changes until you say yes.
* **You approve it.** We publish it. In Webflow that means publishing the site; in WordPress, setting the post to published.
* **Act mode.** We publish without asking only when all three hold: the change is hygiene (structured data, a meta tag, alt text, an internal link, not a sentence carrying a price, a number with a unit, a promise or a competitor's name), the page is inside the paths you allowed, and the workspace is still in act mode at the moment we write. Anything else becomes a draft, and the Inbox says which rule stopped it.

We use the same classifier here as for a repository change (see [Approval modes](/agent/approval)), so a price appearing in a Webflow meta description stops for you just as it would in a file.

## New articles

When the agent writes an article for your site, it is filed for your approval with where approving will send it. Articles always wait for a person's yes, in every mode. Where it goes is worked out from what the workspace has connected, in this order:

1. A connected repository in which a blog directory can be found: a pull request adding the file.
2. Your site's CMS: Webflow, then [Shopify](/agent/shopify) (an unpublished blog article). WordPress cannot take new articles yet; see below.
3. [Notion](/agent/notion): a draft page in your blog database.
4. Otherwise a copy: you paste it into your site and mark it shipped with its live URL.

A CMS counts only when it can say which collection the article would land in, so an article is never filed for an approval that would fail afterwards: it becomes a copy, with the reason given. If you ask for a CMS that is not connected, the article is still written and becomes a copy, and the reply says why.

**In Webflow**, the article is filed in your Inbox with **Publish to Webflow as draft**, and approving it creates it as a **draft item** in your blog collection: the title, a slug made from the title, and the body as rich text. Nothing is published, in any mode, and we never publish the site for a new article. The item waits in your Webflow CMS until you publish it there, and it is recorded as **Draft in Webflow**. Once it is live, mark the item shipped with its URL; that starts the [before-and-after re-reads](/measure/before-and-after).

* **Which collection.** We look for the collection named like a blog that has a rich text field for the body, or, when none is named like one, the only collection with a rich text field. If we cannot tell which of your Webflow sites is this workspace's, if several collections could be your blog, if none has a rich text field, if the body field cannot be told apart from the others, or if the collection requires a field we would have to invent a value for, the article is not sent to Webflow. It goes to [Notion](/agent/notion) when Notion is connected, and otherwise is filed as a copy for you to paste, with the reason. If an article was filed for Webflow and one of these becomes true before you approve it, approving brings it back to you with the reason and writes nothing.
* **One article per slug.** Approving twice, or a retry after a timeout, finds the item already there and creates no second one. If your collection already has a different item at the same slug, or the same title with different words, nothing is written over it, and the item comes back to you saying what to do instead: paste the new version into that item, or rename one of them and approve again.
* **No HTML from the article reaches your CMS.** The body is converted from Markdown with raw HTML switched off, so a tag or a script quoted from a web page is dropped, not stored.

**In WordPress**, new articles cannot be created yet. The connection we use can edit posts that exist but cannot create one, so an article for a WordPress site goes to [Notion](/agent/notion) when Notion is connected, and otherwise is a copy: paste it into WordPress and mark it shipped with its URL.

## If the page changed since you approved

We re-read the field immediately before writing. If it no longer holds what the proposal was made against, because somebody on your team edited it in the meantime, we do not write. The item comes back to your Inbox with a fresh diff.

## What appears in your receipt

Every write is recorded as a page change with a link to the page or the draft. A draft is recorded as a draft; we never write "published" about something nobody can see yet.

## Connecting

1. Open **Settings → Integrations** in your workspace.
2. Find **Webflow** or **WordPress** and press **Connect**.
3. Sign in at Webflow or WordPress and authorise the connection. The sign-in happens on their side; we never see your password.
4. Choose the site or account when asked. You are returned to your workspace with the row showing **Connected**.
5. Check the path list under **Settings → Autopilot**. It decides which pages act mode may publish to on its own, and it starts at your docs and content directories.

You can disconnect at any time from the same screen. If you connect both Webflow and WordPress, we work out which one a page belongs to from the page's own URL.

**Permissions we ask for:** read your collections, posts and pages; update one item; create an unpublished item in your blog collection (Webflow); publish the site (Webflow) or set a post's status (WordPress). We never ask for delete.


# Community answers and Reddit
Source: https://docs.attensira.com/agent/community-answers

How the agent finds the Reddit, Hacker News, Stack Exchange and Quora threads that AI answers cite, and drafts a reply you post yourself. Nothing is ever posted for you.

AI answers often cite a discussion thread: a Reddit post comparing tools, a Hacker News comment, a Stack Overflow answer. When one of those threads decides an answer to a prompt you track, a good reply from you on it is one of the few changes that can reach the answer directly.

## On by default, and never posted

Community answers are **on by default** for every workspace, including existing ones that never touched the setting (`community_answers_enabled`). If a person switches the setting off, it stays off.

**Drafts are never posted.** The agent writes the reply and leaves it in [Work](/agent/inbox) for you to read, edit and post from your own account. There is no setting that makes Attensira post to a community on its own, in any [approval mode](/agent/approval).

## How threads are found

Threads are picked from the [deciding sources](/measure/deciding-sources): the pages AI answers cite for your tracked prompts. Threads on **Reddit**, **Hacker News**, **Stack Overflow and the Stack Exchange sites**, and **Quora** qualify.

Reddit threads are read together with their **subreddit's rules** and whether the thread **still takes replies**. No draft is made for a thread that is locked, archived or removed.

The agent's `find_community_threads` tool does this lookup. It is free and read-only, and `web_fetch` reads Reddit threads free of charge. See [The agent's tools](/agent/agent-tools).

## The rules every draft follows

The tool that files a draft refuses one that breaks these, whatever the agent intended:

* **Disclosure.** A reply that names your product must include a plain disclosure line, such as "Disclosure: I work on Acme." An undisclosed mention is refused.
* **One link, last.** At most one link to you, alone in the last paragraph, after the reply has answered the question. A link before the answer, or a second link, is refused.
* **Self-promotion rules.** When the subreddit has a self-promotion rule, a note on the draft says so.
* **Account floor.** On Reddit, the reply is meant for a real person's account at least **90 days** old with at least **100 karma**. Below that floor nothing is proposed.

The agent also works to these:

* **Evidence first.** A draft is made only for a thread an answer actually cites, and it carries that answer's own words, the engine and the date it was read.
* **Rules beat the draft.** A subreddit rule that bans promotion or links removes the mention, not the rule. If the rules could not be read, the draft says so and you read them before posting.
* **One reply per thread.** Never a second reply, a reply for a colleague to agree with, or a planted question.
* **Nothing to add is an answer.** If an existing reply already says it, no draft is made.


# Decisions
Source: https://docs.attensira.com/agent/decisions

The small, typed questions the agents answer before they act. A decision can stop, escalate or pick among allowed options. It never approves, never spends, and never changes what needs your approval.

## What a decision is

Along the way, the agents answer many small questions that are not worth your time. Is this draft breaking one of your rules? Is this dip noise? Does this need a person sooner? Each one is a **decision**: one typed question about one thing, with a fixed set of things its answer is allowed to do.

A decision can do one of four things, and nothing else:

| Kind | What the answer may do |
| - | - |
| **Stop** | Hold back something that would otherwise happen. |
| **Escalate** | Bring something to a person, or to a re-plan, sooner. It never acts on your behalf. |
| **Select** | Pick one option from a list the agents' own rules already allow. An answer outside the list counts as no answer. |
| **Shadow** | Be recorded, and nothing more. |

None of the four widens what the agents may do. A decision never approves anything, never spends, and never changes what needs your approval. Your [approval mode](/agent/approval) and the change classes decide that, and no decision can override them.

Every decision is recorded: the question, the answer, and what the answer was allowed to do. The same question about the same thing, unchanged, can be answered from that record instead of being asked again.

## When a decision cannot be made

When a question gets no answer, the agents do exactly what they did before decisions existed. A missing answer never counts as yes, and never as no. It means the step the decision would have changed runs the old way.

No answer covers every way a question can fail: it timed out, the service that answers it was unavailable, the answer was missing, or it named an option that was not on the list. In each case the step runs as if nobody had asked. When answers keep failing, the agents stop asking for a short while and run the old way straight away, so an outage never slows the loop down.

[Noise or a real change](#noise-or-a-real-change) and [Worth a run](#worth-a-run) are both of this kind. With no answer, a finding is filed and an event wakes its automation, exactly as before.

The [draft check](#the-draft-check) is the one place this works differently, because the old way there was no check at all.

## Noise or a real change

Every hour, [detection](/agent/findings) checks your readings against five fixed rules. A rule that fires says what moved. It cannot say whether it moved by more than sampling moves on its own. A prompt read three times a day can lose a citation for three days on luck alone. So before a finding is filed, it is measured, and some findings are put to a decision.

### First, the arithmetic

The finding's last three days are compared with the seven days before them, counting successful reads only. A failed read is missing data, never a miss. The comparison is Fisher's exact test. If the change is significant at p \< 0.01, the finding is filed, and nothing is asked.

### Then, one Stop decision

A finding that is not significant on the arithmetic, and has no row open in your [Inbox](/agent/inbox) yet, is put to one question: **is this a real change in AI answers, rather than sampling noise or a gap in the readings?**

The question reads numbers only:

* for each of the ten days, how many answers were read successfully (n), how many reads failed, how many answers named you and how many did not, and how many counted toward the finding;
* the totals and rates for the three recent days and for the prior seven;
* the p-value;
* how many prompts and engines the finding reaches.

It never sees a domain, a URL, a page title or a prompt's text. Nothing written on a page can argue a loss away.

A finding is **held as noise** only when the decision is confident it is not real: less than a 20% chance that it is. Everything else is filed.

### What held as noise means

* **It is not filed.** No Inbox row is raised and no open row is bumped.
* **It is not an event.** No [event automation](/agent/autopilot#event-automations) hears about it, and it starts no run.
* **It is held for this hour only.** The next hourly pass measures the finding again. Once new readings have landed, the question is asked again on them, and a change that holds up is filed then. Until they land the numbers are the same, so for up to six hours the earlier answer stands rather than being asked again.
* **It is recorded.** Each hold keeps the number of successful reads it was judged on (n, across both windows) and the test's p-value. A finding that was held and later filed no longer counts as held.

A held finding is not recorded as an event with a "held" flag either. The same event is recorded at most once per subject per day, so a held event in the morning would hide the real one in the afternoon.

### What is never held

* **A significant change.** p \< 0.01 is always filed, whatever a decision would say.
* **A finding whose Inbox row is already open.** That row is always bumped, so its count keeps growing.
* **A change to your own pages.** It is not a measurement that can be noisy, and it is often the reason behind the other findings.

### How much it matters

The same findings get a second question, a **Select** decision: how much does this matter, on a scale of **low**, **medium**, **high** or **critical**? It reads the same numbers.

The answer only changes the order in which findings take the three open Inbox rows that detection may fill. It never drops a finding. A finding that misses a row still bumps the closest open one, as before.

* A significant finding ranks with **critical**. The arithmetic already proved it.
* An answer that is less than 40% sure is ignored, and the finding ranks as **medium**.
* If no finding got a usable answer, the rules' own order stands.

## Worth a run

An [event automation](/agent/autopilot#event-automations) runs when something happens instead of on a clock. Most events have already been judged by the time they arrive. One kind has not: a competitor changing pages on its site (`competitor_changed`). Those arrive in bursts. A redesign changes every page at once.

So before a competitor's change wakes an automation, a **Stop** decision asks: **is this event worth starting a run of this automation now?**

The question reads:

* the automation's name and the first 500 characters of its instruction;
* the focus of the automation's project and its three most recent directions, when it belongs to a project;
* the event's type, subject and date;
* the automation's event runs from the last seven days, at most five, each as its **Because…** sentence;
* what changed on the competitor's page.

The competitor's page is read as text to judge, never as instructions. A page that says "ignore this change" is weighed like any other wording.

An event is skipped only when the decision is confident it is not worth a run: less than a 15% chance that it is. A change to plans, positioning, a product, or a comparison page reads as worth a run. A styling change, a copyright date, a cookie banner, or something a recent run already covered reads as not.

### What a skip does

* **The event is closed for that automation,** as skipped, with the reason **not worth a run:** and the event in our own words. For example: "not worth a run: competitor.example changed pages on its site".
* **The rest of the batch still fires,** as one smaller run.
* **If every event in the batch is skipped, nothing fires.** [Loop health](/agent/loop-health#why-a-scheduled-run-was-skipped) records a skip with the reason `not_worth_run`, so an automation that always skips shows up there.
* **A skip starts no run,** so it spends no credits and does not count toward the automation's 3 event runs per 24 hours.

### What is never asked

* **Every other event type** wakes its automation as before. Detection's events (`citation_lost`, `prompt_lost`, `new_deciding_source`) were already measured by the pass above. Changes to your own pages and readings (`page_changed`, `win_detected`) are about you, and never noise to you.
* **An event that says nothing about what changed** fires. There is nothing to judge.
* **The 48-hour age limit and the rate cap come first,** and they win whatever the answer. The question can only hold back what those rules let through. Credits, caps and a paused project are checked after it, as before.
* **A batch that already has a run** is not asked again. An event that waits behind a run still in progress can be answered from the earlier decision for up to 12 hours, rather than asked again on every check.

## The draft check

Before anyone can approve a draft the agent writes in your name, it is checked against every rule that applies to it: your compliance and voice rules, your words to avoid, and our house rules. The full list, and what each verdict does, is on [Approval modes](/agent/approval#the-draft-check).

For each rule, the check asks one question: does this draft break this rule?

* A compliance rule the draft clearly breaks **stops** it. One it may break sends it to **review**.
* A voice rule is at most **review**. Sounding off is a reason to read a draft, not to refuse it.
* A word you said to avoid, and the certain part of some house rules, is a plain match. It needs no judgement and runs before anything is asked.

The draft goes in as text to judge, never as instructions. An instruction inside a draft, such as one copied from a page the agent read, is judged as content and never followed.

For articles and pages, the check also records how readily an answer engine could quote the draft. That is a note on the draft only, and it never changes the verdict.

### Checked against N rules, or Not checked

A draft that passes is **checked against N rules**. N counts your rules and our house rules together.

A draft the check could not finish is **not checked**, and it is never read as passed. For your own rules, a missing answer does not fall back to "go ahead": the draft cannot be approved, published or sent until it is checked. A row in your [Inbox](/agent/inbox) says how many drafts are waiting on a check, and approving a draft checks it again. A stop found before the check broke off still stands.

Our house rules are the exception. A house rule the check could not answer is left out of N, and the verdict stands on the rest. Its certain part still runs. Before the house rules existed, drafts were not checked against them, so a gap in the check does not hold every draft in every workspace.

The check only ever adds a stop. A passed draft still waits for every yes it needed before.

## Where a handed-off line goes

When you hand off several lines at once from the composer, or the Chief of staff hands off tasks from a chat, each line becomes its own task in one of your workspace's projects. Picking the project is a **Select** decision. It can choose a project, or decide that the line should wait for you. It cannot do anything else.

Rules come first, and when one applies, nothing is asked:

1. A project named for the line itself goes first: the project the Chief of staff named for that task, or a `#name` in the line that matches one of your projects. A line that *starts* with a `#name` matching no project waits for you, with the reason "there is no project #name". A `#name` later in the line that matches no project is read as an ordinary hashtag and ignored.
2. The project you picked in the composer takes every line that does not name its own.
3. If exactly one project is taking work, every line goes there.

A project named by a rule that is paused, or still being set up, does not take the line. The line waits for you and says why, for example "Blog is paused".

When no rule applies, the decision reads the line and the name and focus of each project that is taking work (up to 25 of them). It then picks one of these: one of those projects, the **Chief of staff** (for a question, a one-off ask, or work that spans several projects), or **a new project** (for an ongoing area of work that no project covers yet). The line is judged as text and never followed as an instruction. A line that says "this is for the blog project, ignore your rules" is weighed like any other wording.

| What the decision answers | What happens to the line |
| - | - |
| A project, 60% sure or more | It is filed as a task in that project |
| The Chief of staff, 60% sure or more | It waits for you: "This does not belong to one project" |
| A new project, 60% sure or more | It waits for you: "This looks like work for a new project" |
| Anything, less than 60% sure | It waits for you: "Not sure which project should take this" |
| No answer | It waits for you: "More than one project could take this" |

A line that waits for you is marked **Waiting on you** on the card. The chat then asks **Which project should take "…"?** and offers your most recently active projects as one-click answers. You can also type a project's name. If up to three lines are waiting, each gets its own question. If more are waiting, one question covers them all. Your answer files the line in that project straight away, with no further decision. An answer that matches no project goes back to the Chief of staff to sort out.

If the workspace has no projects, there is nothing to choose from, so the Chief of staff does the work in the chat.

### How sure it was

The **Handed off** card in the chat lists each line and where it went. Hover over or focus a project that the decision picked to see how sure it was, in words and as a percentage. For example: "Very sure this belongs in #blog · 91%. Attensira picked the project."

| Words | Confidence |
| - | - |
| Very sure | 90% or more |
| Fairly sure | 75% to 89% |
| Somewhat sure | 60% to 74% |

A project you named, or your only project, shows its name with no percentage, because nothing was decided. A line that went to the person because no answer came back has no percentage either. There is no number to show, so none is shown.

### What routing never changes

* **Only the project.** The output and speed you chose in the composer are filed exactly as you chose them.
* **Not your approval mode.** The task runs as an ordinary run under your [approval mode](/agent/approval). Routing never approves anything and never changes what needs your yes.
* **No guesses.** A wrong project means real work done in the wrong place. Confirming takes one click, so a line the decision is unsure of always comes to you.

## Goal re-plans

A goal is a number from a connected source, a target and a date. Each goal is read every day and compared with its pace line, which runs from its first reading to its target. Whether a goal is ahead, on pace or behind is plain arithmetic, never a decision. If decisions are unavailable, a goal's standing does not change.

When the arithmetic says a goal is **behind**, an **Escalate** decision asks one more question: is this gap worth re-planning the work now, or should the current plan keep running?

### When it is checked

The check runs every morning at 05:00 on the workspace's clock. That is after the 04:00 readings land and before the 06:00 [Daily win plan](/agent/automations). It looks only at open goals that are behind as of their latest reading (that morning's or the day before's) and whose date has not passed.

### What decides

* **The floor.** If a goal is more than 30% of its climb behind with fewer than 14 days left, it is always re-planned. Nothing is asked.
* **Otherwise, the decision.** It reads numbers only:

  * the last 28 days of readings, with the sample size (n) behind each;
  * the pace line and its noise band;
  * the gap, and the days elapsed and left;
  * shipped work that is still waiting for its [re-read](/measure/before-and-after), with the re-read dates;
  * how long ago the goal was last re-planned.

  It never sees the goal's name or anything a person or a page wrote, so no wording can steer it. Only a confident yes leads to a re-plan. These read as no: a dip of a day or two, readings with a small n, a trend already climbing back, and shipped work whose re-read could close the gap.
* **No answer means no re-plan,** the same as before decisions existed. The floor still applies.

### At most once a week

A goal re-planned in the last seven days is left alone. Each goal gets at most one re-plan per calendar week, counted on the workspace's clock. A re-plan run you cancel stays cancelled for the rest of that week. A re-plan row you dismiss is not filed again that week.

### What a re-plan does

What happens depends on your workspace's [approval mode](/agent/approval):

* **Draft or Act.** One run of the Daily win plan starts, focused on this goal. It plans at most three pieces of work that move the goal's number and files them as work, which your approval mode governs like any other. If the target looks out of reach, it says so in one line and asks you. The run shows why it started: **Because "your goal" is behind pace.** It is an ordinary run and spends credits like one. If the balance is spent or a cap is reached, nothing starts.
* **Ask.** Nothing starts. One row is filed in [Work](/agent/inbox): **"your goal" is behind pace: re-plan the work toward it?**

For seven days after a re-plan run starts, the goal's chip reads **Behind, re-planning**.

A re-plan never changes the goal, its target or its date, the budget, or any setting. Changing those is always yours.


# Deep reads
Source: https://docs.attensira.com/agent/deep-read

Read-only tools that return the stored answers, citation days and page versions your metrics were counted from.

Every number Attensira shows is a fold over stored readings. The deep reads hand back the rows themselves, so a number can be checked rather than trusted. They are free, read-only, and scoped to the workspace the credential names. They are available as agent tools, as [MCP tools](/mcp/tools), and as REST endpoints under `/v1/memory/`.

## answer\_history

Every sampled answer to one tracked prompt, verbatim, newest first, each with its platform, surface (consumer or api), slot day, whether it named you, its citations in order, and the reading id. `prompt` is required; `platform` and `days` (1 to 365, default 28) narrow it.

* An answer longer than **8 KB** is cut on a character boundary and carries `truncated: true`, with `text_bytes` giving the full stored length.
* A page holds at most **50 readings** (default 10), each with all of its samples. Pass `next_cursor` back as `cursor` for the next page; `has_more` says whether there is one.
* A failed sample is returned with `failed: true` and the customer-facing reason; it has no text and never counts toward n.

## answer\_slice

Every answer matching a predicate: `platforms`, `country`, `surface`, `competitor_named` (a tracked rival's domain, named or linked), `cited_domain` (among the citations), `brand_named` (true or false), and `from`/`to` days or `days`. Terms AND together and select the **sample**, not the reading: "consumer answers that named Jasper and cited g2.com" returns exactly those samples. At least one term is required; for one prompt's answers use `answer_history`. Same 8 KB cut and 50-reading page as `answer_history`. A day range is widened by one UTC day on each side because slot days straddle UTC dates; the exact `day` label is on every row.

## citation\_timeline

One domain's, or one page's, citations per slot day, split by platform, from the same rollup as the sources board, so the two never disagree. Every day of the window is present: a day with zero citations is a real zero. `mean_position` is null when nothing that day carried a position (never 0). `prompts` is how many distinct prompts' answers cited it that day. `days` 1 to 365, default 28.

## page\_history

One of your own pages' snapshot lineage, oldest first: when each version was captured, its title, description, word count, status, HTTP status, JSON-LD presence and types, canonical and robots meta, and `changes`, the fields that moved since the version before. `changed_bits` is how far the body drifted.

* `json_ld` is `present`, `absent`, or `not_captured`. **Not captured is not absent**: versions taken before we recorded metadata cannot say the page lacked JSON-LD, only that we did not look. Metadata is only diffed between two versions that both captured it, so starting to capture never reads as the page changing.
* `known` says whether the page is in your inventory; a known page with no versions has never been read, which is different from "unchanged".
* The URL must be on the workspace's own domain; other sites are refused.

## Over REST

Each has a REST twin with the same payload: `GET /v1/memory/answers/history`, `GET /v1/memory/answers/slice`, `GET /v1/memory/citations/timeline` and `GET /v1/memory/pages/history`. They take the same `atn_live_` key, and `?project=` when the key covers several workspaces. See [The receipts](/api/endpoints#the-receipts) for the query parameters.


# Delegation
Source: https://docs.attensira.com/agent/delegation

How the Chief of staff hands work to your projects: one task per line, how each line finds its project, when you are asked to choose, and what approval mode changes.

You set the direction; the agents do the work. When you ask for work rather than an answer, the Chief of staff does not do it inside the chat. It hands each piece to the **project** whose crew does that kind of work, and tells you where each one went.

A project is a standing area of work with its own crew: the blog, a set of product pages, outreach. Each handed-off piece becomes a **task** in its project: a session of its own, started in your name and queued for the project's crew.

Attensira guarantees the volume of work, not visibility. A task handed off is work we owe you; what an answer engine then does with the result is not ours to promise, and every change is measured [before and after](/measure/before-and-after) instead.

## Handing work off

Write the work as lines, one piece of work per line, in your own words:

```text theme={null}
Write an article comparing our plans with the two biggest alternatives
Fix the meta descriptions on the pricing pages
#outreach pitch us to the three directories that decide "best crm for startups"
```

* **In a chat**, the Chief of staff splits what you asked for into tasks and hands them off. Several lines typed into the message box on Home go to it as one message, and it does the splitting.
* **Over MCP**, the same message sent to [`ask`](/mcp/recipes#hand-several-pieces-of-work-to-the-agents) reaches the same Chief of staff, which hands the lines off the same way.

One hand-off takes up to **20 lines**, each up to **2,000 characters**. A line longer than that is a brief, and belongs in a chat.

Handed off is not done. The Chief of staff says what went where and what happens next, and never describes a task as finished that a crew has only been given.

## How each line finds its project

The rules run in this order, and the first that applies decides:

1. **The line names a project** with its `#slug`. Every `#token` in the line is checked against your projects, so "post it with #seo" is a hashtag, not a destination, unless you have a project called `seo`. A line that *starts* with a `#token` naming no project is treated as a typo and put to you.
2. **The hand-off names a project** for all its lines.
3. **The workspace has exactly one project taking work.** It goes there.
4. **Otherwise Attensira decides**, reading the line against each project's name and focus. When it is at least 60% sure, the line goes to that project. Below that, or when the line belongs to no project, or when the decision does not come back in time, the line is put to you. A wrong project files real work in the wrong place; a one-click choice costs you a second.

A paused project, or one whose crew is still being set up, never takes a line. A line that names one is put to you with the reason.

Routing only decides where a line goes. It never approves anything, never changes an approval mode and never decides that work is allowed.

## When you are asked to choose

Lines waiting on a project come back as a question in the same chat:

> **Project** · Which project should take "Fix the meta descriptions on the pricing pages"?

* The options are your most recently active projects, up to four, each with its `#slug` and focus. Type a project's name to pick one that is not offered.
* Up to three waiting lines get a question each. More than three share one question, and your answer applies to all of them.
* Picking a project files the line straight away. If the project you picked has stopped taking work since it was offered, the Chief of staff sorts it out with you.
* The question waits for up to **7 days**. Unanswered, the lines are not handed off, and the card says nobody picked a project in time.

With **no projects** in the workspace there is nothing to choose from. The Chief of staff does the work in the chat instead, or offers to [make it a project](/agent/chats#handing-work-to-the-agents).

## The card

Every hand-off draws a **Handed off** card with one row per line:

| Row says | Meaning |
| - | - |
| **Queued** | Filed in its project for the crew. The line links to the task's own session, which is where to follow the work. |
| **Waiting on you** | No project yet. **Choose a project** jumps to the question. |
| **Not handed off** | Filing it failed, with the reason: the balance is spent, the project is no longer there, or "try again". **Retry** sends that line again. |

The card is a record of the hand-off, not a progress bar: a filed row keeps saying **Queued** while its crew works. Open the task to see how far it has got.

A placed row names its project. Where Attensira picked the project, hovering or focusing the name shows how sure it was, in words and as a percentage, and says **Attensira picked the project**: *Very sure* from 90%, *Fairly sure* from 75%, *Somewhat sure* from 60%. A project you named, or your only project, shows its name alone.

The same line handed to the same project from the same chat is the same task. A retry, a double click or the agent repeating itself files it once.

## Approval

Handing work off is an action, so it goes through your [approval mode](/agent/approval) like any other.

| Mode | When the Chief of staff hands work off |
| - | - |
| **Ask** | An approval card first. Nothing is filed until you approve the hand-off. |
| **Draft** | Filed without asking. A task stays inside the workspace and is undone by cancelling its session. |
| **Act** | As Draft. |

That is only the hand-off. The work each task then does is gated again in its own run, under the **stricter** of the workspace's approval mode and the project's own. A project can hold its crew tighter than the workspace, never looser. An article a crew writes is filed for your approval like any other article; see [what Attensira changes on its own](/agent/approval#what-attensira-changes-on-its-own-and-what-it-always-asks-about).

## Credits

Deciding where lines go is charged as credits to the chat that handed them off, once per hand-off. Each task's work is metered in its own session, like any other run. A hand-off from a workspace whose balance is spent files nothing, and each row says why.


  
    Modes, questions, and what the Chief of staff can reach.
  

  
    What runs on its own, and what always waits for you.
  



# Deliverables
Source: https://docs.attensira.com/agent/deliverables

Every article, page change, pull request, post, listing, product change and image the agents delivered, with where it lives now and what the re-reads proved.

**Deliverables**, in the sidebar, is the list of everything the agents delivered across your organisation's workspaces. Each row answers three questions: what it is, where it lives, and what it did.

Nothing on the list is a separate record. Each row is put together, every time you open the page, from the same records the [Inbox](/agent/inbox), the [receipt](/account/receipt) and the [re-reads](/measure/before-and-after) use, so the three can never disagree about one piece of work.

## What is on the list

A piece of work is listed once something was actually delivered:

* **It went somewhere.** A pull request opened, a draft was created in your editor, or a field was written. If the latest attempt failed after an earlier one worked, it is not listed until a later attempt works.
* **It is ready for you to put live.** New wording for a page, or a post for a network we never post to: written, and waiting for you to paste or post it, or already marked live by you.
* **It is a listing to file.** A directory listing the directory would not take from us, prefilled for you to submit, or already filed by you.
* **It is a picture.** An image a chat or automation drew.
* **Its pull request was closed without merging.** It was delivered and turned down in your own repository, so it stays listed as closed.

A proposal you declined in the Inbox was never delivered, and is not listed.

## Types

| Type | What it is |
| - | - |
| **Article** | A new piece the agent wrote |
| **Page change** | A change to a live page: new wording for you to paste, or a field written in Webflow, WordPress or Notion |
| **Fix** | A change to a file in your repository, delivered as a [pull request](/agent/github) |
| **Post** | A post for a social network, which you post from your own account |
| **Listing** | A directory listing, filed or prefilled |
| **Product copy** | A product or collection description or SEO text in [Shopify](/agent/shopify) |
| **Image** | A picture a session drew |

## Reading the table

The columns are **Type**, **Deliverable**, **Where it lives** and **What it did**. With more than one workspace, a **Workspace** column names the workspace each row belongs to, until you filter to one.

* **Filter by type** with the chips above the table. Each chip carries its count; a type with nothing in it is not offered.
* **Filter by workspace** with the selector beside them, when the organisation has more than one.
* The filters live in the page's address, as `?project=&type=`, so a filtered list is a link you can send.
* Rows are newest first, by when they last changed. **Load more** fetches the next page.

### Where it lives

The place, such as **Pull request #12 on GitHub**, **Draft in Webflow**, **Synced to Shopify (draft)**, **Lands in Lovable after merge**, **Ready for you to post on LinkedIn** or **Directory listing for you to submit**. Under it, the delivery status, as a sentence:

| Status | What the row says | What it means |
| - | - | - |
| `waiting_on_you` | **Waiting on you to put it live**, or **Prefilled and waiting on you to file it at the directory** | Done on our side, and yours to put live: paste it, post it, or file it. These rows are muted and never shown as live |
| `draft` | **Saved as a draft; publish it there to put it live**, or **Ready to download and use** on an image | Saved unpublished in your editor, such as a new article in Webflow or Notion |
| `synced_draft` | **Synced to Shopify as a draft; publish it there to put it live** | A new article saved unpublished in your Shopify blog |
| `open` | **Pull request #12 is open for review**, or **Being published** | A pull request waiting for review, or a change that is not live yet |
| `merged` | **Merged pull request #12** | The pull request merged |
| `live` | **Live** | An approved change one of our integrations put live itself |
| `shipped` | **You marked it live**, or **You filed it at the directory** | You put it live yourself |
| `closed` | **Pull request #12 closed without merging** | The pull request was closed without merging |

**Live** is the strongest claim on the list, and it has a rule: only an approved change that one of our integrations actually put live carries it. A draft, a pull request that has not merged, or anything waiting on you is never called live, whatever a vendor's own status says.

### What it did

Only something that shipped (merged, live, or marked live by you) can have moved anything. Until then, the column says what it is waiting for: **Measured once you put it live**, or **Measured once it is live**. A pull request closed without merging reads **Not shipped, so nothing to measure**. After that, it reports the [re-reads](/measure/before-and-after):

| What you see | What it means |
| - | - |
| **Waiting for the +7 re-read** | Shipped, and the first re-read has not run yet |
| **Up on 3 of 4 readings at the +14 re-read** | Named more often, past the significance test, on that many of the prompt and engine readings |
| **Down on 1 of 4 readings at the +7 re-read** | Named less often, past the same test |
| **Up on 2 and down on 1 of 4 readings…** | Both directions are real, so no single arrow is shown |
| **No change at the +30 re-read** | No difference past the test |
| **Nothing we can prove yet** | A difference inside the noise. This is a verdict of its own, never counted as up or as flat |
| **Not measured: no tracked prompt was named for it** | Nothing will be measured: the work named no tracked prompt |
| **Not live yet at the +7 re-read; reading again at +14** | The re-read did not find the change on the live page, so it measured nothing and waits for the next one |
| **Not live when we re-read it at +30, so nothing was measured** | No re-read found the change on the live page, and none is left |
| **Its re-reads were cancelled** | The re-reads stopped before any reached a verdict, for example because every prompt they would read was deleted |

The latest re-read with a verdict is the one shown. If a later re-read found the change gone from the page, that is added to the line. An image is not measured on its own; the page it goes on is.

## Opening a deliverable

Click a row to open it beside the list. The panel shows its **Status**, **What it did**, and **Approved by**: the person who said yes, or **your standing permission** when the workspace's [approval mode](/agent/approval) let it go ahead on its own. Then a preview of the thing itself:

| Type | Preview |
| - | - |
| Article | The article, rendered. While it still waits for your yes, this is its current text, including any edit made in Studio; after that, it is exactly what was delivered |
| Page change, Product copy | The field before and after |
| Fix | The pull request, read live from GitHub, as in the [Inbox panel](/agent/github#reviewing-a-pull-request-in-attensira). If GitHub cannot be reached, or the repository is no longer connected, the change as we filed it is shown instead, with a line saying why |
| Image | The picture |
| Post | The post's text |
| Listing | The listing's fields, one per row |

Ready for review and Merge stay in the Inbox. The preview here only shows the pull request.

### Actions

Each one appears only when it has somewhere to go:

* **Open where it lives**: the pull request, the draft, the post or the page.
* **Download**: an article as **Markdown** or **HTML**, a picture as itself, anything else with text as Markdown. A fix has no download: the pull request is where it lives. A download comes from your signed-in session, never from a public link, and the HTML version is rendered without any raw HTML the text contains.
* **Watch the run**: opens the chat or automation run that made it, at the step that made it. Work recorded before we kept that link has no button.

## From your assistant

The [MCP server](/mcp/overview) has a read-only `list_deliverables` tool over the same list, for one workspace at a time, and the REST API serves it as [`GET /v1/deliverables`](/api/endpoints#get-v1deliverables). Neither spends credits, and both work on a spent balance.

| Argument | What it does |
| - | - |
| `type` | One of `article`, `page`, `fix`, `social_post`, `listing_fix`, `product_copy`, `image` |
| `work_area_id` | Only what one project's runs made. Project ids come from `list_projects` |
| `status` | One or more delivery statuses, comma-separated: `waiting_on_you`, `draft`, `open`, `merged`, `synced_draft`, `live`, `shipped`, `closed` |
| `cursor` | The `next_cursor` of the previous page |

It answers `{deliverables, next_cursor}`, newest first, twenty to a page; `next_cursor` is `null` on the last page. Each deliverable carries:

| Field | Notes |
| - | - |
| `id`, `type`, `title` | |
| `work_area_id` | The project whose run made it. `null` for work no project's run produced |
| `location` | `{provider, label, url}`. `provider` is the kind of place: `pull_request`, `cms`, `notion`, `copy`, `listing`, `integration`, `session` (an image, in the chat that drew it) or `none`. `label` is the same sentence the table shows, and `url` the link, `null` when there is none |
| `status`, `status_label` | One of the statuses above, and its sentence |
| `result` | `null` when there is nothing to measure. Otherwise `{state, label, verdict}`, with `state` `pending` or `measured` and `verdict` one of `up`, `down`, `flat`, `noise`, or `null` until a re-read reaches one |
| `approved_by` | `{type, name, at}`, or `null`. `type` is `customer` for a person and `system` for your standing permission |
| `run_id`, `run_url` | The session that made it, which `get_session` reads, and its replay at the step that made it. `null` for work recorded before we kept that link |
| `dashboard_url` | This page, filtered to the deliverable's workspace and type |
| `created_at`, `shipped_at` | `shipped_at` is `null` until it merged, went live or was marked live |

An unknown `type` or `status` is refused with the valid values in the message, so an assistant can correct itself in one step.


# What the agent notices on its own
Source: https://docs.attensira.com/agent/findings

The hourly rules that watch your readings and file what changed, without a person. A change too small to tell from sampling noise is held back and checked again an hour later.

## What a finding is

Every hour, a pass reads your workspace's recent readings and checks them against five fixed rules. The rules are arithmetic over the same numbers your dashboard shows, with no model and no prompt being asked. That makes them fast, cheap and consistent: the same window always produces the same findings.

What a rule finds is then measured before it is filed, so that sampling noise does not reach your Inbox. See [Noise, or a real change](#noise-or-a-real-change).

## The five rules


  
    A page on your domain was cited on at least three of the last seven days, and on none of the last three — with at least one successful read on each of those three days, so a run of scraper failures never looks like a loss.
  

  
    A rival is named on a prompt where it was not named before: named on at least two of the last three days, and never named across the seven days before that — counted only over days you actually read that prompt on that engine.
  

  
    A page of yours was cited somewhere in the prior seven days, and in none of the last three, on one engine — with every one of those three days carrying a successful read. This is the whole site going quiet on an engine, not one page slipping.
  

  
    A domain that was not among an engine's top ten cited sources is now in it. This only fires once that engine has a full prior week to compare against — an engine you started sampling this week does not report its entire source list as new.
  

  
    A page on your own domain changed since the last pass. Not a loss, and never filed as one — it is surfaced because it is usually the answer the other four rules are asking for.
  


## A day that could not be read never counts as a loss

This is the rule everything above is arranged around. Every finding that claims an absence — a citation stopped, a page dropped out — first checks that every day it is claiming silence from carried at least one successful read.

A day with three failed reads and a day with no reading at all look identical to this pass on purpose: both are silence, and silence is never evidence of you losing something.

## Noise, or a real change

A rule says what moved. It cannot say whether it moved by more than sampling moves on its own: a prompt read three times a day can lose a citation for three days on luck alone. So each finding is measured before it is filed.

1. **The arithmetic.** The finding's last three days are compared with the seven days before them, on successful reads only, with Fisher's exact test. A change significant at p \< 0.01 is always filed.
2. **One decision, on numbers only.** A finding that is not significant, and has no Inbox row open yet, is put to a [decision](/agent/decisions#noise-or-a-real-change): is this a real change, or noise? The decision reads the per-day counts (successful reads, failed reads, answers that named you and did not), the two windows' rates and the p-value. It never sees a domain, a URL or a prompt's text.
3. **Held as noise.** Only when the decision is confident the change is not real is the finding held. A held finding is not filed, is not an event, and starts no run. It is held for that hour only: the next pass measures it again, asks again once new readings have landed, and files a change that holds up. Each hold is recorded with the n it was judged on, the number of successful reads across both windows.

Three kinds of finding are never held: a significant change, a finding whose Inbox row is already open (that row is always bumped), and a change to your own pages.

When more findings are found than there are rows free, a second decision says how much each one matters, and the most serious take the rows first. It only changes the order. A finding that misses a row still bumps the closest open one.

If no decision can be made, every finding is filed in the rules' own order, exactly as it was before decisions existed.

## One row per deciding source, not per prompt

A source that decided seven prompts is one problem, not seven. Findings are grouped by the source that caused them before they reach your [Inbox](/agent/inbox), so the row reads **"affects 7 prompts"** and grows in place instead of filing seven near-identical rows.

The Inbox stays worth opening: **at most three open rows** from this pass at a time. A fourth finding bumps the closest existing row rather than raising a new one.

## What happens when a row is raised

A run may start under your workspace's [approval mode](/agent/approval) — the same setting that governs everything else the agent does on its own. What a finding is allowed to do next, from opening the row for you to reading further to proposing a fix, follows that setting exactly as a chat or automation would.

In Draft and Act modes the run starts on its own, at most once per finding per day. It says why it started, in the finding's own words: **Because example.com stopped being cited**. In Ask mode no run starts; the row opens a chat with the question ready for you to send.

## Every finding is also an event

Each finding also becomes an **event** that [event automations](/agent/autopilot#event-automations) can react to, even when the three-row Inbox cap stops it from filing a row of its own. A finding [held as noise](#noise-or-a-real-change) does not become an event.

| Finding | Event |
| - | - |
| A page of yours stopped being cited | `citation_lost` |
| You dropped out of every answer on an engine, or a competitor was newly named | `prompt_lost` |
| A new domain entered an engine's top ten | `new_deciding_source` |
| One of your pages changed | `page_changed` |

The same event is recorded at most once per subject per UTC day, so a loss that persists does not wake an automation every hour.

## Detection and the old Daily check-in

Detection does not pause the [Daily win plan](/agent/autopilot#the-daily-win-plan); the two run side by side. It only retires an old, untouched **Daily check-in** automation that still runs the daily-losses procedure, and only when detection is on for the workspace, so the same loss is not reported twice from two places. A check-in you rewrote is left alone.

## Who has it

Paid workspaces are enrolled in hourly detection automatically, the first time their subscription is active.


  Findings are read-only in what they claim: a rule never invents a percentage from a single day, and a claim about a page always comes from a page the pass actually read.



# GitHub
Source: https://docs.attensira.com/agent/github

Install the Attensira GitHub App and the agent opens pull requests against your site's repositories, as drafts by default. It never pushes twice, and merges only when you press Merge in your Inbox, or a safe change when you switched on auto-merge.

## What the GitHub connection is

Attensira finds things worth fixing on your site. GitHub is how a fix leaves the product and arrives somewhere you can review it: the agent opens a **pull request** against the repository the page actually lives in.

This is a first-party **GitHub App**, installed by you into your organisation or account. It is not an OAuth app, and it is not brokered through a third-party connector — the App is ours, the installation is yours, and you can see and revoke it from GitHub's own settings at any time.

## Connecting the App


  
    From the project's GitHub settings, press **Connect GitHub**. GitHub takes over and asks which account or organisation to install into.
  

  
    Grant the App **only selected repositories**, and pick the ones whose content you want changed. GitHub, not Attensira, holds this list — changing it later is done on GitHub's installation page.
  

  
    Back in Attensira, choose which of the installed repositories this project may open pull requests against.
  


### Where your site lives in the repository

Your site does not have to be at the repository root. A framework config file up to two directories deep, such as `src/next.config.mjs` or `apps/web/next.config.ts`, is detected, and content roots are reported relative to the repository.

**A project can have several repositories.** That is the normal case, not an edge case: a marketing site, a docs site and the app itself are often three separate repos, and a finding on `/pricing` belongs in a different one from a finding on `/docs/quickstart`. Attach all of them and each pull request goes where its page lives.

## What we do and do not do


  We open pull requests. Beyond that we only take one of ours out of draft or merge it, under the rules below. That is the whole of our write access to your codebase.

  * We **merge only when you press Merge** in your [Inbox](#merging-from-your-inbox), or when you switched on [auto-merge for safe changes](/agent/approval#auto-merge-for-safe-changes). Your Merge is pinned to the commit you reviewed; auto-merge merges only a safe change, at the commit we pushed. Either way, only once GitHub says every required check and review is satisfied.
  * We **never push to your default branch**. Every change is on a fresh branch of ours.
  * We **never push twice to the same branch**. One finding, one branch, one push — a pull request we opened is final from our side.
  * We **never touch CI configuration**, workflow files, or anything else that changes how your builds run.


The point of those four rules is that the pull request is the whole surface. Nothing we do can change once you are looking at it, so reviewing our diff is reviewing the final thing.

## Permissions we request

Exactly three, and no others:

| Permission | Access | What it is for |
| - | - | - |
| Contents | Read and write | Read the files behind a page, and push our branch |
| Pull requests | Read and write | Open the pull request, and read its state afterwards |
| Metadata | Read | Mandatory for every GitHub App; lists the repositories you granted |

There is no Actions permission, no Administration permission, and no access to secrets. If GitHub's consent screen shows you something not on this list, you are not installing our App.

### If you cannot install it yourself

Most people who pay for Attensira cannot merge to their own site. That is the normal case, and the agent has a path for it: it writes the fix as a **rewrite**, the passage as published beside a better version of it, instead of a pull request.

On any rewrite in your Inbox there is **Request access from my developer**. Type their email and Attensira writes the ask for you: what the change is, a private link to the rewrite so they can read it before deciding, the install link, the three permissions above, and the four things we never do.

**We do not send it.** The email is written as a draft in your own mailbox (Gmail or Outlook, through the mailbox you connected on the Plugins page), addressed to whoever you named. You read it, change it, and send it, or do not. If you have no mailbox connected, Attensira hands you the text and opens it in your own mail app instead.

Once the App is installed, the same findings arrive as pull requests instead of rewrites.

## What we may change in a file

Pages written in Markdown, MDX and HTML are edited under the [change classes](/agent/approval#what-attensira-changes-on-its-own-and-what-it-always-asks-about). Source files are different:

* In `.ts`, `.tsx` and `.js` files we may edit **only the words inside string literals**: titles, descriptions, copy in data files. Such a change always waits for a person (class **claim**, rule `source-copy`).
* A literal that is code is refused as source: an `import` or `require` path, a `'use client'` or `'use server'` directive, a raw-HTML (`__html`) payload, or an evaluator's argument.
* Any other change to a source file is refused.

If a page's title lives in a file outside your content roots (in `src/lib`, say), the agent says so rather than editing it. Add that directory as a content root and the next proposal can reach it.

## How a finding becomes a pull request


  
    The agent identifies something to fix on a page — a missing answer to a tracked prompt, a claim that has gone stale, a page with nothing citable on it.
  

  
    It locates the file that produces that page in one of the attached repositories, including where the page's title and description are defined. A finding whose source it cannot find does not become a pull request; it becomes an [Inbox](/agent/inbox) row saying so. A proposal for a path that does not exist yet, such as a new blog post under a content root, creates a new file.
  

  
    It writes the change and pushes it to a new branch. The branch is created for this finding and is used once.
  

  
    It opens the pull request against your default branch, with a body that says which page, which finding, why this edit answers it, and whether it will auto-merge — and applies the `attensira` label.
  


Approval rules apply here as they do everywhere else the agent writes outward: if the project requires your yes before an outward write, the pull request is not opened until you give it. See [what the agent shows you](/agent/what-the-agent-shows).

## What happens after the pull request

We keep watching it. Attensira tracks whether the pull request was **merged** or **closed**, and what your **CI** said about it, and shows that on the row in your [Inbox](/agent/inbox).


  **A red build or a merge conflict is surfaced to you and left there. We do not push again.**

  The no-second-push rule has no exception for fixing our own work. If checks fail, or the branch conflicts with something that landed after we opened it, the Inbox row will tell you — and then nothing further happens until a human acts. Nobody should sit waiting for a follow-up commit that is not coming.


What to do with one is your call: push a commit onto our branch yourself, close the pull request, or resolve the finding some other way and mark the Inbox row **Done**. Once GitHub says it can merge, you can [merge it from your Inbox](#merging-from-your-inbox).

**Closing an Attensira pull request without merging declines the work item** ("closed without merging"), and we do not open it again. Reopening the pull request brings the item back.

The item's activity reads "Opened pull request #N … goes live when it is merged and deployed", not "Published": a pull request is not a live page. Once it merges, the tracked prompts attached to it (the `prompt_ids` the agent gave `propose_fix`) are [re-read](/measure/before-and-after) at +7, +14 and +30 days. Without attached prompts, no before-and-after is measured.

## Reviewing a pull request in Attensira

You do not have to leave for GitHub to see what a pull request of ours would merge. Open the item in your [Inbox](/agent/inbox): once the change is approved and its pull request exists, the item's detail shows that pull request **read live from GitHub** at the moment you opened it. Nothing in it is a stored copy, and it is read only for the item you open, never for the whole list.

What the panel shows:

* **The title and description**, as GitHub holds them now. Anyone with write access to the repository can edit a description, so only basic formatting is drawn, only web and mail links become links, and nothing in it runs.
* **Every changed file**, with GitHub's own diff. Up to 300 files are listed, and one file's diff is cut at 64 KB; either cut says so and links to the full diff on GitHub. A binary file, or a diff GitHub itself will not render, is listed without a diff.
* **The checks** on the branch's latest commit: check runs and commit statuses in one list, each with its state (**Failing**, **Running**, **Passed** or **Neutral**) and a link to its own page. If the installation cannot read checks or commit statuses, the panel says which read was refused in place of the list. An empty list means nothing ran; a missing list means we could not read it.
* **A deploy preview**, when one of those checks or statuses links to a preview on `*.vercel.app`, `*.netlify.app` or `*.pages.dev`. We do not read GitHub's deployments, so a preview your hosting reports only there does not appear.
* **Someone else's commits.** If the branch's latest commit is no longer the one we pushed, the panel says so: their commits are in the files shown, and in what a merge would merge.
* **Why it cannot merge yet**, in one sentence, whenever GitHub says it cannot:

| GitHub's state | What the panel says |
| - | - |
| Draft | It is still a draft on GitHub |
| Conflicting | It conflicts with `` |
| A check failing | A check is failing: `` |
| Blocked by branch protection | A required review or check is missing |
| Behind its base | The branch is behind `` |
| Not decided yet | GitHub is still checking whether this can merge |

If GitHub does not answer, the panel says so and keeps the **Open on GitHub** link, which still works. If the repository has been disconnected from the project since, the pull request can only be opened on GitHub, and the panel says that instead.

## Merging from your Inbox

Approving the change is what opened the pull request. **Merging it is a second, separate yes**, and you can give it without leaving Attensira, from the [panel above](#reviewing-a-pull-request-in-attensira). Under the pull request there is at most one button, only while the pull request is open on an approved item, and it depends on where the pull request is:


  
    Shown while the pull request is a draft, which is how [new repository connections open them](#how-your-merge-automation-interacts-with-us). It takes the pull request out of draft on GitHub and does nothing else: no commit, no merge. If your repository merges ready pull requests on its own (GitHub auto-merge, a merge queue or a bot), that may merge it without another click from you; the confirmation says so before you press it. Like Merge, it is refused if the branch moved since you looked.
  

  
    Shown once it is out of draft. It squash-merges the pull request into its base branch, at the commit you were looking at. Anything that deploys from that branch ships it.
  


Each button asks once, inline, before it acts. Neither is ever pressed by the agent, and there is no "approve and merge" in one click.

**Merge goes ahead only when all of these hold:**

* The pull request is still open. One that was closed without merging is refused.
* Its branch is still at the commit you reviewed. If anyone pushed since, nothing is merged, the panel is read again, and it says **The branch changed. Review it again.**
* It is not a draft.
* GitHub reports it mergeable with nothing in the way. Otherwise Merge is greyed out, with the sentence from the [table above](#reviewing-a-pull-request-in-attensira) beside it. A failing check blocks Merge even when your branch protection does not require it: fix the check, or merge on GitHub.

You may merge a change of any [class](/agent/approval#what-attensira-changes-on-its-own-and-what-it-always-asks-about), including one classed **claim** that would never merge on its own: the click is your yes to that specific merge.

**Pressing Merge twice is safe.** If the pull request is already merged, the second press reports it merged and does not merge again. The item's activity records "Merged pull request #N by" the person who pressed it.

A merge from here is recorded the same way as a merge on GitHub: the row moves to merged, and its [re-reads](/measure/before-and-after) are booked, only when GitHub reports the merge back to us.

Merging and marking ready are a signed-in person's clicks in the dashboard. The [REST API](/api/overview) and the [MCP server](/mcp/overview) cannot merge a pull request or take one out of draft, and neither is offered to an assistant.

## How your merge automation interacts with us

We open a pull request. Your branch protection and merge automation decide what happens next — **if your repo auto-merges on green, a pull request of ours that is ready for review will merge too.**

Two controls make this predictable:

* **Draft mode (`open_as_draft`).** New repository connections open pull requests as GitHub **drafts by default**. Most merge automation ignores drafts, so nothing of ours can land until you mark it ready for review, on GitHub or with **Mark ready for review** in your Inbox. The per-repository toggle overrides the default either way. The exception is a pull request the workspace set to [auto-merge](/agent/approval#auto-merge-for-safe-changes): it opens ready for review.
* **The `attensira` label.** Every pull request we open carries it, without exception. It is a stable hook for a rule on your side — exclude the label from an auto-merge workflow, require an extra reviewer on it, or route it to a particular team.


  We do not read your branch protection settings and cannot tell you what your automation will do with our pull request. If you are not sure, keep draft mode on, watch the first few, and turn it off once you are.


## Lovable

If your site is built in [Lovable](https://lovable.dev), there is nothing separate to connect. A Lovable project can sync to a GitHub repository, and from then on a merge on the branch Lovable syncs shows up in the Lovable editor. So a fix reaches Lovable the same way it reaches any repository: as a pull request on that repository, which lands in Lovable when it is merged.

### Connecting

**Lovable** has its own card among your connectors (**Customize → Connectors**), but connecting it is the GitHub App install above: pick the repository your Lovable project syncs to, and attach it to the project. The card shows as connected once at least one attached repository syncs to Lovable. Disconnecting the Lovable card disconnects only those repositories; any other repository the project has stays connected.

### How we recognise a Lovable repository

When the repository is attached, we look for either of Lovable's own fingerprints on its default branch:

* `lovable-tagger` among the dependencies in `package.json`, which Lovable adds to every project it generates; or
* a README that links to `lovable.dev`, as the generated one does.

Either is enough. If neither is there, the repository is treated as an ordinary repository. If the files could not be read, nothing is decided: the repository works as an ordinary one, and we look again before the next pull request opens.

### What changes for a Lovable repository

The pull request itself is the same one any repository gets: a fresh branch, opened against your default branch, under the same [rules](#what-we-do-and-do-not-do). Two things are worded differently:

* **The pull request body** ends with: "Merging this lands it in your Lovable project if `` is the branch Lovable syncs. Your live site changes when you next publish from Lovable."
* **Where it lives**, on [Deliverables](/agent/deliverables) and in `list_deliverables`, reads **Lands in Lovable after merge** while the pull request is open and **In Lovable** once it is merged. A pull request closed without merging landed nowhere, so it reads as the plain pull request it was.


  **Two things we cannot see.** We cannot read which branch your Lovable project syncs: if you switched Lovable to a branch other than the default, a merge into the default branch does not reach Lovable. And Lovable does not publish on a sync: a merged change is in the Lovable editor, and reaches your live site only when you publish from Lovable.


The [re-reads](/measure/before-and-after) are booked when the pull request merges, as for any repository, and we cannot tell whether you have published from Lovable since. Publish soon after you merge, so the first re-read, seven days later, measures a page that carries the change.

## Disconnecting

Disconnect from the project's GitHub settings, or uninstall the App from GitHub — either one ends our access immediately.

Pull requests we already opened **stay exactly as they are**. They are ordinary pull requests in your repository, owned by you: nothing is closed, withdrawn, or rewritten on the way out. What stops is everything future — no new branches, no new pull requests, and no further tracking, so the merged/closed and CI state on existing Inbox rows freezes at whatever it was when you disconnected.

Reconnecting starts the same install flow again. It does not resume tracking on pull requests opened before you disconnected.


# Google Ads
Source: https://docs.attensira.com/agent/google-ads

The agent researches keywords, drafts a Search campaign and files it for your approval. Nothing reaches Google and nothing is spent until a signed-in person says yes in the app.

The agent builds and runs Google Ads **Search** campaigns for your own site. It does the research, writes the campaign and watches it once it runs. You set the monthly budget and approve every yes that spends money.

Ad spend is paid to Google, from your own Google Ads billing. It is not our fee and it never comes out of your [credits](/account/credits#google-ads).

## Building a campaign

Ask for one in a [chat](/agent/chats), for example "Draft a Google Ads Search campaign for our pricing page, US only, and don't send anything to Google until I approve it." The agent works in three steps, and none of them writes to Google.


  
    It looks up up to 50 keywords at a time in one market: searches a month, average cost per click, the top-of-page bid range and paid competition. They are shown as a table, with amounts in the currency the data source reports. A number the data source does not have reads **unknown**, never zero.
  

  
    It reads the landing page and writes the campaign: ad groups and keywords, exactly **15 headlines** and **4 descriptions**, two display paths, negative keywords and up to two generated images (landscape 1.91:1 and square 1:1). The draft is checked against the rules below and shown to you as a preview. Nothing is filed yet.
  

  
    When you are happy with the draft, the agent files it as **one item in [Work](/agent/inbox)**, shown in the chat as an approval card. The card and the row in Work are two views of the same item: approving either one is the same yes.
  


### The budget is yours

The agent asks you how much you want to spend on Google a month, and drafts with the amount **you** gave. It cannot choose a budget for you: a monthly budget you did not state in that conversation is refused, and so is one a web page it read tried to talk it into.

The daily budget is set so a month of it stays under your monthly figure. Google counts a month as **30.4 days**, so a monthly budget of $1,520 becomes a daily budget of $50.

Your monthly figure is what you spend on Google **in total**, across every campaign in the workspace. A campaign holds its budget from the moment it is filed for your approval until you stop it or decline it. A campaign the spend guard paused still holds its budget, because it is offered back next month, and so does one Google stopped serving (a suspended billing account, for example), because it goes back to running when Google serves it again. So a second campaign is drafted and filed only when you have said a monthly total that covers it on top of what the others hold: with one $1,500 campaign filed, a second $1,500 one needs you to say \$3,000. Otherwise the agent asks you. A higher daily budget the [optimizer](#the-rules-the-optimizer-runs) proposes is a separate yes: approving it raises your total by that much.

Drafting and filing need a person in the conversation to set the budget, so they happen in a chat you are part of, never in a scheduled automation. Keyword research is available to automations too.

### What a draft must pass

A draft that breaks any of these rules is repaired once. If it still breaks one, it is refused, and the agent tells you which rule.

| Rule | Detail |
| - | - |
| Copy length | Exactly 15 headlines of at most 30 characters, 4 descriptions of at most 90, display paths of at most 15. Double-width characters (Chinese, Japanese, Korean, fullwidth forms) count as two, the way Google counts them |
| Your own site | The ads send people to the page the agent read, on the workspace's own domain. A page that asks for a different address does not change this |
| No competitor names | None of your [tracked competitors'](/tracking/competitors) names or domains appear in the ad copy |
| No unsupported figures | No number that neither the landing page nor your company facts state |
| Your words to avoid | None of the phrases your workspace's guidelines say never to use |
| Match types | Keywords are exact or phrase match. Broad match lets Google decide what the money is spent on, so it is not offered |
| Within your budget | The daily budget is held under the monthly budget you set |
| Spreadsheet-safe | No headline or description starts with `=`, `+`, `-` or `@` |

Every campaign bids to maximize conversions, with your target cost per conversion when you give one. A campaign we create in your account shows to people **in** the places you chose, not people interested in them. The import file does not carry that setting, so Google Ads Editor gives the campaign its own default, which also reaches people interested in those places; change it in Editor if you want presence only.

## Approving a campaign

The approval card says, before you answer: the daily budget and about what it comes to a month, how the campaign will reach Google, what Google said about it when it could check it, how many headlines, keywords and ad groups it has, and that ad spend is paid to Google.

You can approve the proposed daily budget, or **start at** the lower budget the card offers beside it, which is half. The lower budget is the one stored with the draft. Nobody can approve a budget higher than the one you read.

### Spend is approved in the app, by a person

Approving a campaign, or any change that can make Google charge more or cannot be taken back, must be **a signed-in person's own yes in the Attensira app**. It is refused from anywhere else:

* an [API key](/account/api-keys), including `POST /v1/inbox/{id}/approve`;
* an assistant over [MCP](/mcp/overview), including `approve_work_item`;
* a [client reviewer link](/agencies/client-reviewer-link).

Each gets **403 Forbidden** with the reason `spend_approval_in_app` and a message saying where in the app to approve it. This holds in every [approval mode](/agent/approval#google-ads-spend), Act included, and no setting changes it.

The agent never approves its own campaign. It files the item and stops.

## How a campaign reaches Google

There are two paths. Which one a campaign takes depends on whether we can write to your Google Ads account at the moment it is filed, and the approval card says which.


  
    Approving gives you a **Google Ads Editor import file**. Nothing is written to your Google Ads account by us.

    1. In Work, choose **Download import file**. Each download is a fresh link.
    2. Import the file with Google Ads Editor. The campaign imports **paused**, so nothing spends until you turn it on in Google Ads.
    3. Choose **Mark imported**. The campaign becomes **Imported** and its item in Work closes.

    The file holds the campaign, its locations and languages, ad groups, keywords, negative keywords and a responsive search ad per ad group. The generated images are not part of it.

    On this path **Google's own budget is the only cap**. We cannot see or pause a campaign in an account we cannot read.

    Marking it imported never makes it **Live**: we cannot see an account we cannot read. If we can later read the account, and you have marked the campaign imported, the daily sync finds it by the tag at the end of its name (`[att-…]`) and takes it over as a campaign we manage; from then on its status, spend and the [spend guard](#the-spend-guard) apply to it. Keep the tag in the campaign name, or we cannot match it.
  

  
    Approving builds the campaign in your Google Ads account through the Google Ads API. Before filing, Google is asked to check the campaign without creating anything, and the card shows "Google accepted this campaign (dry run)".

    On approval the campaign is created **paused**, read back to confirm it matches what you approved, and then turned on. Work says "Sent to Google Ads. It shows as live once Google reports it serving."

    A retry never builds a second campaign: before creating, we look the campaign up by its tag and continue from where the last attempt stopped. If turning it on fails after it was built, it stays paused and a row in Work says why.

    On this path the campaign is capped twice: by its Google daily budget, and by our [spend guard](#the-spend-guard).
  


If the path changes after a campaign is drafted, nothing is sent the other way. A campaign approved as an import file is never written to your account, and one approved to be created in it is never handed off as a file instead: it comes back to you to draft or approve again as what will actually happen.

## Connecting Google Ads

Google Ads is connected as a connector on the workspace (see [Plugins](/agent/plugins)). It is read-only to the agent: no agent tool can write to your Google Ads account, in any mode. An approved item is the only way anything is created or changed there.

One Google login often reaches several Google Ads accounts, such as your own and the clients under a manager account. The account is where the money is spent, so **you choose it**. When the login reaches exactly one account it is chosen for you; otherwise a row in Work asks you to pick, and nothing is guessed.

Without a connection, every campaign takes the import-file path.

## Campaign status

A campaign's status is the server's, read from Google. Approving a campaign never makes it **Live**: only the sync does, from Google reporting that the campaign is on and serving.

| Status | Meaning |
| - | - |
| **Draft** | Drafted, not filed. Nothing is sent to Google |
| **Waiting for your yes** | Filed in Work for your approval |
| **Ready to import** | Approved on the import-file path. Download the file and import it |
| **Imported** | You marked it imported. It shows as live only once we can read your account and Google reports it serving |
| **Building** | Being created in your Google Ads account, paused |
| **Paused** | It exists in Google Ads, paused. Nothing spends |
| **Live** | Google reports it serving |
| **Limited** | Google is serving it, but limited, for example by budget or because it has not started yet. The reason is shown |
| **Stopped** | Stopped by the spend guard, or because its end date passed. The reason is shown |
| **Needs attention** | Google refused a step, or stopped serving the account. The reason is shown |

## What runs on its own, and what always asks

Once a campaign runs, the agent reads what it spent on every day and proposes changes. The rules are fixed and deterministic. A language model writes only the one-line "because" each change carries, from the figures the rule used.

Each change is classed from the campaign's state before and after it, never from what the agent says about it.


  
    Changes that can only narrow where your ads show or lower spend, and can be undone:

    * adding negative keywords;
    * pausing keywords;
    * pausing a campaign;
    * lowering the daily budget or the target cost per conversion;
    * promoting an ad variant that already ran, with copy you already approved.

    In [Act](/agent/approval#act) they go out without waiting, and count toward your [change caps](/agent/approval#change-caps) like any other change made without you. In Ask or Draft they wait in Work for your yes, which you can also give over the API or MCP.
  

  
    Anything that can make Google charge more, or that says something new in your name:

    * creating or turning on a campaign;
    * raising the daily budget or the target cost per conversion, or removing the target;
    * adding keywords, or turning a keyword or the first ad in a campaign on;
    * removing a negative keyword;
    * new ad copy.

    These are never approved automatically, and are approved only in the app (see [above](#spend-is-approved-in-the-app-by-a-person)). Any change the classifier does not recognise is treated the same way.
  


### The rules the optimizer runs

The optimizer runs once a day, after the account's sync. It leaves a campaign alone for its first **72 hours** of serving, and reads the last **14** complete account days.

| Rule | What it proposes |
| - | - |
| Low-intent searches | Searches for jobs, logins or cracked copies that cost money become phrase negatives, unless the campaign targets that word itself |
| Wasted search terms | A search term with at least 2 clicks, no conversion, and a cost of at least twice your target cost per conversion becomes an exact negative |
| Out of budget while converting under target | When the campaign's cost per conversion is at or under your target and it spent at least 90% of its daily budget on 5 of the last 7 days, a 20% higher daily budget. This always waits for you, and approving it also raises the monthly budget the spend guard holds the campaign to |

The last two rules need a target cost per conversion on the campaign and conversion data: a cost per conversion is judged over at least 3 conversions, and both rules are off for an account that tracks no conversion action. A campaign drafted without a target gets the low-intent rule only.

The optimizer files at most one change per campaign, per day, per kind of change: the two negative-keyword rules share one change, which adds at most 20 negatives. A change still waiting for you is revised in place the next day rather than joined by a second one.

## The spend guard

Your monthly budget is held two ways when we can read your account. Each campaign is held to its own approved budget, and because [those budgets together fit inside the total you set](#the-budget-is-yours), holding each one holds the total.

* **Google's budget.** The daily budget is at most your monthly budget over 30.4 days, so Google's own monthly charging limit holds the month.
* **Our spend guard.** Google may spend up to twice a daily budget on a busy day, and reports cost a few hours late. So once an hour the guard reads each managed campaign's month to date from Google, adds what it could have spent in the last 3 hours unreported, and **pauses** the campaign when that reaches **95%** of the monthly budget you approved it at: its approved daily budget over 30.4 days. If you approved the lower starting budget, that is the figure the guard holds it to.

The pause can only set the campaign to paused. It runs in every approval mode with no approval behind it, because stopping spend can be undone and waiting for a yes is how a cap is overrun. It is recorded in the activity log as **Spend guard** and raised as a row in Work with its reason. If Google refuses the pause, the row in Work says so and asks you to pause the campaign yourself.

The guard never turns anything on. Once a campaign it paused has room under its budget again, for example in a new month, it files "turn it back on?" for your approval, and a signed-in person decides.

On the import-file path, the guard cannot see the campaign until we can read your account, and Google's budget is the only cap.

## Only campaigns we manage count

A campaign is **managed** when we built it, or when you imported it from our file and the sync matched it by its tag. Only managed campaigns:

* count toward ad spend figures;
* are held to a budget by the spend guard;
* are touched by the optimizer.

Campaigns you built yourself in the same account are labelled **Yours, not managed by us** wherever they are listed, and left out of every total. We do not change them.

## Ad spend

* **Paid to Google, not our fee.** You pay Google from your own Google Ads billing.
* **In your ad account's currency.** Figures are in the currency of the Google Ads account and are never converted. Accounts in two currencies are never added together.
* **Its own line.** Wherever ad spend is shown, it is on its own line, marked as paid to Google and not our fee, and it is never added into your budget, your credit spend or any other total.
* **Managed campaigns only**, as above.

## The numbers

Every Google Ads figure comes from one daily table, read from Google. When you ask the agent about ad performance it reads the same rows (the `ads_spend`, `ads_impressions`, `ads_clicks` and `ads_conversions` metrics), so its answers and every other Google Ads figure come from the same days.

| Figure | Definition |
| - | - |
| Spend | What Google charged for the campaigns we manage, in the ad account's currency. Month to date by default |
| Clicks | Clicks on the managed campaigns' ads. Impressions are shown beside them |
| Conversions | Google's own conversion count, which can be fractional under data-driven attribution. Each figure names its source: the conversion action, or "As Google Ads counts them" |
| Cost per conversion | Spend ÷ conversions, over the same campaigns and days. A dash when there were no conversions, never a zero |
| Search terms | What people typed before an ad showed, costliest first. The 500 costliest per campaign per day are kept for 13 months, and appear the day after |

* **A day is the ad account's day**, in its own time zone, the day Google bills and reports by.
* **A day Google has not reported is a gap, never a zero.** See [gaps are gaps](/agent/what-the-agent-shows#gaps-are-gaps-not-zeros).
* **The last 3 days are read again on every sync**, so recent figures can still change as Google settles late conversions and refunds invalid clicks.
* **Every figure carries its as-of time**: when Google last reported it.
* **No conversion action tracked** shows a dash for conversions and cost per conversion, and switches off the optimizer's cost-per-conversion rules.

### Where you see them

* **Analytics → Ads** shows the window's spend and this month's, clicks and conversions by day, every campaign (ours and the ones you run yourself) with its status, and the search terms, costliest first. A workspace without Google Ads sees why there is nothing to show, not zeros.
* **Beside a project's feed**, a project whose team runs Google Ads shows what its work cost this month against the project's cap, and its ad spend on a separate line, with its campaigns.

## When something goes wrong

The sync runs once a day from 06:00 on the ad account's clock, and again about 15 minutes after any change we make. Nothing it finds is silent: each problem is a line in [loop health](/agent/loop-health) or a row in Work, and the ones you can fix are always a row in Work.

| What happened | Loop health says | In Work |
| - | - | - |
| We cannot reach the account yet | Waiting for Google Ads access | Nothing to do: campaigns take the import-file path |
| Access that worked stopped working | Google Ads stopped letting us read the account; it needs reconnecting | Reconnect Google Ads |
| No account chosen on the connection | No Google Ads account is chosen on the connection | Choose the Google Ads account |
| Google stopped serving the account | Google stopped serving the account: billing or setup is unfinished | Google stopped serving your ads: billing |
| Google's ad policies disapproved an ad | — (it is posted in the project's feed) | Google Ads disapproved ads in "*campaign*", with the policy |
| The guard paused a campaign | — | Paused "*campaign*" at your cap |
| Google asked us to slow down, three times running | Google Ads asked us to slow down, three times running | No row. Figures keep an older as-of time until a read succeeds |

## What we do not promise

Google decides where, when and how often an ad shows. We do the work: research, drafting, the daily reads and the optimizer's changes. We do not promise clicks, conversions or a position, and we do not claim that running ads changes what AI models say about you.

Only Search campaigns are built. Performance Max and Display campaigns are not.


# Inbox
Source: https://docs.attensira.com/agent/inbox

Everything the agent needs from you, as at most three decisions first: what to approve, what to answer, and what to do on your side. Plus what is running and what shipped.

## What the Inbox is for

The agent does the work and stops only when it genuinely needs you. The **Inbox** (at `/inbox`) is where all of it lands, so "does anything need me, and what did it do?" is one page rather than a hunt through run histories. Open it from the Inbox button in the top bar, on every screen; the badge on it counts what is waiting on you.

The Inbox replaced the separate Work and Studio screens. The list of rows waiting on a person is called the **inbox** in the API, MCP (`list_inbox`) and emails too.

It has four views, named for what you ask of it:

* **Waiting on you**: everything waiting on you, as decisions. The three that matter most come first; the rest are grouped by what they ask. See [At most three decisions first](#at-most-three-decisions-first). Approvals, suggested changes, and articles and posts **written for you** all wait here, and snoozed rows come back here when their time comes.
* **Running**: work that is approved and moving, and every **Done** we are still checking. Nothing here needs an answer; it is there so you can see what is happening.
* **Done**: merged pull requests, placed pitches and delivered changes, each with its [re-read](/measure/before-and-after) result. See [Shipped](#shipped).
* **From your assistant**: everything your own assistant filed over [MCP](/mcp/overview), open or moving.

Two more sit beside them: **Emails to send**, the outreach drafts only you can send from your own mailbox, and **Snoozed**, what you put off and when it comes back. Links to older tab names, such as the ones in emails we already sent, open the view that holds those rows now.

The Inbox names your workspace's [approval mode](/agent/approval) — ask, draft or act — because that setting decides what waits here at all. An empty **Waiting on you** means something different in each mode, and the line says which one is in force.

Every row has its own page at `/inbox/`, where you read it in full and act on it. A row's ref is the same everywhere: in the Inbox, on Home, in emails, and in the API and MCP.

## Three kinds of row

Every row waiting on you asks one of three things of you. We work out which, not the agent and not the screen, so the Inbox, Home, the morning brief, the daily email, the API and MCP all agree on what a row is asking.

| Kind | What it asks | What you do |
| - | - | - |
| **Approve** | We wrote something and want a yes or a no: a proposed change, an article, or a run that stopped at the [approval gate](/agent/approval) | Approve, edit then approve (for the kinds that allow it), or decline with a reason |
| **Answer** | We need a fact only you have | Type the answer, or pick one of the answers offered. It is saved and the run that asked carries on |
| **Do on your side** | Something only you can do somewhere else: connect an integration, add a DNS record, paste new wording into your CMS | Follow the steps, then press **Done**. When we can check it, we do |

A few rows are notices: they tell you something, such as credits we added back to your balance, and ask nothing. They are listed last and never count as a decision.

## At most three decisions first

A row is one attempt; a decision is one thing for you to decide or do. Seven runs that hit the same missing GitHub connection are seven rows and **one** decision, shown as **Connect GitHub · 7 waiting**. Every row about the same missing integration folds into one decision, and so do approvals waiting on the same tool. Each proposed change is its own decision.

**Waiting on you** shows the three decisions that matter most, ranked in this order:

1. An approval that expires within a day. If it lapses, the run goes on without doing what it asked.
2. How much is at stake: a whole capability switched off, then a failed run, then a run stopped on one thing or an approval that expired, then a judgement call or an unsent rewrite.
3. An approval whose effect cannot be undone, which includes anything that spends money.
4. A question, because an answer is the quickest thing on the list to give and unblocks a run straight away.
5. How many rows are waiting on it.
6. How long it has been waiting, oldest first.

Everything after the first three is grouped by what it asks, such as **2 more questions** or **3 more approvals**, and stays collapsed until you open it.

This one ranking is read everywhere "what needs me" is shown: the Inbox, **Needs you** on Home, the badge, the [morning brief](/agent/morning-brief), the [daily email](#one-email-a-day-at-most), and `view=triage` in the [API](/api/endpoints#get-v1inbox) and [MCP](/mcp/tools#list_inbox). They cannot disagree about what comes first. The badge counts decisions, not rows.

## Needs you on Home

[Home](/start/today#needs-you) leads with the same thing, under **Needs you**: the **top three** decisions from **Waiting on you**, in the same order, each one something you can act on right there: approve or decline a change, or connect what a group of rows is waiting on. A chat paused on a question for you is listed after them. Under the three, Home says how many more are waiting, with a link to the Inbox.

The number beside **Needs you** on Home and the badge on the Inbox button in the top bar are **the same number**, read from one place, so they cannot disagree. It counts decisions, not rows, and adds each chat waiting on your answer. Act on a row on Home and both change together.

## What ends up in Waiting on you

All of these used to be visible only inside a run's transcript.


  
    The agent tried to do something irreversible or hard to undo — publishing a page, sending mail, spending with a third party — and stopped for your explicit yes. The row's page shows the tool, what it would do and whether it can be undone. **Approve once** lets the run make that one call; **Decline** takes a reason and an optional note, which the run reads before it carries on without the call. An approval is never edited; see [why](/agent/approval#approvals-are-answered-as-asked).

    An approval that goes unanswered expires after **7 days**. When it does, the run is closed without doing the thing, nothing is changed, and you get a row in the Inbox saying so. See [what the agent shows you](/agent/what-the-agent-shows).

    A run that stopped to ask you questions, rather than for permission, is answered in the run itself; the row links there.

    Whether an action stops here at all — and how much the agent will do on its own before it does — is set by your workspace's [approval mode](/agent/approval).
  

  
    The agent needs a fact only you have: a launch date, which plan to lead with, the name your customers use for a feature. You answer in the row. See [Answering a question](#answering-a-question).
  

  
    The agent tried and could not proceed: a site refused us, a credential is missing, a file it has no write access to. It files what it attempted and what it needs from you, rather than writing "I was unable to…" into a transcript nobody reads. When it knows exactly what you need to do, the row carries numbered steps and, where we can check the result, a check behind **Done**. See [Done, and the check behind it](#done-and-the-check-behind-it).
  

  
    Several courses of action are defensible and picking one is your call, not a guess the agent should make on your behalf.

    In Ask mode, a goal that falls far enough behind to re-plan becomes one of these rows: **"your goal" is behind pace: re-plan the work toward it?** The row starts nothing on its own. It is filed at most once per goal per week, and a dismissed row is not filed again that week. See [Goal re-plans](/agent/decisions#goal-re-plans).
  

  
    A tool could not run because an [integration](/agent/plugins) is not connected, was revoked, or does not have the permission the action needs. These are raised automatically from a call that actually failed — never from the agent's opinion about what it can do.

    All rows about one integration, whether it is not connected or its credential stopped working, from any run or any of its failing tools, are **one row** with an occurrence count. Connecting the integration closes it automatically.

    A scheduled run that needs an integration its automation's tool list does not include files nothing here, because connecting it would not help that run. It is noted on the run's transcript; add the tool to the automation to fix it.
  

  
    Business profile fields the agent needed and found empty share one row: **fill in your business profile**. It is a question: answer it in the row and the answer is saved to the profile field it names. It closes when a profile field is saved.
  

  
    When the credit balance is spent, or the workspace's monthly credit cap is reached, one row says scheduled work is paused and names the automations. It closes itself when the first one runs again. See [Credits](/account/credits#when-the-balance-is-spent).
  

  
    A scheduled [automation](/agent/automations) run ended in failure. A chat that fails is failing in front of you; a 3am run is not, so it comes here instead.
  

  
    One of your [webhook endpoints](/api/webhooks#when-an-endpoint-is-turned-off) kept failing, or answered `410 Gone`, so we stopped sending to it. The row says which host and why, once per endpoint. Turn the endpoint back on in **Settings → Webhooks** once your server answers again, and resend what it missed from its delivery log.
  


## Repeats and duplicates are one row

Each problem appears once. Three rules make sure of it, and none of them decides anything on your behalf.

**The same problem again.** An automation that hits the same wall every night does **not** produce a row per night. The Inbox recognises a repeat of the same problem and updates the existing row, showing how many times it has now come up. The same question asked twice is one row too.

That count is the useful part. "14 times" means the problem has been there for two weeks and nobody has acted on it — a different fact from the same row appearing for the first time this morning.

Resolving a row closes that chapter. If the same problem happens again afterwards, it comes back as a new row rather than silently reopening one you had already ticked off.

**A better version of the same change.** When the agent proposes a change to something it already has a proposal waiting on — the same file, the same passage of a page, the same article — it revises the waiting row in place rather than filing a second one. The row keeps its link, its activity log reads **Revised: this replaced the earlier version**, and the new version is what you approve. If you had the older version open, your approval is refused with **this was revised since you opened it**, so you never approve words you did not read. A proposal you already approved is left alone; the new version files beside it.

**The same thing in different words.** Decisions, questions that do not fill a profile field, and other free-form rows cannot be matched by what they name, so a run that asks "which launch date?" on Monday and "pick a day for the launch" on Wednesday would file two rows. Every 15 minutes a duplicate check compares each new row of that kind from the last 7 days with the older open rows of the same kind. When it is confident they ask the same thing, it folds the newer row into the older one: the older row counts it as a repeat, and the newer row is hidden.

The duplicate check can only hide a row. It never approves, answers, declines or closes anything, never touches a proposed change or an approval, never compares rows of different kinds, and leaves alone a row you have started to answer or that is being checked. When it is not sure, nothing is hidden.

Every fold can be undone. Open the hidden row's link and you land on the row it was folded into, with **Not the same**: press it and the hidden row is its own row again, and the two are never folded together again. The duplicate check costs you no credits.

## Acting on a row

What you can do depends on what the row is:

| Row | What you can do |
| - | - |
| An approval | **Approve once**, or **Decline** with a reason and an optional note |
| A proposed change, article or post | Approve it, [edit it then approve](#editing-before-you-approve) where the kind allows, or [decline it](#declining-with-a-reason) |
| An approved change with an open pull request | **Mark ready for review** while it is a draft, then **Merge** (see [Merging a pull request](#merging-a-pull-request)) |
| A question | [Answer it](#answering-a-question) |
| Something to do on your side | Follow the steps, then [**Done**](#done-and-the-check-behind-it) |
| Anything the agent raised | **Done** when you have handled it, **Dismiss** if it does not matter, or snooze it until later |

A snoozed row is put away until the time you picked, and comes back to **Waiting on you** on its own — nothing is lost by putting it off.

**Done** is deliberately not called "approve". Nothing is queued up to run when you press it — the work happened outside the product, and you are telling us it is handled. Declining and dismissing are both recorded rather than deleted: what you turn down is as useful a signal as what you accept.

After you act on a row on its own page, the page says what happened and offers **Next**: the row the ranking puts first now.

### Editing before you approve

An article or a LinkedIn post that is still waiting can be edited on its page before you approve it. Press **Edit**, change the words, and **Approve edited** approves your version in the same step. Your version is what publishes, and the agent's original is kept on the item. In the dashboard a [rewrite](#rewrites) has no Approve button: we never publish one, so you copy its new text into your own editor. Over the [API](/api/endpoints) or the [MCP server](/mcp/tools), an approve call can still carry your edit of a rewrite (`edited_value`); that keeps your words on the item, and it is still yours to paste.

* **An edited change is always a claim.** Your words are now what the business says, so an edited change is never treated as a safe change and never goes out on its own: it waits for a person's yes in every [approval mode](/agent/approval), and is never auto-merged.
* **Code is never changed this way.** An edit that adds or changes a fenced code block, a `
` or a `` sample is refused with the rule, and nothing is approved.
* **Your edit gets the same check as a draft.** It is checked against your workspace's rules before it is approved, the same check the agent's own draft gets.
* **An article keeps its front matter.** If the article came with front matter for your site's build and your edit dropped it, it is put back.
* An edited article or post is updated on the content plan too.

A fix to a file in your repository cannot be edited here: edit it in the pull request it opens, or decline it with a note saying what to change. An approval from a paused run cannot be edited either; see [why](/agent/approval#approvals-are-answered-as-asked).

If the agent revised the proposal after you opened it, **Approve** and **Approve edited** are refused with **this was revised since you opened it**, and the page shows the new version. Read it, then approve that.

### Declining, with a reason

Every decline carries one of four reasons:

| Reason | Means |
| - | - |
| **Wrong** | The proposal itself is bad |
| **Not now** | Right idea, wrong time |
| **Already done** | It was handled elsewhere |
| **Never touch this page** | A standing rule about the page, not a verdict on this one proposal |

You can add a note of up to 1,000 characters in your own words. With **Wrong** the note is required, because "wrong" alone does not say what to fix. The reason and the note are kept on the item and in its activity. Declining an approval from a paused run hands the run your reason and note before it carries on without the call.

### Answering a question

Open the row and type your answer, up to 2,000 characters, or pick one of the answers the agent offered. Then:

1. **The answer is saved where every later run reads it.** If the question names a [business profile](/agent/knowledge-and-skills#company) field, the answer is saved to that field, and every other row waiting on that field closes with it. Any other answer is kept as a fact you stated: every run in the workspace reads your newest stated facts, up to 40 of them. There is no screen that lists stated facts.
2. **The row closes,** marked as answered by you.
3. **The run that asked is put back to work,** depending on where it is now:

| The run that asked is… | What happens | What the row says |
| - | - | - |
| Still running | It gets your answer and uses it from its next step | The agent is back on it |
| Finished, failed or suspended | It gets your answer and starts a new turn on it, which spends credits like any turn | The agent is back on it |
| Waiting for you to approve something | It gets your answer and reads it once you answer that approval. It is not restarted behind the approval | Continues after you answer the pending approval |
| Cancelled, or there was no run | Nothing starts: somebody stopped it on purpose. You can start a run with the answer | Nothing is running that was waiting on this |

If the run could not be handed the answer, the answer is still saved and the row stays closed; the row says so and offers **Retry**.

If two people answer at once, the first answer counts and the second is told the row was already answered. If saving fails, nothing changes and you can answer again.

### Done, and the check behind it

A row to do on your side lists numbered steps, with a copy button beside each value you need to paste: a DNS record, a snippet, a line of text. Some rows also carry a check, because there is something we can look at to see it was done:

| Check | What we look at |
| - | - |
| A DNS record | The TXT records on your domain, for the value in the steps |
| A connection | Whether the named integration is connected to this workspace |
| A line on a page | Whether a page on your site shows the text from the steps |

On such a row the button reads **Done, verify it**. Pressing it does not close the row on your word, and you do not wait on a spinner either: the row moves to **Running** as **Checking…**, and the check runs within a few minutes.

* **It passes:** the row closes on its own, saying what was verified.
* **It fails:** DNS and caches often lag a change by minutes, so we try again three more times: after 10 minutes, then 20 minutes after that, then 30 minutes after that, about an hour in all. A check that points off your domain is not retried.
* **It still fails after that:** the row comes back to **Waiting on you** with the reason in plain words, such as "There is no TXT record on example.com yet", and a **Check again** button.

A check only ever looks: a DNS lookup, a read of your connections, a read of one public page. It only looks at your own domain and its subdomains, and a page that redirects off your site does not count. A row cannot be snoozed while its check is running. A row with no check closes when you press **Done**, as it always has.

### The keyboard

The list is keyboard first. Keys are ignored while you are typing in a field, and whenever ⌘, Ctrl or Alt is held.

| Key | Does |
| - | - |
| `j` or `↓` | Next row |
| `k` or `↑` | Previous row |
| `Enter` | Open the row |
| `a` | Approve, where the list can |
| `e` | Open the row to edit it |
| `d` | Open the row to decline it |
| `?` | Show or hide the keys |
| `Esc` | Close the key list |

`a` approves from the list only a single proposed change whose effect can be undone and that is classed safe. Anything else — a claim, a change that cannot be undone, spend, an approval from a paused run, a question, a decision with several rows behind it — says **Open it to approve**, because a yes to something nobody read is not a yes.

### On a phone

On a narrow screen the row's page keeps its main action pinned to the bottom of the screen, so approving is two taps: the row, then **Approve**. The action is reachable without scrolling past a long article or diff, long lines wrap instead of scrolling sideways, and every button is at least 44 pixels tall on a touch screen.

## One email a day, at most

If something has been waiting on you, unseen, for 24 hours, we email you about it. It is **one email per workspace per day**, however many rows are waiting: it lists the top three decisions, in the same order as the Inbox, and how many more are behind them. A row being checked, or one folded into another as a duplicate, is not waiting on you and does not count. These emails are the **Attention** category of your [notifications](/account/notifications).

## The pull request chip

A row whose work ended in a [GitHub pull request](/agent/github) carries a **PR chip**: the pull request number, linked, plus its current state. The chip is how you tell "the agent opened something" from "the agent opened something and it is stuck".

| Chip | What it means |
| - | - |
| **Open** | The pull request is waiting for review. Checks have not failed |
| **Open · checks failing** | Your CI went red on our branch. It is yours to fix — we do not push again |
| **Merged** | It landed. The finding is resolved |
| **Closed** | It was closed without merging. The work item is declined ("closed without merging") and not opened again; reopening the pull request brings it back |

The state comes from GitHub, not from us guessing: it updates as the pull request is reviewed, merged or closed. A merge conflict shows the same way a red build does — surfaced on the row, and left for you.


  A failing chip is not a task we are working on. Attensira never pushes a second time to a branch it opened, so nothing changes on that pull request until a person acts on it.


A change that is approved but held by your weekly [change cap](/agent/approval#change-caps) shows as **Approved, held by your weekly limit**.

### Merging a pull request

We merge only when you press **Merge** here, or when you switched on [auto-merge for safe changes](/agent/approval#auto-merge-for-safe-changes). Approving a change opens its pull request and never merges it.

Open an approved item whose pull request is still open and the pull request is shown in full, read live from GitHub: its files and diff, its checks, a deploy preview when there is one, and why it cannot merge yet when it cannot. Then:

1. **Mark ready for review**, while it is a draft. It takes the pull request out of draft and does not merge it. If your repository merges ready pull requests on its own, that automation may merge it from here.
2. **Merge**, once GitHub says it can merge. It squash-merges at the commit on screen.

Both are pinned to the commit you were looking at: if the branch moved since, the click is refused and the view reloads so you review what changed. Once it is out of draft, a conflict, a missing review or a failing check keeps **Merge** disabled with the reason beside it. The chip turns **Merged** when GitHub reports the merge, and the re-reads start then. Merging is dashboard only; the API and MCP cannot merge. The full rules are on the [GitHub page](/agent/github#merging-from-your-inbox).

## Rewrites

A rewrite, shown as **new wording** in the app, is a fix for a page we cannot open a pull request against, because no repository is connected or because that page does not come from one. It is one passage of your live page, quoted exactly as published, beside a better version of it, with the reason and the buying questions it is meant to move.

**We never publish a rewrite.** There is nothing to approve, so a rewrite row has no Approve button. What it has instead:

* **Copy the new text** and paste it into your CMS yourself, then **Mark shipped** with the URL (see [Mark shipped](#mark-shipped)).
* **Share a link**: a private, signed link that expires in 30 days, for forwarding to whoever owns the site. They need no account. The page carries your org's [branding](/agencies/branding). Rotating your workspace's share secret revokes every link at once.
* **Request access from my developer**: see [GitHub](/agent/github#if-you-cannot-install-it-yourself).

The before side is always the page as we actually read it. If the agent quotes a sentence that is not on the page, the rewrite is refused rather than filed. Rewrites carry the same change class as pull requests: one classed **claim** (it touches a price, a number with a unit, a promise, or a competitor's name) says so on the row and on the shared link.

**If a rewrite sits for five days** with nobody copying it and no access request sent, it comes back in **Waiting on you** and in your morning brief. That row goes away the moment you act on it or ask your developer.

## Written for you

Articles and LinkedIn posts the agent writes appear under **Waiting on you**, in **Written for you**, rendered as a document you can read in place. What you can do depends on where the piece goes:

| Action | When it is offered | What it does |
| - | - | - |
| **Copy** | Always | Copies the piece, including any edit you made on the page |
| **Open PR** | A connected repository has a blog directory | Adds the post as a new file in a pull request, with front matter that follows your existing posts. Nothing goes live until you merge it |
| **Send to Notion** | A [Notion blog](/agent/notion) is mapped | Creates a draft page. It is published only if the workspace allows the agent to act |
| **Mark shipped** | Nothing of ours publishes it: a copy-target article, or a LinkedIn post | You give the live URL |

In the list, **Edit** is offered only on a piece you publish yourself (a copy target or a LinkedIn post): the edit stays on the page and **Copy** takes your version. To change the words of a piece bound for a repository or Notion before it goes, open it and use [Edit then **Approve edited**](#editing-before-you-approve), which saves your version on the item. For an article bound for a repository, the front matter fields show as one line above the body.

Articles always wait for a person's yes, in every mode. Revising an article that is still waiting updates the same item. Briefs, emails and notes the agent writes are not filed here. Articles also appear as drafts on the content plan.

**LinkedIn posts** always carry the link to their source page. Attensira never posts to LinkedIn: you post it from your own account and mark it shipped with the post's link.

## Mark shipped

**Mark shipped** is for copy handoffs, where you publish the piece yourself: articles with a copy target, LinkedIn posts, and new wording for an existing page when no repository is connected (next to **Copy the new text** and **Share a link**). It is also how you tell us an article we created as a draft in your [CMS](/agent/cms#new-articles) went live, once you published it there.

* You give the URL where it went live, and the item leaves **Waiting on you**. It is not yet listed in **Done**, which today shows what the agent shipped from its action log; the re-reads below still run.
* The URL is recorded as given. It is not checked until the first re-read, which confirms the change is on that page before measuring anything.
* Marking shipped starts the [before-and-after re-reads](/measure/before-and-after) for the tracked prompts attached to the item, unless a read of your page already saw the change and booked them. See [Page changes](/measure/page-changes).

Approving a copy item does not publish anything. The item comes back with an instruction to paste it and mark it shipped.

## Shipped

The **Done** view lists merged pull requests, placed pitches and delivered changes. Each shows its re-read result in plain words: **Moved** (named more often on X of N prompt and engine pairs, across M answers), **Fell**, **No change**, **Too close to call**, **Too few answers** (not enough data from before the change), **Not live**, or **Re-read booked** with its date. Every result is a single dated reading, never a day-over-day comparison. See [Before and after](/measure/before-and-after) for what each means.

## What the agent can and cannot put here

The agent can raise three things itself: something it is **blocked** on, a **decision** that is yours, and a **question** only you can answer. Each requires it to say what it actually tried. A row it raises can carry at most eight steps, and a check only against your own domain or an integration we offer; anything else is refused and the agent is asked to try again, with nothing filed.

It cannot file the other kinds. A missing connection, a failed run and an expired approval are raised from evidence — a call that really failed, a run that really died, a deadline that really passed — so that everything on this list is a fact rather than a guess.

An hourly pass also files decisions on its own, grouped by the source that caused them — see [What the agent notices on its own](/agent/findings). A change too small to tell from sampling noise is held back rather than filed, and checked again an hour later; see [Noise, or a real change](/agent/findings#noise-or-a-real-change).


  Raising something in your Inbox costs no credits and changes nothing in your workspace. The agent is never discouraged from telling you it is stuck.


## The Inbox is not only a screen

Everything above is available through the [MCP server](/mcp/tools#inbox) and the [REST API](/api/endpoints#inbox) as well, so "what needs me right now" is answerable from wherever you already work, not only from the dashboard. Both read the same ranking (`view=triage`), read any row by its ref, answer questions, approve with an edit, decline with a note, and approve or decline a paused run's approval. Every act returns `next_item_ref`, the row the ranking puts first now.

An assistant acting for you should approve, answer or decline only on your instruction, never on its own reading of the row. Two things are not in the MCP tools or the REST API yet: **Not the same** on a folded duplicate, and **Retry** when a run could not be handed your answer. Use the row's page for those.


# Knowledge and skills
Source: https://docs.attensira.com/agent/knowledge-and-skills

Give the agents the facts about your company, reusable skills, and the guardrails every draft is checked against before anyone can approve it.

The business profile is on every plan, and the agent reads and uses it on every plan. Writing your own skills (rules) is on Growth and above.

All of it lives under **Customize** in the sidebar, one pill each:

* **Company**: the facts every draft is written from, your brand and values, and the competitors you track.
* **Skills**: your playbooks, and the skills built into every agent.
* **Guardrails**: your compliance rules, how the agents write, and the words a draft may never use.

The old **Knowledge** address, `/knowledge`, opens Customize → Company. The other two pills, Connectors and Agents, are described on [Connectors](/agent/plugins).

## What does this change?

Knowledge and Skills changes how the agent writes, not what it measures. A brand profile tells it who you are, what you sell, and how you talk. Skills give it reusable instructions to apply every time. Your Share of voice, Citation rate, and Source share are untouched by anything on this page.

Without it, the agent still answers correctly — it just answers generically, and you end up pasting the same context into every [chat](/agent/chats) and every [automation](/agent/automations).

## Company

**Customize → Company** holds the brand profile. The brand profile is the standing context the agent carries into every conversation in the workspace. Put in it the things you would otherwise repeat: what the product does, who buys it, the words you use for your own categories, the competitors you care about, and the claims you are not willing to make.

Be specific and be short. Vague positioning statements make output vaguer. Concrete constraints — "never call it a platform", "always distinguish crawlers from models", "prices are in USD, monthly" — do visible work on every answer that follows.


  Write the profile as rules the agent can check its own draft against, not as marketing copy. "Avoid X" is actionable; "we are innovative" is not.


### Drafted for you at setup

When a workspace is set up, Attensira reads your homepage, plus `/about` and `/pricing` when they exist. It drafts only the profile fields those pages actually state, and each draft names the page it came from. Drafts show as **drafted** until you confirm them. A field that already holds a value is never overwritten.

When the agent needs a field that is empty, it raises one row in the [Inbox](/agent/inbox), **fill in your business profile**, shared by every empty field. It is a question: answer it in the row and the answer is saved to the field it names. The row closes when a profile field is saved.

Answers you give to the agent's other questions in the Inbox are kept too, as facts you stated, and every run reads your newest ones, up to 40. There is no screen that lists them.

### Brand and values

Two groups of the profile say who you are rather than what you sell:

* **Brand**: your name as said aloud, your tagline, your colours and your logo URL. Scripts and voice answers use the spoken name, listings reuse the tagline and logo, and images and charts use your colours.
* **Values**: what you stand for, as a list. Drafts are written so they do not contradict it.

Both are optional, so a workspace that leaves them empty keeps its profile coverage. Tone is not a brand field: how the agents write is set by your voice rules under [Guardrails](#guardrails), and the Brand group links there. The name, logo and colour a client sees on a report or page you share are a separate setting, under **Settings → Branding**.

### Competitors

The competitors you track are listed on the Company pill too, and you can add or remove them there. Each tracked competitor adds an **Against *rival*** field to the profile: why a buyer would choose you over them, used on every comparison and alternatives page about them. A competitor you add is measured from the next reading on; see [Competitors](/tracking/competitors).

### Reporting standard

The profile's **Reporting** group has one free-text field, **Reporting standard**: the tone of your reports, who reads them, and words to avoid. Every [client report](/agencies/client-reports) follows it and stores a copy of it. It never overrides the report rules: no percentages, and no movement that a re-read did not prove.

## Skills

**Customize → Skills** lists two kinds: **your playbooks**, which you write, and **Built in**, the skills we maintain. Every agent has the built-in ones; they read your company facts and follow your guardrails. You cannot edit a built-in skill. The list in the app is always the current set.

A skill is a named, reusable instruction for a recurring job: a weekly summary format, the structure of a competitor brief, the tone for a customer-facing note. Instead of restating the format each time, you point the agent at the skill.

Skills compose with the brand profile. The profile decides voice and boundaries; the skill decides structure and steps for one kind of output. Keep each skill narrow — one job, one shape. A skill that tries to cover every report produces output that fits none of them.


  
    The second time you retype an instruction is the signal it should be a skill.
  

  
    Sections, order, length, what to do when a number is not measured.
  

  
    Check the shape on real workspace data before you attach it to a cadence.
  

  
    Once the shape holds, a recurring run produces comparable output week over week.
  


## Guardrails

Guardrails are your rules for everything the agents write in your name. Every draft, whether an article, a page fix, a social post, an outreach email or a community reply, is checked against them before anyone can approve it. The agents do the checking and the rewriting. You write a rule once, and every draft after it is held to it.

**Customize → Guardrails** has three sections, hardest first:

| Section | What a rule there does to a draft that breaks it |
| - | - |
| **Compliance** | What may never be said in your name. A draft that clearly breaks one is **stopped**. One that may break it goes to **review**. |
| **How agents write** | Your voice rules. A voice rule never stops a draft. One that may not follow it goes to **review**. |
| **Words to use and avoid** | Words and phrases a draft may never contain, as chips on a rule. Any one of them **stops** a draft, wherever it appears. |

Words to avoid are matched exactly, ignoring case, and on whole words: avoiding "cheap" does not stop "cheaper", and avoiding "HIPAA certified" stops "hipaa certified". They are listed explicitly, never guessed from a rule's text, so a sentence like *say "practice", not "clinic"* never stops a draft for saying "practice". Write that sentence as the rule, and put "clinic" in its avoid chips.

### When and where a rule applies

Every rule shows **When: … · Where: …** under its text.

* **When** is the rule's one-line description: the situation it is about.
* **Where** is the kinds of draft it holds: pages, articles, emails, community replies, social posts or ads. A rule applies to every kind unless you narrow it. A rule about email sign-offs, set to emails only, never holds back a page fix.

Only rules that are switched on are checked. Switching one off keeps it, so you can switch it back on later. Editing a rule is recorded as a new version, and every rule a verdict names carries the exact version the draft was checked against.

### The verdict on each draft

Each draft gets one verdict, for the exact text it was checked against:

| Verdict | What it means | What happens next |
| - | - | - |
| **Passed** | Every rule that applies was checked, and none is broken. | It waits for approval as it would have before. The check never removes a step. |
| **Review** | A rule may be broken. | Read it before you approve it, or ask for a revision. |
| **Stopped** | A compliance rule is broken, or the draft uses a word a rule forbids. | It cannot be approved as written, by anyone. Revise it first. |
| **Not checked** | The check could not finish. | It cannot be approved, published or sent until a check finishes. |

The verdict also counts the rules the draft was checked against: your rules that apply to it plus our own house safety rules, which every draft is checked against on every plan. A review or stopped verdict names each rule involved, and for a forbidden word, the word.

When the agent writes a draft, it is told the verdict straight away and revises once if it can keep the rule without losing the point. An article that is stopped is not filed for approval at all; if the revision is stopped too, the agent asks you in your [Inbox](/agent/inbox) what the article should say instead.

### A draft nobody could check is never passed

If the check cannot finish, because your rules could not be read or the check did not answer for every rule, the draft is **not checked**, never passed. It cannot be approved, published or sent until a check finishes. One row in your Inbox, **We could not check N drafts against your rules**, says so for the whole workspace, with a **Retry** button that opens the draft. A draft whose text changed after it was checked is checked again before it can be approved or sent. One that changed after it was approved is not published: it has to be checked and approved again.

A stopped or unchecked draft never goes ahead on its own, whatever the workspace's [approval mode](/agent/approval). A draft marked for review does not go out on the workspace's own say-so either: it waits for your approval instead.

### Unblocking a draft

There is no override button. A stopped draft moves only when it no longer breaks a rule:

* **Revise the draft.** Edit it yourself, or ask the agent to rewrite it. An edited draft is checked again as soon as it is saved, and a clean one can be approved.
* **Change the rule.** If the rule is wrong, edit it, narrow where it applies, remove the word from its avoid chips, or switch it off. Every draft checked under the old rules is checked again when you approve it.
* **Retry a draft that was not checked.** **Retry** on the Inbox row opens the draft. Approve it and the check runs once more before the approval goes through.

Your assistant gets the same answer. Approving a stopped draft over [MCP](/mcp/overview) or the REST API is refused with `409 Conflict` and a message naming the rules it breaks. Over the REST API the error's `details.reason` is `guardrail_stopped`, or `guardrail_unchecked` when the draft could not be checked, and `details.guardrails` is the whole verdict. A work item read over the API carries the same verdict as its `guardrails` field: `status` is `passed`, `review`, `stopped` or `unchecked`, and `checked` is how many rules it was checked against.

### On every plan

Writing rules is part of Knowledge and Skills. Checking drafts against them is not: every rule you have switched on, and our house rules, are checked on every plan. A workspace that moves to a plan without custom skills keeps the rules it wrote, and every draft is still held to them.

## What it will not do

Knowledge and Skills cannot make the agent assert something the data does not support. A brand profile that says "we lead the category" does not turn an unmeasured cell into a number, and a skill that asks for a share-of-voice ranking cannot produce one — Attensira measures mention and citation rates against a shared denominator, so brand rates do not sum to 1 and there is no brand rank metric to report.

It also does not change sampling. Depth stays at n=1 on Starter and n=3 on Growth, Business and Agency, and each Prompt × Model × country is still measured at most once per workspace-local calendar day.


  If a skill asks for a metric that does not exist, you get an honest gap in the output rather than an invented figure. Write skills around Share of voice, Citation rate, Source share, and average citation position.


## Knowledge you write vs. what the agent already knows

The brand profile and skills on this page are yours to write. Separately, and on every plan, the agent keeps its own nightly notes about your workspace — see [What the agent remembers](/agent/what-we-know).

## On Starter

On Starter you have the business profile, and the agent reads and follows it and the rules in every chat and automation. Every draft is still checked against your guardrails and our house rules. Writing custom skills shows an upgrade state — a screen that names the plan requirement and does nothing else. Nothing you configure elsewhere is lost by being on Starter.

Plans are Starter $129, Growth $349, Business $699 and Agency $2,000 per month, with yearly billed at ten months' price. Crawler logs are also Growth-and-above. See [Plans](/account/plans).

## House skills

Besides the skills you write, the agent follows Attensira's own procedures for recurring jobs, such as `orchestrate/daily-win-plan` for the [Daily win plan](/agent/autopilot) and `report/client-weekly`, `report/client-monthly` and `report/client-quarterly` for [client reports](/agencies/client-reports). See [The agent's tools](/agent/agent-tools#house-skills).


# Loop health
Source: https://docs.attensira.com/agent/loop-health

Whether the autopilot loop is working, idle or blocked, and why: readings, automation runs and skips, findings, work produced and re-reads due, per day.

## What it answers

"Why has nothing happened?" Loop health answers that from records rather than guesses. For each workspace and each day it shows:

* **Readings per engine**: how many succeeded and how many failed.
* **Scheduled automation outcomes**: each automation that fired, was skipped (with the reason), or failed.
* **Detection findings** raised by the hourly [detection](/agent/findings) pass. A finding [held as noise](/agent/findings#noise-or-a-real-change) is not counted.
* **Work produced**: items filed for your approval or review.
* **Approvals pending**: how many items wait on a person.
* **Pull requests opened**.
* **Re-reads due**: [before-and-after](/measure/before-and-after) re-reads scheduled for that day.

Each count is read once for its date. The days are listed side by side, but they are not a trend and nothing in the report compares one day with another.

In the dashboard, [Home](/start/today#home) shows it only when the loop is **Blocked**: one status line with the first reason and **Fix it**, which opens the Inbox. The full report is `get_loop_health` over [MCP](/mcp/tools#get_loop_health) and `GET /v1/loop-health` over the [REST API](/api/endpoints#get-v1loop-health).

## The three statuses

| Status | Meaning |
| - | - |
| **Working** | Work was produced, or a pull request opened, in the last two days |
| **Idle** | Nothing is blocked, and nothing was produced. A quiet loop, not a broken one |
| **Blocked** | Something stops the loop. The reasons say which |

A workspace is **blocked** when any of these is true:

* the credit balance is spent;
* no reading succeeded in two days;
* nothing is scheduled and detection is off;
* every scheduled run failed;
* the scheduler stopped.

Every status comes with its reasons as sentences you can act on, such as "The credit balance is spent, so scheduled automation runs are refused. Top up to restart the loop." or "'Daily win plan' is paused." Reasons also carry notes that do not block the loop, such as how many pieces of work wait for your approval.


  The org home at [/clients](/agencies/clients) currently shows a simpler status per workspace, derived from its automations: **blocked** when there is no scheduled automation, all of them are paused, or credits are used up; **idle** when an automation is scheduled but has never run, or has not run in 48 hours. It will read loop health instead once that is wired in.


## Why a scheduled run was skipped

A skipped run is recorded, grouped with a count, instead of disappearing. Each skip carries one of these reasons:

| Reason | What it means |
| - | - |
| `no_credits` | The credit balance was spent when the run was due. See [Credits](/account/credits#when-the-balance-is-spent) |
| `manual_trigger` | The automation only runs when started by hand (**When I ask**), so nothing was scheduled |
| `parked_on_ask` | The previous run is waiting on an answer from you; the scheduled fire handed that run back instead of starting another |
| `flag_disabled` | [Detection](/agent/findings) is off for this workspace, so a lost citation or a competitor change does not start work on its own |
| `retired` | The product paused the automation on its own (see `retired_reason` on the automation). Resume it to put it back on the schedule |
| `paused` | Somebody paused it |
| `cap_reached` | A [change cap](/agent/approval#change-caps) was reached; the changes over it wait for you to approve them by hand |
| `in_flight` | A run of the same automation was still in progress, so a second was not started |
| `not_worth_run` | Every event in the batch was a competitor's change that a [decision](/agent/decisions#worth-a-run) judged not worth a run of this event automation, so nothing started |

[Markets](/tracking/markets) add two lines of their own, named after the market or language rather than an automation. The first is recorded as a skipped run, the second as a failed one:

| Reason | What it means |
| - | - |
| `market_allowance_reached` | **Readings in DE** were skipped: this month's response allowance is held for your primary market, which keeps reading. They resume when the allowance resets |
| `translation_failed` | **Translations into de** could not be made, so readings in that market language wait and are retried on the next pass. They are never asked in the original language instead |

## Credits stop runs, not readings

A spent credit balance stops **scheduled automation runs**. It does not stop **readings**: the daily answers to your tracked prompts run on your plan's monthly response budget, not on credits. A blocked workspace with no credits still gets its numbers; what stops is the work built on them.

Loop health itself costs nothing and answers on a spent balance.

## Over the API

```bash Shell theme={null}
curl "https://api.attensira.com/v1/loop-health?days=7" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "status": "blocked",
  "reasons": ["The credit balance is spent, so scheduled automation runs are refused. Top up to restart the loop. (Readings run on the plan's response budget, not credits.)"],
  "days": [
    {
      "date": "2026-09-22",
      "readings": { "chatgpt": { "ok": 40, "failed": 1 }, "perplexity": { "ok": 41, "failed": 0 } },
      "runs": [
        { "automation_id": "aut_71cd", "name": "Daily win plan", "status": "skipped", "reason": "no_credits", "count": 1, "at": "2026-09-22T06:00:00Z" }
      ],
      "findings": 2,
      "work_produced": 0,
      "approvals_pending": 3,
      "prs_opened": 0,
      "rereads_due": 1
    }
  ]
}
```

`days` takes 1 to 30 and defaults to 7. The dashboard reads the same data from `GET /v2/projects/{id}/loop-health`.


# The morning brief
Source: https://docs.attensira.com/agent/morning-brief

One email a day at 07:00 in your own time zone, and on Mondays a longer note: what shipped, the early signals on the pages it changed, citations, goals, and what the agents do next.

## When it arrives

The brief sends at **07:00 in your workspace's own time zone** — not ours, and not UTC. A customer in Auckland and a customer in Denver both get a brief that says "this morning" and means it.

Six mornings a week it is the short daily edition. On **Monday**, again in your own time zone, it is the [Monday note](#the-monday-note): the week's work and what is showing up from it.

## The daily edition, in order


  
    The newest finished piece of work since the last brief: a page rewritten, a pull request opened, an outreach draft filed. If there is nothing to lead with, this section is absent — the brief does not manufacture a highlight.
  

  
    Up to **three** decisions waiting on you: the Inbox's top three, in the same order. The same problem is never listed twice. This is the same list as [Inbox → Waiting on you](/agent/inbox#at-most-three-decisions-first); the brief is a pointer to it, not a second copy of it.
  

  
    Counts, with the sample size behind each one — never a percentage on its own, and never a day-over-day comparison. One day is too small a window to compare honestly; see [How we collect data](/measure/how-we-collect-data) for why.
  

  
    Shown when [re-reads](/measure/before-and-after) are scheduled: at most **five** prompts, soonest first. The date shown is the day the re-read is actually read, a few days after its +7, +14 or +30 day mark, once its window has fully happened.
  

  
    Shown when a change on one of your pages was first seen, or had its before-and-after decided, the day before (the week before, on Monday): at most **three**, moved first. Each is the [page change](/measure/page-changes) card's own two sentences, what changed and what was measured, never joined by a "because". These lines alone never send a brief on an otherwise quiet day.
  


The footer reads **"no proven change yet"** until a re-read has come back **moved**. That line is not boilerplate: the footer and any movement wording unlock only after a re-read clears the bar, the same significance test every delta in the product has to pass.

The daily edition is email only. It is never posted to Slack, whatever the Monday note's settings say.

## Quiet days send nothing

If there is no artifact, nothing needs you, and nothing has changed, no daily email goes out. A quiet day is not a failure to report on; it is the absence of anything worth interrupting your morning for.

## The Monday note

The Monday note answers a different question from the daily brief: what did the agents do for you this week, and is any of it showing up yet? It covers the seven days before Monday in your time zone, and every number in it carries its own dates.

The subject line leads with the work: **"4 shipped this week · OpenAI's crawlers fetched /vs/dentrix 41 times"**, or **"Nothing shipped this week · …"** when nothing did.

### What it says, in order

Shipped work and the early signals come first, because they are what can honestly be shown in a workspace's first weeks. Citations and goal pace take longer, so they come after.


  
    One line on your goals: how many are ahead of pace, on pace, behind, or not measured yet. A workspace with no goals reads "No goals are set for this workspace yet."
  

  
    How many pieces of work were delivered this week, by kind ("We shipped 4 pieces of work this week: 2 page changes, 1 pitch and 1 pull request."), then the newest **three**, each with its link and, for work that went live on a page, the day it did. Each piece of work is counted once, however many steps it took.

    A week with nothing delivered says **"Nothing shipped this week."** in those words, followed by how many items are waiting on you when any are. If the delivery record could not be read that morning, the section is left out rather than printed as a zero.
  

  
    The pages that delivered work changed and that went live in the last seven days. An open pull request, or a rewrite nobody has published yet, is not a changed page.

    For up to **three** of them, most fetched first, how often AI crawlers fetched the page after it went live: "OpenAI's crawlers fetched /vs/dentrix 41 times between 24 and 28 September. The page went live on 24 September." Only verified crawlers count; a request that only claims to be one is left out. The two facts sit side by side, and the note never says the change caused the fetches.

    Then Search Console's impressions and clicks on up to **ten** of those pages, with the days they cover and a note that Search Console trails today by two days.

    When something cannot be counted, the note says why instead of printing a zero: the [crawler snippet](/traffic/ai-traffic) is not installed, crawler data could not be read that morning, Search Console is not connected, Search Console did not answer that morning or has not been asked about the changed pages yet, or the pages went live inside its two-day lag. A week in which no changed page went live says so, because there is then no page to count anything for. If neither source could be read at all, the section says "Crawler and Search Console data was unavailable this morning." and the note still sends.
  

  
    Up to **three** changes the crawl saw on your own pages during the week, each with what it matched and what was measured. Shown only when there are any.
  

  
    How many answers named or linked you for the first time this week, with up to **three** of them: the engine, the day, and the prompt it was answering. Then the [re-reads](/measure/before-and-after) that came back this week, as counts with their n: in how many prompt and engine pairs the difference cleared the sampling noise.
  

  
    Each goal's pace, in the goal's own words when they pass the same honesty rules as the rest of the note, and in plain words ("on pace", "behind pace") when they do not.
  

  
    While your workspace is in its first eight weeks, and always in its first Monday note, the note says which week you are in and what to expect. See [What to expect, week by week](#what-to-expect-week-by-week).
  

  
    Up to **three** pieces of work you approved that the agents have not shipped yet, oldest approval first. "No approved work is waiting to start." when there is none. Then what we read again, and when.
  

  
    Up to **three** things waiting on you, the same list as the daily edition.
  

  
    One observation, stated as a count with its sample size — never a bare percentage. One verbatim answer quote from a real model response, word for word, never paraphrased.
  


The note ends with a link to that Monday's note in the app, and the same footer as the daily edition.

A section whose data source is not available on your workspace is left out. A section whose source was read and found nothing says so in words.

### What to expect, week by week

The note, the app when you sign up, and this page show the same four steps:

| Week | Signal | What happens |
| - | - | - |
| 1 | Shipped work | The agents ship the first changes, and this note lists each one with a link. |
| 2 | Crawler fetches | We count how often AI crawlers fetch each changed page after it goes live. |
| 4 | Impressions | Search Console starts to report impressions for the changed pages, about two days behind. |
| 8 | Citations and goals | We report whether answers name or link you for the first time, and how each goal is pacing. |

The steps describe what we measure and roughly when it can first appear. They are not a promise about what the engines do with the work: we guarantee the work itself.

### It always sends

The Monday note sends even on a quiet week. A week we found nothing more than "Nothing shipped this week" is itself worth saying out loud; suppressing it would make Attensira look broken instead of quiet.

## Getting the Monday note in Slack

The Monday note can go to email, to a Slack channel, or to both. Email is the default.

1. Connect Slack under **Customize → Connectors**.
2. Open **Settings → Notifications** and, under **Monday note**, pick the **Slack channel** to post in. It is one channel, shared with the win message under **Wins**.
3. Turn on **Slack**.

Slack cannot be turned on until Slack is connected and a channel is picked; settings refuse it rather than save a choice that would fail on Monday. At least one of email or Slack stays on: "nowhere" is what unsubscribing means, so to stop the note, set it back to email and [unsubscribe](#turning-it-off).

The Slack post is the same note as the email, posted by Attensira through your workspace's Slack connection. The agents themselves cannot post in your Slack.

If a post fails because of something on your side — the Slack connection was removed or needs reconnecting, the channel was archived, or the Attensira app was removed from it — you get **one** item in your Inbox: **"Reconnect Slack to keep getting the Monday note."** A post that fails for any other reason is tried again, and raises the same item only if the last try fails too. However often it happens, it stays one item, and it closes on its own once you reconnect Slack or the next post goes through. The email copy is separate and still goes.

## The same brief, wherever you read it

The email, the Slack post, the app, and your own tooling all get the same brief, composed from the same data each time rather than separate renderings that can drift apart. Pull it yourself with the `get_brief` [MCP tool](/mcp/tools#get_brief), or over REST with `GET /v1/brief`. Both take an optional `date` (`YYYY-MM-DD`) for one of your workspace's past days.

On a Monday the response also carries `order`: the note's section keys in the order it prints them.

A past Monday's note shows the crawler and Search Console numbers stored when it was sent that morning, not this week's. If none were stored that morning, the early-signals section is left out rather than filled with this week's numbers.

## Who gets it

Paid workspaces are enrolled in the morning brief automatically, the first time their subscription is active.

## Turning it off

The morning brief is its own [notification category](/account/notifications) and unsubscribes independently of Attention, Runs, Credits and the rest — turning it off does not touch anything else you get emailed about.

Unsubscribing stops the email, daily and Monday. It does not stop a Monday note you chose to post to Slack; turn Slack off under **Settings → Notifications → Monday note** for that.


# Notion blog
Source: https://docs.attensira.com/agent/notion

Connect a Notion database as your blog: approved articles arrive as draft rows, updates to existing posts wait for your yes, and nothing is ever deleted.

If your blog lives in a Notion database, Attensira can deliver articles into it and propose updates to posts that are already there.

## Connecting


  
    Connect Notion from the workspace's integrations and grant access to the page that holds your blog database.
  

  
    Choose the database your posts live in.
  

  
    Tell Attensira which property holds what:

    | Field | Required | What to map |
    | - | - | - |
    | Title | Yes | The title property |
    | Slug | No | A text property holding the post's slug |
    | Status | No | A status, select or checkbox property, plus its **draft** value and its **published** value |
    | Body | No | Empty means the page content. A text property is limited to 2,000 characters |
    | Published date | No | A date property |

    The mapping is checked against the database's columns when you save it, so a property that does not exist or has the wrong type is refused then, not at the first delivery.
  


## How an article is delivered

When the agent writes an article that targets Notion, it waits in [Work](/agent/inbox#written-for-you) for your approval like every article does. Approving it (**Send to Notion**) creates a **new row** in the database, in its draft state.

* **Ask or draft mode:** the row stays a draft for your team to publish in Notion.
* **Act mode:** the row is set to published, and its published date to today, **only if** the mapping has a published value. Without one it stays a draft.

A post whose title already exists in the database is not duplicated. The item comes back to you with a link to the existing post.

## How updates work

The agent can list and read the posts in your mapped blog, and propose an update to one as a before/after in your [Inbox](/agent/inbox).

* Updates **always wait for your approval**, in every mode, act included.
* Approving replaces that post's content in Notion. The old blocks are archived, not deleted, so Notion's own history still has them. This is the one exception to the [Connectors](/agent/plugins) rule that nothing of yours is archived, and it happens only on your yes.
* An update never changes the post's status.
* We never delete a Notion post.

The agent tools behind this, `list_blog_posts`, `read_blog_post` and `propose_blog_update`, are offered only when a Notion blog database is mapped. See [The agent's tools](/agent/agent-tools).

## After it ships

Publishing through Notion starts the [before-and-after re-reads](/measure/before-and-after) for the tracked prompts attached to the article, the same as a merged pull request.

## Settings

The mapping is stored in project settings as `notion_blog`, with `database_id`, `database_title`, `title_property`, `slug_property`, `status_property`, `status_kind`, `draft_value`, `published_value`, `body_property` and `published_date_property`. Changes show in the settings log as `notion blog: X → Y`.


# Off-site work
Source: https://docs.attensira.com/agent/off-site

Most of what decides an AI answer is not on your site. Here is what the agent does about the pages, lists and repositories that are — pitches, entries in other people's repositories, and original pages worth citing.

## Why off-site at all

When an answer engine names a product, it is usually quoting somebody else's page — a roundup, a comparison, a README, a docs page. Fixing your own site matters and is table stakes. The rest of the work is on the sources that decide the answer, and none of those are yours.

The agent works those sources in one order, and it never skips a rung to reach a lower one:


  
    A source an engine **already cites** for your questions, that names a competitor and not you. The engine has decided it trusts this page; the only missing thing is you on it.
  

  
    A source that outranks where you are today but has not been read citing you yet.
  

  
    Nothing worth joining exists. Then the move is to publish the page that would be cited — see [Original pages](#original-pages-worth-citing) below.
  


## The rules that bind every off-site channel


  These hold on every channel — email, directories, repositories — and they are enforced in code, not by instruction.

  * **Nothing is ever sent from an Attensira domain on your behalf.** Mail goes from your own connected Gmail or Outlook, or it does not go.
  * **The first sends on any channel are yours.** Until ten pitches have gone out from your mailbox by hand, no pitch sends on its own; until you have approved one directory listing, no listing is filed on its own. Approving one channel does not unlock another, and one workspace never unlocks another.
  * **A daily cap per channel, per workspace.** Two pitches a day, three directory listings a day. It applies to you as well as to automations, and rolls over at midnight UTC.
  * **A listing always waits for you.** A directory listing publishes claims about your product under your name, and claims are never published unattended in any mode, including act mode.
  * **Community posting is never unattended.** Reddit, forums and Q\&A answers are drafted for you to post, always. See [Community answers](/agent/community-answers).
  * **Replies land in your mailbox**, because that is where the conversation is. We never answer one for you.


## Directory listings

When the deciding-sources map shows that a directory decided answers to your tracked questions, Attensira prefills that site's submission form from your brand profile — name, tagline, category, pricing, logo, website — attaches the citation counts that make the case, and puts it in your Inbox. It knows the forms for G2, Capterra, GetApp, Product Hunt, AlternativeTo, Clutch and Slashdot.

Anything the brand profile cannot answer is left blank and named as blank. We do not guess a logo URL or invent a category. Fill those in on the Knowledge page and the next listing carries them.

On your approval, we submit through a connected integration where one exists. Where none does — which is every directory today — you file it and mark it sent, and we take it from there.

## How a placement is verified

A placement is **verified** only when both of these are true:

1. **The page shows you.** We fetch the page and look for your brand name in its text or a link to your domain. We record which one we found.
2. **An answer cited it.** The page's URL turns up in a reading's citations, on a day after we did the work.

If the page shows you and nothing has cited it yet, the placement is **placed** — dated, honest, and re-checked daily for sixty days. It becomes verified when, and only when, an answer cites it.

If the page does not show you, nothing changes, however often an engine cites that page. An engine citing a page you do not appear on tells you the engine likes the page, not that the placement exists — a directory that quietly rejected a submission still gets cited every day.

The same rule applies to code: a merged pull request is not a deployed page, so we read the live URL before anything counts. Verification only moves forward. A page that returns an error today does not undo a placement confirmed yesterday. When a placement is verified, we schedule a re-read of your answers so we can tell you honestly whether it moved anything.

## Pitches

When a source an engine cites names a competitor and not you, the agent reads the page, finds who runs it from the page itself, and writes the pitch.

**What the pitch carries** is evidence, not adjectives: the verbatim answer passage that cited the page, the URL of the source, and the one page of yours that answers what its readers came for. The draft is under 150 words, makes one ask, and is signed by a real person from your profile.

**Where the draft goes.** It lands on an Inbox row you can read and edit. From there, press **Save as Gmail draft** or **Save as Outlook draft** and it appears in your own Drafts folder. You send it.

**Sending it for you.** With the workspace in **act** mode, and after the first ten have gone out by hand, and while there is room in the day's two, the agent can send the drafted pitch from your own connected mailbox. Anything else refuses and says which of those it was. Sending cannot be undone, so it is the one capability that is off unless you turn it on — see [Approval and modes](/agent/approval).

**What happens next.** Once a day the agent checks your mailbox for a reply to each pitch it sent. When one arrives, the row moves to **replied** and a line appears under **Waiting on you** in your [Inbox](/agent/inbox). It reads that a reply exists, who from and when — never the reply itself, and it never writes back.

**Who we never write to.** Anyone who has opted out, any address on a domain that has declined, and any market where prior consent is required and we do not have it. One email per site, one follow-up at most, never a third.

## Entries in other people's repositories

Some of the pages engines read are files in repositories: a curated `awesome-` list, another product's README, an ecosystem or integrations page in somebody's docs. A merged one-line entry there is a citation with no editor to persuade.

The agent reads the file, reads its `CONTRIBUTING.md`, checks the list's own stated criteria against your profile, and writes the single row in the file's own format. Then one of two things happens, and the Inbox row says plainly which:

* **Your connected GitHub account opens it.** If the account you connected has permission to open a pull request on that repository, the pull request is opened from your account, under your name.
* **You open it in one click.** Otherwise — and this is the usual case — the row carries the exact one-line diff plus a prefilled GitHub edit link. We are not installed on somebody else's repository and cannot open a pull request there for you, so we do not pretend to.


  This is draft-only in every mode. **Act** is a standing permission over your own site; a stranger's repository is not that, and no standing yes covers it.


We change one line. We never reorder or reformat a file, never remove somebody else's row, never open a second pull request while one of ours is open there, and never chase a maintainer with an issue or a comment. Whether it merges is theirs to decide.

## Original pages worth citing

When nothing third-party is cited for a question at all, there is nothing to join. The move is to publish the page that would be cited — and to build it out of something nobody else has: what we have actually read for you.

Two shapes, and only two:

* **A data page**, answering a question with counts from your own readings — what four answer engines said about something, across a window, with the sample size.
* **A reference page**, defining and organising something the category argues about, with each definition attributable.

**Every claim carries its source** — a reading id from your own history, or a public URL read while writing it. A sentence with no source is deleted rather than softened, and a draft with fewer than three sourced claims is not written at all. The method comes before the numbers: what was asked, of which engines, over what window, how many samples. Every count carries its sample size in the sentence itself, not in a footnote.

**How it ships.** Through the same path as any other change: a pull request against your repository, with your documentation site treated as a first-class target rather than a fallback, because engines cite docs. With no repository connected, the full draft lands in your Inbox with the path it wants.

**How it gets found.** Once the page is live, it becomes the thing the pitches point at. Never before — a pitch pointing at a page that is not there yet costs you the contact.

## Community threads

Threads on Reddit, Hacker News, Stack Overflow and Stack Exchange, and Quora that AI answers cite are worked through [community answers](/agent/community-answers): on by default, drafted with a disclosure line, and never posted for you.

## Who has it

Paid workspaces are enrolled in the off-site work streams automatically, the first time their subscription is active.

## Where to see it

Every off-site act is on your [private receipt](/agent/what-the-agent-shows): what was drafted, what was sent, who replied, what was placed, and what a later reading verified. A placement counts as verified only when the page itself shows you **and** the URL later turns up in a reading's citations. A merged pull request is not a live page, and we check the live page.


# Connectors
Source: https://docs.attensira.com/agent/plugins

Connect the tools your work already lives in, see what each one lets the agents read and do, and reconnect one that stopped working.

Connectors give the agents reach beyond Attensira's own data. A connector is a third-party tool an agent may use on your behalf: your Search Console, your mailbox, your CMS, a Google Drive folder. You can also register **your own MCP server**, which makes every tool it exposes available inside a chat or an automation.

## Where to find them: Customize

Everything that shapes how the agents work for you is under **Customize** in the sidebar. It has five sections, one pill each. On every pill but Company, a search box filters the list you are looking at:

| Section | What it holds |
| - | - |
| **Connectors** | The tools the agents can read from and deliver to, your own MCP servers, and webhooks. This page. |
| **Skills** | Your playbooks, and the skills built into every agent. See [Knowledge and skills](/agent/knowledge-and-skills#skills). |
| **Guardrails** | Your compliance rules, how the agents write, and the words a draft may never use. See [Guardrails](/agent/knowledge-and-skills#guardrails). |
| **Agents** | The agents working in this workspace, by project. Read-only. |
| **Company** | The facts every draft is written from, your brand and values, and the competitors you track. See [Knowledge and skills](/agent/knowledge-and-skills#company). |

Customize opens on Connectors. The old **Plugins** address, `/plugins`, takes you there.

## Connecting a connector

Open **Customize → Connectors**, find the tool under **Available**, and press **Connect**. You sign in on the tool's own site; Attensira never sees your password. The connection is per workspace, and you can disconnect it at any time from the connector's **Manage** page.

Until a connector is connected, the agents cannot see its tools at all. Connecting is the only way a tool enters their toolset.

The list shows what you can connect today, grouped by what each connector is for. A tool we cannot reach, or one we have built but not shipped, is not on the page: a row you cannot use is not an integration, it is a roadmap. If the tool you need is missing, **Missing a tool? Request it** at the foot of the page reaches a person.

## Reads and Can

Every connector says, under its name, what connecting it lets an agent **read** and what it lets an agent **do**:

```text theme={null}
Reads your messages, threads and labels · Can draft emails and send them, each once you approve it
```

The two lines are written from the same rules that decide which of the tool's actions an agent may call, so they promise nothing those rules do not allow. Two things hold for every connector:

* **Can: nothing yet** means the connector is read-only. No agent can change anything in that tool.
* **Every write an agent makes waits for a yes.** An agent that calls a write stops and asks first, which is why each thing a connector can do says it happens once you approve it. Nothing is sent, posted or published on the strength of the connection alone. The one standing yes is [Act](/agent/approval#act): a workspace you switch to Act lets safe hygiene fixes go out without asking each time, and anything that makes a claim still asks.

Slack works differently: an agent cannot post to it at all. Attensira itself posts your Monday note and the wins you share, and only to the one channel you picked.

Reads run without asking: the worst a read can do is return something irrelevant. Nothing that deletes or revokes anything of yours is ever admitted, on any connector, and nothing is archived with one exception: an update you approve to a [Notion blog](/agent/notion#how-updates-work) post replaces the post's content, and the old blocks are archived, not deleted, so Notion's own history still has them.

| Connector | Reads | Can |
| - | - | - |
| Google Search Console | The queries, positions, impressions and clicks behind each page, your sitemaps and each page's index status | Nothing yet |
| Bing Webmaster Tools | Who links to you, how Bing crawls and indexes your pages, and the searches that bring people in | Nothing yet |
| PostHog | Your events, funnels and insights, and the pages people convert on | Nothing yet |
| Google Ads | Your campaigns, keywords and what each click costs, in the account you choose | Nothing yet |
| GitHub | The repository that builds your site, its metadata and its pull requests | Open pull requests with fixes, which merge only when you approve them |
| Notion | The pages and databases you share with it | Create pages, draft blog posts and publish them, each once you approve it |
| Webflow | Your sites, CMS collections and their items | Stage edits and new articles as drafts and publish them, each once you approve it |
| WordPress | Your posts and pages | Save a fix to a post as a draft and publish it, each once you approve it |
| Shopify | Your products, collections, blogs and shop details, never orders or customers | Rewrite product and collection descriptions and SEO text, and save new blog articles as unpublished drafts, each once you approve it |
| Google Drive | The files in the one folder you pick, never the rest of your Drive | Save finished work as a new file in that folder, once you approve it |
| Google Calendar | Your calendars, their events and your free time | Add an event to your own calendar with no guests invited, once you approve it |
| YouTube | Your channel, its videos and their stats, and their captions | Nothing yet |
| Canva | Your designs, templates and brand kit | Nothing yet |
| Gmail | Your messages, threads and labels | Draft emails and send them, each once you approve it |
| Outlook | Your messages, folders and threads | Draft emails and send them, each once you approve it |
| LinkedIn | Your profile, your company pages and how your posts performed | Share a post or an article, once you approve it |
| Slack | The names of your channels, so you can pick where the note goes | Post your Monday note and the wins you share to the one channel you pick, and nothing else |
| Reddit | Subreddits, threads and comments where your category is discussed | Nothing yet |
| Instagram | Your profile, your posts and how each one performed | Nothing yet |
| HubSpot | Your contacts, companies and deals, a page at a time | Nothing yet |

The page in your workspace is the authority: a connector appears there only when it can actually be connected right now.

### Google Drive works inside one folder

After you connect Google Drive, pick the one folder the agents work in. Until you do, every Drive call is refused, reads included, and the row sits under **Needs attention** with **Pick folder**. The default for "which of my files may an agent read" is none, not all.

With a folder picked:

* **Reads stay in that folder.** An agent can find a file or a folder, read a file's details, download a file, and export a Google Docs, Sheets or Slides file. Those named operations are the whole list. A search only looks directly inside the folder, and a file named by its id is checked to sit directly in the folder before it is read. Subfolders, sharing, revisions, comments and the rest of your Drive are out of reach.
* **Finished work lands in that folder.** The one thing an agent can do is save a new file there, after you approve it. It always lands in the folder you picked, whatever folder the agent names. It never overwrites, moves, copies, shares or deletes a file, and never changes a permission.

The folder you pick is checked against the connection: it has to be a real folder in the Google account you connected. Its name shows beside the connector under **Connected**.

### Google Calendar adds holds, and invites nobody

An agent can read your calendars, their events and your free time, so launches, reviews and calls are planned around time you actually have. The one thing it can do is add an event to **your own** calendar, once you approve it. Every guest is removed from the request and Google is told to send no updates, so nobody else is emailed or invited. It cannot edit or delete an existing event, or change a calendar's settings or sharing. Inviting people is outreach, and goes through the outreach path and its limits.

### Shopify reads products, never orders

An agent reads your products, collections, blogs and shop details. Orders, customers, payouts, discounts and themes are out of reach. What it can change is copy, each change once you approve it:

* a product's or collection's description, and its SEO title and description. Never its price, variants, title, handle or status;
* a new blog article, always saved unpublished. You publish it from your own Shopify admin.

It cannot delete a product or publish one.

## Needs attention, and reconnecting

Connectors that need you sit at the top of the page under **Needs attention**, each with the one button that clears it:

| Reason | What the row says | Button |
| - | - | - |
| The connection expired | Reconnect *Name*: the connection stopped working. | **Reconnect** |
| The connection is failing for another reason | Reconnect *Name*, and why. | **Reconnect** |
| No site or account chosen (Search Console, Bing, Google Ads) | Pick which site *Name* should read. | **Pick property** |
| No folder chosen (Google Drive) | Pick the folder *Name* should work in. | **Pick folder** |
| An Inbox row is blocked on this connector, or work waits on one that is not connected | The blocker, in its own words. | **Connect**, or **Open** |

When approved work is waiting on a connector, the row says how much: **3 items waiting**. Everything else that works is under **Connected**.

A connection is marked as needing a reconnect when the tool's provider confirms it no longer works: when an agent's call through it fails and the provider reports the connection expired, or when the daily check of every connection finds the same. A single failed call on a connection the provider still reports as working marks nothing. When it happens:

* one Inbox row, **Reconnect *Name***, is filed with a **Reconnect** button. It is not filed again for every failed call;
* work you already approved for that tool is not lost. It stays approved and waits, and it goes out after you reconnect;
* [loop health](/agent/loop-health) says how many connections need reconnecting.

Reconnecting is one sign-in. The row clears, the Inbox row resolves, and the waiting work resumes on the next publishing pass.

## Where finished work can land

Some connectors are places the agent can deliver to, not only read from. Each one writes under the same [approval mode](/agent/approval) as everything else the agent does outward, and each finished piece shows up in [Deliverables](/agent/deliverables) with where it lives.

| Connector | What lands there | How it goes live |
| - | - | - |
| [GitHub](/agent/github) | A pull request against the repository your page lives in | When the pull request merges and deploys |
| [Lovable](/agent/github#lovable) | A pull request on the repository your Lovable project syncs | When it merges, and you next publish from Lovable |
| [Webflow and WordPress](/agent/cms) | A change to one field of a page or post; in Webflow, also a new article saved as a draft | A field change when you approve it, or a hygiene change in act mode; an article when you publish it in Webflow |
| [Notion](/agent/notion) | A new row in your blog database, or an update to a post | As the [Notion page](/agent/notion) describes |
| [Shopify](/agent/shopify) | Product and collection copy, or a new blog article saved unpublished | Copy when it is approved, or a hygiene change in act mode; an article when you publish it from your Shopify admin |

Without one of these, the agent still does the work: it hands you the text to paste, and you mark it shipped once it is live.

## Registering your own MCP server

If you run an MCP server, register it under **Customize → Connectors** and the agent can call it like any built-in tool.


  
    Give it a name and its URL. The name becomes the slug the agent uses to address it. If you already use the server in Claude Desktop or Cursor, paste that `mcpServers` configuration instead: each remote server in it is imported with its URL and its headers.
  

  
    An open server needs nothing. Most servers that need a key expect it as a request header, such as `Authorization: Bearer …`, which comes across with a pasted configuration. Keys and header values are write-only: the page shows which headers are set, never their values.
  

  
    Attensira calls `tools/list` on the server and registers what it finds.
  


A server that runs as a local program (a `command` entry in a pasted configuration) cannot be registered: there is nothing for us to reach over the network.

### How your tools are named

Discovered tools are namespaced:

```text theme={null}
mcp____
```

So a `search` tool on a server registered as `warehouse` becomes `mcp__warehouse__search`. The namespace makes collisions impossible: two servers can both expose `search`, and Attensira's own built-in tools can never be shadowed by a registered one, because the built-ins are assembled first.

### Servers that sign in with OAuth

You can register a server whose auth mode is OAuth, but the agent cannot call it yet: Attensira holds no OAuth token for it. The server appears to the agent as one tool that says it needs authorising, so the agent tells you that instead of failing with a 401 nobody can diagnose. Use `apiKey` or a header instead, or put an authenticating proxy in front of the server.

### When a server is unreachable

An unreachable server does not silently vanish from the agent's toolset. It becomes a single tool named `mcp____unavailable` that explains the problem when called.

That is deliberate. A tool that disappears looks to the agent like a capability that never existed, and it will confidently tell you it cannot do something it normally can. A tool that answers "this server is unreachable" gets you a real diagnosis.

### Limits

Discovery and calls are bounded so one slow server cannot hold up a whole session:

| Bound | Applies to |
| - | - |
| Discovery timeout | Listing tools when a server is added or a session starts |
| Call timeout | A single tool call |
| Tools per server | How many discovered tools are registered |
| Discovery budget | Total time spent discovering across all servers before a session starts |

A server that exceeds the discovery budget is skipped for that session rather than delaying the answer.

## How connectors relate to Attensira's own MCP server

These are opposite directions and are easy to confuse.

* **Connectors and registered MCP servers** are *inbound to the agents*: tools the Attensira agents can call.
* **[Attensira's MCP server](/mcp/overview)** is *outbound to you*: it lets your own assistant, such as Claude or ChatGPT, call Attensira. Set it up from **Settings → Your assistant**; see [Connect a client](/mcp/connect).

You can use either, or both. They share no configuration and no credentials: a connector's sign-in has nothing to do with your [Attensira API key](/account/api-keys).

## What connectors cannot do

* They cannot act without a yes: yours on the item, or the standing one you gave by switching the workspace to [Act](/agent/approval#act). Nothing that deletes anything of yours is admitted.
* They cannot be re-read on demand. There is no control that forces a connector to refresh, because there is no endpoint behind it. Rather than show a button that spins and does nothing, the product omits it.
* A connected tool is available to the whole workspace, not to one person or one chat.


# Projects
Source: https://docs.attensira.com/agent/projects

A project is a standing conversation with a crew we staff for one piece of ongoing work: what it shows, how you steer it, and what bounds it.

## What a project is

A **project** is a standing conversation with a crew of agents we staff for one piece of ongoing work, such as "win the comparison questions on Perplexity" or "keep the docs cited". You set the direction and answer the few questions only you can answer. The crew does the work and posts what it did, and why, to the project's feed.

A project has:

* a short name, shown as `#slug` (two to forty lowercase letters, digits or hyphens);
* a **focus**: what the crew works on, in a sentence or two;
* a **crew**: the roles staffed on it, and what wakes each one;
* **automations**: the crew's standing work, on a schedule or when something changes;
* optionally a **goal**, a **deadline** and a **monthly cap**.

A project lives inside a [workspace](/start/concepts#project-and-workspace). The workspace's prompts, competitors, connections and data are the project's too; a project narrows what the crew works on, not what it can read.

Ongoing work belongs in a project; a one-off question belongs in a [chat](/agent/chats). The conversations a project's crew has are listed under the project in the sidebar, not in Chats.

## Starting a project

Press **+** next to **Projects** in the sidebar and describe a problem or a goal in your own words. There is nothing else to fill in: no name, no team picker, no schedule.

The Chief of staff scopes it with you in a chat, then proposes the project as a card: a name, the focus, the crew and what wakes each member. Nothing is created until you press **Create project**. A chat that turns out to be ongoing work can end in the same card without you asking for it.

Creating the project:

1. makes the project and its crew;
2. creates one automation for each thing that wakes a crew member, marked **By Attensira** and yours to edit, test or switch off;
3. posts a kickoff line from the Chief of staff in the feed.

If a crew member needs an integration the workspace has not connected, it starts without that tool rather than failing, and the feed gets an **On your side** line such as "Connect GitHub so the Writer can open pull requests on your site. Until then they work without it."

If setup stops part way, the feed says so and an Inbox task, **Finish setting up #slug**, is raised. Creating the same proposal again picks up where it stopped; it never makes a second project or duplicate automations.

## The crew

You never define an agent. The Chief of staff staffs the project from a fixed set of roles, each with the tools and house procedures that role works with. To add someone, ask the Chief of staff in the project.

| Role | Wakes when |
| - | - |
| Chief of staff | You post in the project, and every week to report what the crew did and what moved. Always on the crew |
| Researcher | A competitor takes one of your questions, or a new source starts deciding the answers |
| Writer | A question you track has no page that answers it, or one of your pages stops being cited |
| Designer | A page AI answers already cite could travel further with a picture or a card |
| Web engineer | Something on your site stops AI crawlers or search engines from reading it |
| Community | A thread AI answers cite asks something you can answer |
| Analyst | Every week, to read what moved and whether it is more than noise |
| Outreach | A site AI answers cite could mention you, or a pitch is due a follow-up |
| Video | A video AI answers cite needs a better title, description or chapters |
| Ads strategist | You pay for clicks on a question AI answers already give away, or an ad contradicts your pricing |
| Copywriter | AI answers get your pricing, product or positioning wrong |

The project header's team menu shows what each member is doing now:

* **Working**, with what it is working on;
* **Waiting on you**, with the question or approval it is stopped on and where to answer it;
* **Stopped moving**, when a run has stalled. Open the run to see where;
* **Idle**, with when it wakes next.

When a change wakes one of the crew's automations, the run can be written up by the crew member it fits best: a competitor's new comparison page that wakes the Researcher's automation may be answered by the Writer. The run keeps that automation's tools, approval mode and caps; only the name on it changes.

## The feed

The feed is the project's conversation, oldest first. Each run has one line, which says **Working** while the run is under way and is updated in place when it finishes, so a run never floods the feed with a line per step. Lines you have not read are marked **New since you last looked**; that marker is yours alone.

| Line | What it is |
| - | - |
| Update | A message you posted, or a note from the Chief of staff such as the kickoff |
| Working | A run under way |
| Shipped | A run that produced something: an article, a pull request, a post |
| Result | A run that finished with an answer and nothing to ship |
| Needs you | A run stopped for your approval or your answer |
| Question | A question for you |
| On your side | Something only you can do, such as connecting an integration |
| Direction | A steer you gave |
| Plan updated | The crew changed how it works. See [Plan updated](#plan-updated) |
| Didn't finish | A run that failed or was stopped. The line says why in plain words |

A line that waits on you carries the same **Approve**, **Review** or **Answer** button as its row in the [Inbox](/agent/inbox), and every run's line has a **Replay** button that opens [that run, watched back](/agent/what-the-agent-shows#watching-a-run-back). Any line can open a thread.

### Because lines

Every run's line says why it started, in one sentence of at most 160 characters:

* *Because "Comparison check" runs every Monday at 09:00 BST*
* *Because competitor.com changed pages on its site*
* *Because AI answers stopped citing example.com/pricing, and 2 more changes*
* *Because you steered #comparisons: "Focus on multi-location practices"*
* *Because "Comparison check" was started as a test*

These sentences are built from what we recorded, the automation's schedule or the change we detected, never from a model's summary of it. A competitor's own words stay out of the sentence: they go to the run, not into a line we put our name to.

## Talking to the crew and steering it

The composer at the bottom of the feed has two modes.

* **Message** asks the crew something or hands it work. The Chief of staff answers in the feed and hands out the work.
* **Steer** sets direction: "Focus on multi-location practices, stop the SMB posts." The Chief of staff answers it the same way, and the steer is also kept, word for word, as the project's **direction**.

Every run of the crew's automations from then on is told the project's latest five steers, up to 2,000 characters together. The newest is always told, cut only if it alone is longer than that; older steers that do not fit are left out whole rather than cut. A project keeps its last 50. No model can write to the direction: it holds only what people typed.

A post holds at most 4,000 characters. An archived project takes no posts.

### Plan updated

A steer often means the crew's standing work should change: a different instruction, a different schedule, different tools. The Chief of staff can propose those changes to the project's automations, and what happens next depends on who wrote the automation.

* **By Attensira** (created when the project was set up): the change is made under the run's [approval mode](/agent/approval#projects-can-be-stricter), the stricter of the workspace's and the project's. In Ask it waits for your yes; otherwise it is applied. Every applied change posts a **Plan updated** line saying what changed: the instruction before and after, the schedule, the tools, or that the automation was paused. **Show the change** expands it.
* **By you**: nothing is changed. The Chief of staff shows the change as a card, and it is applied only when you confirm it.

Turning an automation back on is always your call, whoever wrote it. The Chief of staff cannot give an automation a tool that needs a stricter approval than anything it can reach today, and it cannot change the project's approval mode, its cap or its direction. Those are yours, in the project's settings and your own steers.

## The project panel

Beside the feed, the panel has three tabs:

* **Open work**: everything this project raised that waits on you or is moving. The same rows are in the [Inbox](/agent/inbox).
* **Deliverables**: what the project's runs shipped, with where each one lives.
* **Automations**: the crew's automations, each with its author (**By Attensira** or **By you**), whether it is on, and the first tools it may reach for. Each opens the automation's page, where it can be edited and [tested](/agent/automations#testing-an-automation).

Below 1280 pixels wide, the panel opens from a button in the header.

## Project settings

Open **Settings** in the project header.

| Setting | What it does |
| - | - |
| Name and focus | The project's name, and what the crew works on. Conversations in the project are told the current focus. The crew's automations were written with the focus the project was set up with, so when the focus changes, steer the project and the Chief of staff updates them |
| Goal | None, one of your measured goals, or a goal in words. A measured goal shows whether the project is on pace in the header; a goal in words has no number to pace, and the header says so |
| Deadline | Optional, and must be in the future when you set it |
| Monthly cap | In dollars. See [The monthly cap](#the-monthly-cap) |
| On its own | **Ask first**, **Leave drafts** or **Act**: how much the crew does without you. It can be stricter than the workspace, never looser. See [Approval modes](/agent/approval#projects-can-be-stricter) |
| Works on its own when… | One switch per automation, with its author. Turn one off and it stops running on its own until you turn it back on |

Changes are saved only when the server confirms them; a refused change says why and saves nothing.

## The monthly cap

A project can have a **monthly cap** of its own, in dollars, to the cent. Empty means no cap of its own: the workspace's limits apply. The cap is a budget for the crew's work in a month. It bounds what the crew spends, and it is not a promise of a result.

Only an org admin can change a project's cap, here or in billing settings; both edit the same cap. The settings show what the project has spent this month against it.

When the project reaches its cap, its new work stops until the month turns or the cap is raised:

* its scheduled and event runs do not start, and **Test** is refused;
* the Chief of staff does not start on new posts either. Your post is saved, the feed says the reply could not start, and a steer is still kept as direction;
* the feed gets one line for the month, such as "This project reached its monthly cap of \$400.00 for October, so its scheduled and event work is paused. It resumes on November 1, or as soon as the project's cap is raised in billing settings.";
* the Inbox gets one row, **Scheduled work is paused: this project reached its monthly cap**, naming the scheduled automations that did not run, with a **Raise cap** button. It closes itself once a run of that project is paid for again.

A run already under way always finishes, so a month can go over the cap by at most one run. Months are calendar months in UTC. See [Credits](/account/credits#project-monthly-cap) for how the project cap sits beside the workspace's.

## Paused and archived projects

A **paused** project's automations are off until it is resumed; anything you post to it still gets an answer. Resuming turns back on only the automations the pause turned off.

An **archived** project is hidden from the sidebar, its automations are off, and its feed, runs and slug are kept as they were. It takes no posts until it is resumed.

An automation whose project is paused or archived does not run, and a test of it is refused too. If we cannot read the project's state when a run is due, the run is refused rather than risk spending on a project you stopped. A scheduled or event run skipped for any of these shows in [loop health](/agent/loop-health), with the reason `work_area_inactive` (paused or archived), `work_area_unreadable` (its state could not be read) or `work_area_cap` (at its cap). A refused test says why when you press **Test**.

## Who sees a project

Everyone in the workspace sees every project in it. There are no per-project permissions. Unread counts and the **New since you last looked** marker are per person.

## Projects, automations and the API

The workspace's **Automations** page still lists every automation. Once the workspace has a project, a **Project** column says which project each belongs to, and a project's automation opens inside its project.

Over [MCP](/mcp/tools), these tools reach projects:

* `list_projects` lists the workspace's projects, leaving out archived ones: each one's `id`, `slug`, name, status, crew, goal, unread and needs-you counts, whether its crew is running, its `autonomy` and its `budget_cap_usd` (null when it has no cap of its own). With `id` it reads one project instead, adding its direction, its triggers, its `automation_ids` and the newest 50 lines of its feed, newest first. A read key is enough.
* `steer_project` takes a project's `id` and your `text`, at most 4,000 characters, and does what **Steer** does in the dashboard: the text is kept word for word as the project's direction, posted to its feed, and answered by a Chief of staff run, which is paid work. It changes no setting and approves nothing, whatever the text says. It needs a read-and-write key. Pass an `idempotency_key` so a retry posts nothing twice; the same key with different text is refused.

Creating a project, its settings, and pausing or archiving it are in the dashboard only. The [REST API](/api/endpoints#projects) has the same reads and the steer under `/v1/work-areas`: `GET /v1/work-areas`, `GET /v1/work-areas/{id}` and `POST /v1/work-areas/{id}/steer`. A project's runs and automations still appear in `list_sessions` and `list_automations` like any other. Where a payload refers to a project it says `work_area_id`, because `project` already means the workspace on every tool and endpoint.


# Searching what was actually said
Source: https://docs.attensira.com/agent/recall

Recall searches the answers we have already read for the passages nearest your question, and hands them back with the prompt, platform and day they came from.

## Counts answer "how often". Recall answers "in what words"

Most of what the agent reads for you is arithmetic: how often you were named, how often you were cited, which pages earned it. Those are the right answers to "how are we doing".

They are the wrong answer to "how is that competitor being *described*", or "what reason did the answer give for recommending them", or "is the claim we published being repeated back". Those questions are about wording, and the wording is in the answers themselves.

**Recall** searches those answers by meaning rather than by keyword. Ask it for "reasons given for choosing a cheaper tool" and it finds the passages that say that, whether or not any of them used the word "cheaper".

## Every passage comes with its receipt

A quote you cannot check is an anecdote. So every passage recall returns carries, on the same row:

| | |
| - | - |
| **Day** | The day the answer was read |
| **Platform** | Which AI platform said it |
| **Prompt** | The tracked question that produced it |
| **Passage** | The words, up to about a paragraph, taken from around your brand where the answer names it |
| **Reading** | The id of the sampled reading, so it can be opened |

The agent is told to quote these rather than paraphrase them. A summarised passage is a claim the platform did not make.

## What is searched, and what is not

Recall searches **the answers themselves, and nothing else.** Not the pages those answers cited, not snapshots of your site, not your own documents.

That is a deliberate limit rather than an unfinished one. Indexing every cited page alongside every answer would multiply the store roughly tenfold, for passages that are one sentence of somebody else's page — and that page is better read directly, which the agent can also do.

Answers are searchable from the point the feature was switched on. Older readings are indexed in the background, oldest first, so a long history fills in over the following days rather than all at once.

## Scope and window

Recall only ever searches **your own workspace**. There is no way to widen it — not from a question, not from an automation's settings, not by asking the agent to.

It searches the **last 28 days** by default, and up to 90 on request. If nothing in the window matches, it says so. "Nothing in the last 28 days matches that" is a real answer, and a better one than a paragraph assembled from memory.

## What it costs

A recall search costs a fraction of a credit — it is one small lookup, not a model call, and it is priced accordingly. It appears on your usage log like any other metered step. Reading your counts and boards stays free.


  How the agent draws what it finds, and why a gap is never a zero.



# Reports
Source: https://docs.attensira.com/agent/reports

Ask the agent for a report: the full client report or the one-page board report, for the last complete week, month or quarter, on a link that opens without an account.

A report says, for a closed period, what was done and what it changed. The agent writes the words; every number on it is read from your workspace for that period, so a report cannot claim more than the data shows.

## Two formats

| Format | Who it is for | What is on it |
| - | - | - |
| **Client** (the default) | A client, or anyone who wants the detail | Visibility per engine, the work shipped, before-and-after re-reads, deciding sources, competitors and next steps. See [Client reports](/agencies/client-reports). |
| **Board** | A board, a CEO, anyone with a minute | One page: your goals against their pace, what shipped, what was spent, and one line on visibility. |

Both cover the **last complete period**: the ISO week, the calendar month or the calendar quarter. A board report is usually monthly.

## Getting one

* **In a chat.** Ask for it, "the board report for last month", or pick **Report** from the `/` menu first. A Report chat may read your data and publish the report, and nothing else that acts. See [Modes](/agent/chats#modes).
* **On a schedule.** The weekly, monthly and quarterly report [automations](/agencies/client-reports#making-reports) publish the client format. Say "board report" in an automation's instruction to publish that format instead.
* **By hand.** **Make one now** under **Results → Client reports** publishes the client format for the period that ended last.

Board reports appear in the same list under **Results → Client reports**, beside their period and marked as board reports, for example **Monthly board**.

In a chat, the published report opens in the [work panel](/agent/what-the-agent-shows#the-work-panel) under **Deliverable**, drawn as the report itself, with its share link above it to copy. The agent's reply says what the report is for and what to look at first; it does not retype the report's numbers.

## The board report

The board report reads in this order:

1. **Summary.** The totals in a sentence (how many goals stand at each pace below; how many pieces of work shipped; what was spent), and the agent's highlights.
2. **Goals.** One row per goal that existed by the end of the period and is not archived: what it measures, where it stands now with its sample size, the target, the deadline, and its pace.
3. **What shipped.** Shipped, waiting and failed counts, and the shipped work by kind, for example "4 pull requests, 2 directory listings".
4. **Spend.** What the workspace spent in the period, in US dollars.
5. **Visibility.** One line: named in X of N answers across every engine, how many days in the period were read, and how many changes a re-read proved to move.
6. **Next period.** What comes next.

### Reading a goal's pace

| Pace | Meaning |
| - | - |
| **Ahead of pace** | Past where a straight line from the goal's starting value to its target says it should be by now, by more than the reading's noise band |
| **On pace** | Within the noise band either side of that line |
| **Behind pace** | Short of that line by more than the noise band |
| **Not measured yet** | No reading yet. Its current value shows as **—**, never as 0 |
| **Reached**, **Missed**, **Paused** | The goal was hit, its deadline passed without it, or it is paused. These replace the pace |

The pace is the goal's own stored verdict, quoted as it stood at the end of the period. A goal on a business signal, such as sessions from Google Analytics, is marked **(business signal)**: it is shown beside the work, not credited to it.

### What the board report does not claim

* A section whose data could not be read says so: "The goals could not be read for this report", "Spend could not be read for this report". It is never shown as zero or left out.
* The agent's words follow the same rules as every report: no movement words (increased, dropped, improved) unless a [re-read](/measure/before-and-after) proved the change, and counts written as "X of N answers" rather than percentages.
* Nothing on it promises an outcome. Attensira guarantees the volume of work, not visibility; the report shows the work and what was measured after it.

## Sharing

Every report, client or board, gets a signed link at `app.attensira.com/r/report/` that opens without an account, lasts **180 days** and prints to PDF. Rotating the workspace's receipt link revokes every link at once. The page carries your org's [branding](/agencies/branding).

A report published from a chat is not emailed. Email delivery, recipients and the reporting standard work as described in [Client reports](/agencies/client-reports#email-delivery).


  
    Sections, schedules, recipients and email delivery in full.
  

  
    The only place a report may say something moved.
  



# Shopify
Source: https://docs.attensira.com/agent/shopify

Connect your Shopify store and the agent rewrites product and collection copy and adds blog articles as unpublished drafts. Nothing reaches shoppers without your yes or a hygiene change your workspace allows.

If you sell on Shopify, the product and collection pages are where shoppers and AI assistants read about what you sell. Connect your store and the agent can fix the copy on those pages itself, and put new articles into your store's blog, instead of handing you text to paste.

## What we change

On a **product**, a **custom collection** or a **smart collection**, three fields and no others:

| Field | In your Shopify admin |
| - | - |
| Description | The product's or collection's description |
| SEO title | **Search engine listing → Page title** |
| SEO description | **Search engine listing → Meta description** |

And in your store's **blog**: new articles, always created unpublished.

That is the whole of it. We never change a price, a variant, a product title, a handle, an image or a theme, and never touch orders, customers or discounts. We never publish or unpublish anything, never delete anything of yours, and never empty out an SEO title or description. Image alt text and structured data are not changed through Shopify.

**What we read:** your products, collections, blogs and shop details. Never orders or customers.

## Which pages it covers

A change is proposed against a page on your storefront at `/products/` or `/collections/`. A language prefix (`/fr/products/…`) is read past, and a product reached through a collection (`/collections/sale/products/…`) counts as the product.

On your store's primary domain or its `myshopify.com` address, a handle is matched to the product or collection exactly. The same path on any other domain is treated as a likely match, and the agent reads the page before proposing a change to it.

## When a change goes live

The same [approval mode](/agent/approval) that governs every outward write decides:

* **Ask or Draft mode.** The proposed change waits in your [Inbox](/agent/inbox). Nothing is written to your store until you approve it.
* **You approve it.** We write the field, and shoppers see the new copy.
* **Act mode, hygiene only.** A change goes live without asking only when it is class **safe** (no price, number with a unit, promise or competitor's name), its page is inside the paths you let act mode change, and the workspace is still in act mode at the moment we write. Changes are classed by the same rules as a repository change. The path list starts at `/docs/**` and `/content/**`, which no product or collection page is under, so until you add `/products/**` or `/collections/**` every change to your store waits for your yes.
* **Act mode, anything else.** The change is saved as a **proposal on the product or collection**, and waits in your Inbox for your yes. Shoppers see nothing.

### Proposals in your Shopify admin

Shopify has no unpublished version of a product's description, so a proposal is kept beside the product instead: a metafield in the `attensira` namespace, one per field (`attensira.proposed_description_html`, `attensira.proposed_seo_title`, `attensira.proposed_seo_description`). You can see what is waiting from your own admin.

The item goes back to your Inbox, and its timeline says **A draft is waiting on your site: Synced to Shopify (draft)**, with the reason it was not put live. When you approve it, the live field is written and the proposal metafield is removed. We only ever remove our own `attensira` metafields; a metafield of yours is never deleted.

### If the copy changed since you approved

Immediately before writing, we read the field again. If it no longer holds what the proposal was made against, because someone on your team edited it in the meantime, we do not write over it.

## New articles

An article the agent wrote for your Shopify blog waits in your Inbox for your yes, as every article does. Approving it adds it to the blog **unpublished**, whatever the workspace's approval mode: publishing it is always yours, from your Shopify admin.

* **Which blog.** If the store has one blog, that one. With several, the one whose handle reads like a blog (`news`, `blog`, `blogs`, `journal`, `articles` or `stories`). If none of them does, or more than one does, or the store has no blog at all, nothing is written, and the item comes back to you saying why.
* **No twins.** Approving the same article twice leaves one article: if the unpublished article an earlier approval created is already in that blog, you get that one back. An article you have already published is yours, and is never reported as our draft.
* Once you have published it, **Mark shipped** in the Inbox with its URL. That books the [re-reads](/measure/before-and-after).

## After it ships

A product or collection change is live once it is written. From then, the tracked prompts attached to it are [re-read](/measure/before-and-after) at +7, +14 and +30 days, and the result appears on the item and in [Deliverables](/agent/deliverables) under **What it did**.

## Connecting

1. Open **Customize → Connectors** in your workspace.
2. Find **Shopify** and press **Connect**.
3. Sign in to your store and approve the connection. The sign-in happens at Shopify; we never see your password.
4. Check the path list under the workspace's [approval settings](/agent/approval). It decides which pages act mode may change on its own, and it starts at `/docs/**` and `/content/**`: add `/products/**` and `/collections/**` if you want act mode to put hygiene changes live on your store.

Disconnect from the same place at any time. Disconnecting does not remove proposals already saved in your admin.


# Tables, charts, the work panel and replay
Source: https://docs.attensira.com/agent/what-the-agent-shows

What the agent draws alongside its reply, why an unmeasured day is a gap and not a zero, how to copy, download or pin it, the work panel that holds what a run made, and how to watch a run back step by step.

## The agent draws, it does not retype

When you ask the agent something about your numbers, it reads them and then draws them: a headline figure, a table of the rows behind it, and a trend line where there is one. Its written reply sits above that and says what the numbers mean — what moved, what it implies, what to do next.

This split is deliberate. A model asked to restate a forty-row table in prose does it at length, and occasionally retypes a number wrong. Handing it the rows and drawing them separately means the figures you read came out of the measurement, not out of a sentence.

## What it can draw

The agent does not write its own markup. It names a component from a fixed set, and the app draws it:

| Component | What it is |
| - | - |
| Metric | One headline number with its movement |
| Table | Rows and columns, capped at the top ten |
| Chart | A trend line over the window, one line per model |
| List, quote, prose | Ordinary reading blocks |
| Code, diff, link list | Reference blocks |

Two things follow from this. The agent can only produce layouts that have been designed, so a generated answer is held to the same visual standard as the rest of the app. And an app that has not shipped a component yet falls back to a plain-text description of what it would have said, rather than breaking the conversation.

## Gaps are gaps, not zeros

A day nobody sampled shows as a **break in the line**, never as a drop to the floor.

This is the same rule the dashboard follows. A share of voice of `null` means the prompt was not read that day; a share of voice of `0` means it was read and you were not mentioned. Those are different facts, and drawing the first as the second would invent a collapse that did not happen. The caption under a chart says how many days in the window were not measured.


  If a chart looks sparse, that is usually sampling depth rather than a bad week. See [Reading the numbers](/measure/reading-the-numbers).


Movements follow the same rule. A change inside the sampling noise is reported as "no change" rather than as a small number with an arrow.

## Copying and downloading

Every table and chart the agent draws carries **Copy** and **Download**.

* **Copy** puts the rows on your clipboard tab-separated, which pastes straight into Sheets or Excel and still reads correctly if you paste it into a message instead.
* **Download** saves a `.csv`. Charts save the rows behind the line, not a picture of it — a picture is not something you can pivot.

Only the table or chart is copied. The tool calls above it and the agent's reasoning are not part of what you are taking away.

## Pin to board

A chart or table drawn from your own data also carries **Pin to board**. Pick one of your boards, or let Attensira make one called **My board** if you have none. The pin lands on that board under **Analytics → Boards**.

What is pinned is the read that drew the view, never the numbers or a picture of them. The board reads it again each time it opens, so next month it shows next month's numbers.

* Only views drawn from a read of your data can be pinned. A draft, a card, or anything drawn by a tool that fetched the web or spent credits has nothing that could be read again, so it has no **Pin to board**.
* Only your own boards take pins. The boards Attensira builds in are not offered.
* A board holds up to **24** pins. A full board is listed but cannot be picked.
* If a pin fails, a message says so and the card stays as it was.

## Seeing what it did

While the agent works, each step appears as it happens — reading your data, searching the web, fetching a page, changing what you track — each with its own mark, so a lookup is distinguishable from a crawl at a glance.

Once the steps have finished they collapse into a single line, and the answer sits directly under the question. Click that line to open them again. Two exceptions never collapse: a step that failed, and a step waiting on your approval.

### Worked for

When a run ends, one quiet line under it says how long it worked: **Worked for 2m 14s**. It counts from the run's first step to its end, less any time it spent waiting for your answer to a question, and shows at most two units, the larger first. A run that took less than a second says **Worked for under a second**.

## The work panel

The work panel holds what a run made, beside the conversation about it. Open it with **Work** at the top of the chat, and close it with **Hide work**; **Replay** opens it on the Replay tab.

On a window at least 1,024 pixels wide the panel sits beside the chat, and opens on its own when the run made something to take away. On a narrower window it opens over the chat, only when you ask for it.

| Tab | What it holds |
| - | - |
| **Deliverable** | The thing to take away: an article, a page, an image, or a published [report](/agent/reports). Copy it, or press **Review and publish** to open its approval in the [Inbox](/agent/inbox). A report is drawn as the report itself, with its share link to copy. |
| **Changes** | Every piece of work the run filed, with where it stands: waiting for your yes, revised, approved, or already in your Inbox. Each opens its Inbox item. |
| **Browser** | The pages the agent read, with what it took from each. |
| **Replay** | Every step the run took, in order, with its time and the credits it was charged, and the run's totals. |

A tab appears only when it has something in it, so a run that only answered a question usually shows **Replay** alone.

Nothing is published from the panel. **Review and publish** goes to the work item's approval in the Inbox, where your [approval mode](/agent/approval) applies as it does everywhere else.

## Watching a run back

Any finished or running piece of agent work can be watched back step by step. The replay is the same wherever you open it:

* **Replay** on a line in a [project's feed](/agent/projects#the-feed), including a **Plan updated** line;
* **Replay** on a run in an [automation's history](/agent/automations#runs-and-run-history), and on a test run once it starts;
* a deliverable, and an item in the [Inbox](/agent/inbox), open the run that made them;
* in a chat, the **Replay** tab beside the conversation.

Each of these opens exactly the run it belongs to, never a neighbouring one.

The header says who did the work (the crew role, in a project), which project it was for, when it ran, and its **Because** line: why it started, in one sentence built from the schedule or the change we recorded, never from a model's summary.

Under it are the run's totals, how long it was working, the credits it spent and how many steps it took, and then the steps in the order they started. Each step says what was done and to what ("audited example.com/pricing", "asked for your approval"), how long it took, the credits it was charged (free reads show none), and how it ended. A step that failed shows the reason in plain words. A stop for you shows how it was settled: approved, declined, answered, withdrawn, never answered, or still waiting on you.

Beside a step is what it saw or made:

| Frame | What it shows |
| - | - |
| Page | A page the step read: its address, its title and an excerpt quoted word for word from what was fetched |
| Document | A document the run rewrote, as a before-and-after of the two versions |
| Data | A table or chart the step drew |

Runs do not take screenshots, so the screenshot slot of a page says **No screenshot for this step** rather than showing an empty box.

In the dialog, play steps through on their own, drag the scrubber, or use the keyboard: the arrow keys move a step, **Home** and **End** jump to the first and last, and **Space** plays or pauses.

A replay carries at most 500 steps. A longer run shows the first 500 and says so; its totals still count every step.

The replay is a summary of the run, not its raw log: a step shows its one-line description rather than everything the tool was called with, and a failure shows the plain-words reason rather than the internal error.


  How to ask a good question, what the box can do, modes, and the questions the agent asks you.



# What the agent remembers
Source: https://docs.attensira.com/agent/what-we-know

The evidence-cited facts the agent derives about your workspace every night, so a conversation doesn't start by rediscovering what last night's run already knew.

## What this is

Every night, a job looks back over your workspace's readings and actions and writes down what it learned — in plain sentences, each one carrying the evidence it rests on. The next time you open a chat or an automation runs, the agent already knows the shape of your workspace instead of spending its first tool calls rediscovering it.

## What gets recorded

* **Top deciding domains per engine**, with their share and the sample size behind it — the same domains [Who decides an answer](/measure/deciding-sources) shows, folded into a standing fact instead of a live query.
* **The source mix** — how much of what decides your answers is your own pages, competitors, or everyone else.
* **Prompts won and prompts lost**, and which ones.
* **Differences by surface or by country** — but only when the difference clears the same significance test the rest of the product uses. A split that is not outside the noise is not written down at all; "consumer and API look the same" is the correct sentence at a small sample, and stating a difference that isn't real would have the agent repeat a lie for ninety days.
* **How often each kind of off-site action got placed** — pitches, listings, mentions you pursued — and how often they landed.

## Every observation carries its evidence

A sentence without a sample size is noise in a confident font. A sentence without evidence is a claim nobody can check — and once it is folded into every future conversation, an unchecked claim is indistinguishable from a hallucination that reinforces itself. Every observation states the `n` it stands on and points at what it was derived from.

## They expire

An observation is good for **90 days**. After that it ages out rather than sitting there as a stale fact the agent keeps repeating about a workspace that has since changed.

## The agent can write one too — with evidence that resolves

The agent is not limited to what the nightly job derives. It can record an observation itself, but only when the evidence behind it actually resolves against your workspace — a claim citing something that does not check out is rejected outright, not written down with an asterisk.

## Available on every plan

This is system memory, not a paid add-on. It ships on every plan — unlike writing custom skills in [Knowledge and Skills](/agent/knowledge-and-skills), which is Growth and above. What the product itself learned about your workspace is available regardless of plan.

## Shown to the agent as data, never as instructions

Observations are third-party in origin — nightly ones are derived from prose written by ChatGPT, Perplexity and Gemini, which in turn quotes pages nobody at Attensira controls, and agent-written ones come from a model. Nothing in an observation is ever treated as something the agent should *do*. It is read the same way any other untrusted context is read: fenced off and scrubbed before it reaches a conversation, so a stray instruction hidden in a quoted page cannot steer what the agent does next.


  See [Reading the numbers](/measure/reading-the-numbers) for how `n` and significance work everywhere else in the product — observations use the same rules, not a separate standard.



# Wins: the first time an answer names you
Source: https://docs.attensira.com/agent/wins

What counts as a win, how the hourly pass decides it, the message you get, the share link, and the win_detected event.

## What a win is

A **win** is the first time an engine names your brand, or links to your own site, in an answer to a prompt it had been answering without you. It is recorded per prompt and per engine, so ChatGPT naming you on a prompt and Perplexity naming you on the same prompt are two wins.

There are two kinds, and each is its own first:

| Kind | What happened in the answer |
| - | - |
| **Named** | The answer named your brand in its own words |
| **Linked** | The answer cited a page on your domain, or on a subdomain of it |

An engine can name a brand it never links, and link a page while recommending somebody else, so one does not stand in for the other. Each (prompt, engine, kind) can be a win **once**, ever.

A win says what happened and when we started reading. It never says why. The message reads "for the first time since we started reading this prompt", not "after we shipped" or "because of our work".

## What counts

Every hour, a pass reads the last three days of your [readings](/measure/how-we-collect-data), in your workspace's time zone. It is arithmetic over the same numbers your dashboard shows, with no model involved, so the same readings always produce the same wins.

A naming is a win only when all of these are true:


  
    A prompt that names you ("is Acme any good") is a question about you, and being named in its answer is not news. The prompt's text is checked against your brand's name, your domain and your domain's bare name, as whole words. A prompt that names a **competitor** ("Acme vs Curve") still counts: that is a question a buyer asks.
  

  
    A prompt you deleted, or an engine you removed from a prompt, produces no wins.
  

  
    Before the day you were named, at least **three** earlier days carried at least one successful reading of that prompt on that engine.
  

  
    On no earlier day with a successful reading did the answer name you (for a named win) or link to you (for a linked win). Every earlier day is checked, not only the last three.
  


Answers for the same prompt from different surfaces or countries on the same day are one day here. The question is only whether you were in the answer.

### Failed reads never count

A day counts only if at least one reading of the prompt on that engine succeeded. A day of failed reads and a day with no reading at all are the same to this pass: silence. A failed day cannot be one of the three earlier days, and it cannot be the day you were named.

### What is never announced

* **Names from before detection started.** The first pass on a workspace records everything you are already named or linked for as a baseline and announces nothing. Turning detection on does not announce months-old news as a first.
* **A naming we could not call a first.** If an engine named you on a prompt before we had three successful earlier days of reading it, or had named you on it before, that (prompt, engine, kind) is recorded as a baseline. It is never announced and never becomes a win later.

Baselines are never listed, shared or counted as wins.

## The win message

When the pass finds new wins, you get **at most one message per workspace per day** in your workspace's time zone, listing every win not told yet. A win found after that day's message went out is in the next day's message.

Each win is told in one sentence:

> ChatGPT named Acme for the first time since we started reading this prompt on 2 September 2026, in an answer to 'best dental software' on 28 September 2026.

"Since we started reading" is the first day we have a successful reading of that prompt on that engine. A linked win says "linked to acme.com" instead of "named Acme".

Under the sentence:

* **The quote.** The engine's own words, verbatim, cut from the answer we read: at most 400 characters around the first mention of your brand (or the cited link, for a linked win). When the quote is cut from a longer answer, it is shown with "..." at both ends. A quote is never edited or summarised. If no answer that day yields a quote that contains the mention and passes our honesty check, the win has no quote rather than a tidied one.
* **Related work**, only when it is true. If a piece of work that lists this prompt went live in the 90 days before the win, the message adds "Work on this prompt: *title*, live since *date*." as a separate line. It does not claim the work caused the win, and work that is not live yet is never mentioned.

### Where it goes

The message goes by email by default. Under **Settings → Notifications → Wins** you can send it to Slack instead, or to both; Slack posts to the same channel as the Monday note. See [Notifications](/account/notifications).

The win email is its own notification category, so you can unsubscribe from it without turning off anything else.

## The win card in the dashboard

The newest win nobody in your workspace has seen yet appears as a card at the top of **Home**, and at the end of every project's feed. Most visits have none, and then nothing is drawn.

* **Shown once.** The card counts as seen as soon as it is on screen, so it does not come back on your next visit, on Home or in a project.
* **Share in one tap.** The card carries LinkedIn, Slack and **Copy link**. Slack posts the card to the workspace's Slack channel; see [Sharing a win card](#sharing-a-win-card) for what the link is.
* **Close** takes the card off the screen. The win stays in your list of wins, and its link, if it has one, keeps working. **Dismiss** puts the win away for good and, when it has a link, turns that link off in the same step. The button says **Dismiss and turn off the link** when there is one.

### The referral offer

After you share a win, and in the card's place when you close or dismiss it, you may see **Give a month, get a month**: your referral link to send to a team that should be in AI answers too. When they start a plan with it, you each get a month. It appears only after a win, and only when your organisation is eligible; **Not now** hides it for the rest of that visit. See [Referrals](/account/referrals) for who qualifies, the link and the rules.

## Sharing a win card

A win card shows the quote, the engine, the prompt and the date. Sharing it to LinkedIn or copying its link makes a **public link** to the card.

* **One win, one link.** The first share makes the link and every later share reuses it, so a LinkedIn post and a copied link open the same card. On LinkedIn, the link is opened in LinkedIn's own share box; Attensira does not post anything for you.
* **Permanent until you turn it off.** The link has no expiry of its own. It stops working when you **unshare** the win or **dismiss** it. Dismissing puts the win away and turns off its link in the same step.
* **A turned-off link stays off.** Sharing the win again makes a new link. The old one never works again.
* A dismissed win cannot be shared.

### What a stranger sees

The person who opens the link needs no account. The page shows only your brand, your domain, your logo (when your brand profile has one), the engine, the prompt, the quote and the date. It shows no ids, no counts, nothing else from your workspace, and no Attensira branding, so an agency can share a client's card as it is.

A link that was turned off, or was never a win card, shows "This card is not available".

## The `win_detected` event

Every win is also recorded as one `win_detected` event, with the win's id as its subject. Its payload carries:

| Field | What it is |
| - | - |
| `win_id` | The win |
| `prompt_id`, `prompt_text` | The prompt |
| `platform` | The engine |
| `kind` | `named` or `linked` |
| `slot_day` | The day of the answer that named you, in your workspace's time zone |

Today no automation you create reacts to it. `win_detected` is not one of the event types you can pick in the dashboard, over MCP or over REST, and each of those asks for at least one type, so a new [event automation](/agent/autopilot#event-automations) never listens for it. The built-in **Event responder** does not listen for it either. Only an automation saved with an empty event list would, because an empty list means every event type.

## Reading your wins

* **Over MCP**, the `list_wins` [tool](/mcp/tools) returns your wins newest first, each with the same sentence the message uses, the quote, the date, "since we started reading" and the share link when there is one.
* **In the [morning brief](/agent/morning-brief)**, the Monday edition counts the week's wins.
* **In the dashboard**, the newest unseen win is a card on Home and in project feeds. See [The win card in the dashboard](#the-win-card-in-the-dashboard).

Dismissed wins are left out of the list.

## Who has it

Wins run on every workspace with hourly [detection](/agent/findings#who-has-it). Paid workspaces are enrolled automatically, the first time their subscription is active.

If the pass fails for a workspace, the failure shows on [loop health](/agent/loop-health) as **First-naming detector**, and the next hour's pass tries again. If the day's message could not be queued on any channel you chose, its wins stay stored and untold, and the next hour's pass tries again; a win is never lost.


# Authenticating REST API requests
Source: https://docs.attensira.com/api/authentication

Send a workspace-scoped API key as a bearer token. The key names the workspace, so no request can be pointed at someone else's.

Every REST API request except `/healthz` needs a workspace-scoped API key, sent as a bearer token.

```bash Shell theme={null}
curl https://api.attensira.com/v1/analytics/visibility \
  -H "Authorization: Bearer atn_live_"
```

It is the same key and the same header as the [MCP server](/mcp/connect). One key reaches both surfaces; you do not need a second credential to add the other.

The REST API takes a key and nothing else. OAuth access tokens belong to the [MCP server](/mcp/oauth), where a client can open a browser; an API called by a script should not need a consent screen, and a key is the one credential that works where nothing can open a browser at all.

## The key

Mint a key under **Settings → Your assistant** at [app.attensira.com/settings/access](https://app.attensira.com/settings/access). See [API keys](/account/api-keys) for minting, scopes and revocation.

The format is `atn_live_` followed by 32 hexadecimal characters:

```text Key format theme={null}
atn_live_0123456789abcdef0123456789abcdef
```


  The key is displayed exactly once, because only a hash is stored. Attensira cannot re-display it — a lost key is replaced, not recovered.


REST keys do not expire. A key stays valid until you or a workspace admin revokes it, so a script you deploy once keeps running without anyone re-authorizing it. There is no rotation deadline and no maximum age.


  Access tokens obtained through OAuth *do* expire, and the client refreshes them for you. That lifetime is a property of the token, not of this API — nothing about `/v1` times out a key.


## The credential names the organisation

A credential belongs to your organisation and names one workspace inside it as the default. Every `/v1` endpoint takes an optional `?project=`; omit it and the default is used.

No workspace id appears in a path segment or a body field — the query parameter is the only place one can be named, and it is checked against the organisation the credential belongs to. A workspace outside it comes back `404`, indistinguishable from one that does not exist, so naming an id you do not own tells you nothing.

One credential therefore covers every workspace in the organisation. `GET /v1/account` returns them in `workspaces`, alongside the resolved one in `project`.

## What a 401 means

A request with a missing, malformed or unusable key gets:

```json 401 response theme={null}
{
  "error": {
    "type": "unauthorized",
    "message": "missing or malformed Authorization header. Send: Authorization: Bearer atn_live_<32 hex>",
    "status": 401
  }
}
```

The response carries the challenge, so a client knows which scheme to retry with:

```http theme={null}
WWW-Authenticate: Bearer error="invalid_token"
```

A `401` covers four different situations deliberately, and neither the message nor the challenge distinguishes them: the header was absent or the wrong shape, the credential does not exist, it was revoked, or it expired. The challenge is one constant string for all of them — a caller who could tell them apart could probe for which credentials exist. Check the key at [app.attensira.com/settings/access](https://app.attensira.com/settings/access) rather than inferring from the response.


  A key that is real but was minted read-only gets `403`, not `401`, from an endpoint that writes: the error envelope carries `type: "forbidden"` and the header `WWW-Authenticate: Bearer error="insufficient_scope", scope="write"`. The distinction matters: a `401` is a reason to check the key, a `403` is not, and a client that reacts to one as if it were the other throws away a working key and fails identically. Keys cannot be widened after minting — mint a read-and-write key. `GET /v1/account` returns the credential's `scopes`, so that is where to look when a write fails and a read succeeds.


## How the token travels

The API service checks only the *shape* of the key at the door — the `atn_live_` prefix and 32 hex characters — and rejects anything else before it can become a round trip, so a typo never reaches the backend or its logs. That check is not authentication.

The token itself is forwarded verbatim to the Attensira backend, which owns the real validation: hash lookup, revocation, expiry, scopes and per-key rate limiting. Two consequences worth knowing:

* **Redirects are never followed.** A `3xx` is treated as the response rather than chased, because following one could re-send a live key to another host.
* **A key is never cached or mirrored.** Revoking a key at the dashboard takes effect on the next request; there is no copy of it in the API layer to go stale.

## Keeping a key safe

Send it from a server, never from a browser or a mobile app — anything shipped to a client device is public, and this key can delete prompts and spend credits.

Store it in your platform's secret store and read it from an environment variable. A key in a committed `.env`, a CI log or a screenshot should be revoked and replaced rather than reasoned about.

Mint one key per integration. Keys carry `last_used_at`, so a per-integration key is the difference between revoking one job and breaking every job you have.


# REST API endpoint reference
Source: https://docs.attensira.com/api/endpoints

Every Attensira REST endpoint — parameters, request bodies, response shapes, and which ones spend credits or delete data.

Every endpoint lives under `https://api.attensira.com/v1`. Every one takes `Authorization: Bearer atn_live_<32 hex>`, and every one also takes an optional `?project=` when the key's organisation has more than one workspace — see [authentication](/api/authentication).

Each endpoint below names the [MCP tool](/mcp/tools) it is the twin of, and answers with the same payload. The tools without a REST endpoint yet are listed under [MCP-only for now](#mcp-only-for-now) at the end of this page.

`?project=` is always the workspace, never a project. A [project](/agent/projects) is a **work area** on the wire: it is read and steered under `/v1/work-areas`, and a goal or a deliverable that points at one carries its id as `work_area_id`. Creating a project and changing its settings are in the dashboard only.

## Before you start: what costs money and what destroys data

| Endpoint | Effect | Why |
| - | - | - |
| `POST /v1/prompts` | Spends credits | Each new prompt is read across your tracked platforms from the next run onward |
| `POST /v1/automations/{id}/runs` | Spends credits | Triggers an automation immediately instead of waiting for its schedule |
| `POST /v1/ask` | Spends credits | Runs the agent on an agent model |
| `POST /v1/sessions/{id}/context` with `run: true` | Spends credits | Queues the session again to act on the new context |
| `POST /v1/inbox/tasks/{id}/answer` | Spends credits when the run that asked had finished | A finished, failed or suspended run starts a new turn on the answer |
| `POST /v1/work-areas/{id}/steer` | The reply spends credits | Posting is free; the project's crew answers the post with an ordinary agent run |
| `DELETE /v1/prompts/{prompt}` | Destructive | Stops tracking a prompt; its history goes with it |
| `DELETE /v1/competitors/{id}` | Destructive | Stops scoring a rival; past readings keep what they recorded |
| `DELETE /v1/automations/{id}` | Destructive | Removes the automation and its schedule. Past runs are kept as sessions |
| `DELETE /v1/sessions/{id}` | Destructive | Deletes a finished session and its trace |

Nothing else on this page produces a bill or erases history.

## Status codes

A `POST` that creates a durable resource answers `201`. `POST /v1/ask` and `POST /v1/automations/{id}/runs` answer `200`, because what they return is a handle to work in progress rather than a resource at a new URL. Everything else answers `200`. Failures are the [error envelope](/api/errors).

***

## Find and measure

### GET /v1/search

Read-only. No credits. MCP tool: `search`.

One search across prompts, pages, automations and sessions. Use it to locate something by wording when you do not have an id.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `q` | query | string | — | Required. Matched against prompt text, page URLs and titles, automation names and instructions, and session titles and transcripts |
| `limit` | query | integer | 20 | Maximum 50 |
| `offset` | query | integer | 0 | For paging |

```bash Shell theme={null}
curl -G https://api.attensira.com/v1/search \
  --data-urlencode "q=pricing" --data-urlencode "limit=2" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "total": 37,
  "has_more": true,
  "hits": [
    {
      "type": "prompt",
      "id": "prm_8f21",
      "title": "best crm for small teams pricing",
      "snippet": "…compared against three competitors on price…",
      "when": "2026-08-30T09:12:00Z"
    },
    { "type": "page", "id": "https://acme.com/pricing", "title": "https://acme.com/pricing", "snippet": "Pricing — Acme", "when": null }
  ]
}
```

`type` tells you which endpoint can fetch the hit in full: `prompt` → `GET /v1/prompts/{prompt}`, `page` → `GET /v1/pages`, `session` → `GET /v1/sessions/{id}`, `automation` → `GET /v1/automations/{id}`. A hit whose label starts with the query ranks above one that merely contains it; at equal footing prompts come before pages, then automations, then sessions. Answers themselves are not searched — they are read verbatim through [`GET /v1/memory/answers/history` and `GET /v1/memory/answers/slice`](#the-receipts).


  **Every string on a hit is capped at 400 characters**, not just `snippet`. A complete model answer can never come back through search, no matter which fields a hit carries. Fetch the session for the full text.


Page on `has_more`. `total` is how many items matched up to the server's fetch ceiling — a floor, not a grand total.

### GET /v1/analytics/

Read-only. No credits. MCP tool: `get_analytics`.

The main measurement endpoint. One call returns an aggregated, ranked slice of your tracking data for a window.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `area` | path | enum | — | Required. See the table below |
| `days` | query | integer | 30 | 1–365 |
| `order` | query | enum | `worst_first` | Or `best_first`. Rows nothing was measured for sort last either way |
| `limit` | query | integer | 20 | Maximum 100 |
| `filter` | query | string | — | Case-insensitive substring on row labels. For `area=platform` it names the platform and is required |

| `area` | One row per | Answers |
| - | - | - |
| `visibility` | tracked platform | Share of voice per platform, with the workspace's own rate alongside — the orienting call |
| `prompt` | tracked prompt | Which prompts you win and lose |
| `page` | page of yours | Which of your pages get cited, as a rate on every answer sampled |
| `source` | external domain | Which domains the models cite in your category, as a share of every citation |
| `competitor` | rival | Workspace-wide share of voice per competitor, with the competitor id |
| `topic` | prompt topic | Per-topic rollup; prompts under no topic are one row with id `no_topic` |
| `platform` | the platform named in `filter` | That platform end to end |
| `bot` | AI system that fetched your site | Reads, pages reached, purpose mix, when it last came |

```bash Shell theme={null}
curl -G https://api.attensira.com/v1/analytics/prompt \
  --data-urlencode "days=30" --data-urlencode "order=worst_first" --data-urlencode "limit=1" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "area": "prompt",
  "days": 30,
  "order": "worst_first",
  "filter": "",
  "total_rows": 12,
  "truncated": true,
  "visibility": { "value": 0.3, "n": 20 },
  "rows": [
    {
      "id": "prm_8f21",
      "label": "best crm for small teams",
      "topic": "buying intent",
      "country": "US",
      "visibility": { "value": 0.12, "n": 25 },
      "share_of_voice": { "value": 0.12, "n": 25 },
      "citation": { "value": 0.04, "n": 25 },
      "position": null,
      "delta": { "value": -0.05, "real": true },
      "platforms": {
        "chatgpt":    { "tracked": true, "readable": true,  "rate": { "value": 0.2, "n": 15 },
                        "delta": { "value": -0.03, "real": false } },
        "perplexity": { "tracked": true, "readable": false, "rate": { "value": null, "n": 0 } }
      },
      "answers_read": 25,
      "failures": 1
    }
  ]
}
```

`total_rows` is how many rows survived the filter and `truncated` whether `limit` cut them, so a short list can be told from a cut one. A tracked platform with no readings in the window appears as `tracked: true, readable: false` with a null rate — present, and not zero.

`area=bot` rows carry `visits`, `pages`, `purpose_mix`, `last_seen` (`null` when it never came) and `agents` instead of the rate fields. A model can only cite a page it has fetched, so check `bot` before concluding a page underperforms on content — but a row here counts only what our middleware recorded, so absence is no proof nothing came.


  `competitor` and `source` are workspace-wide aggregates. For the rivals and cited sources of **one** prompt, `GET /v1/prompts/{prompt}` already returns both — no second call needed.


### GET /v1/prompts/

Read-only. No credits. MCP tool: `get_prompt`.

Everything recorded for one prompt: its topic and country, its per-platform rates and delta, the competitors seen in its answers, and the sources those answers cited.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `prompt` | path | string | — | Required. A prompt id, **or** the prompt's exact text, percent-encoded |
| `days` | query | integer | 30 | 1–365 |

```bash Shell theme={null}
curl https://api.attensira.com/v1/prompts/prm_8f21?days=30 \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "id": "prm_8f21",
  "text": "best crm for small teams",
  "topic": "buying intent",
  "country": "US",
  "visibility": { "value": 0.12, "n": 25 },
  "citation": { "value": 0.04, "n": 25 },
  "delta": { "value": -0.05, "real": true },
  "position": 2,
  "answers_read": 25,
  "failures": 1,
  "platforms": {
    "chatgpt": { "tracked": true, "readable": true, "rate": { "value": 0.2, "n": 15 }, "citation": { "value": 0.07, "n": 15 } }
  },
  "competitors": [{ "name": "rival.com", "count": 15, "rate": { "value": 0.6, "n": 25 } }],
  "sources": [{ "url": "g2.com", "domain": "g2.com", "count": 11, "rate": { "value": 0.44, "n": 25 } }],
  "why": ["Named in 3 of 25 answers; rival.com was named in 15."]
}
```

`topic` is `null` when the prompt is not filed under one and `country` is `null` for the workspace default. Competitor and source rates are on this prompt's own `answers_read`.

Passing exact text saves a search round-trip when you already know the wording; inexact text will not match. Percent-encode it — a prompt containing `/` or `?` will otherwise change the route.

### GET /v1/pages

Read-only. No credits. MCP tool: `get_page`.

Everything recorded for one URL on your site: its citation rate overall and per platform, its delta, and the prompts whose answers cited it.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `url` | query | string | — | Required. Absolute URL, exactly as it appears on the site |
| `days` | query | integer | 30 | 1–365 |

```bash Shell theme={null}
curl -G https://api.attensira.com/v1/pages \
  --data-urlencode "url=https://acme.com/pricing" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "url": "https://acme.com/pricing",
  "title": "Pricing — Acme",
  "citations": 5,
  "total_answers": 120,
  "citation": { "value": 0.04, "n": 120 },
  "delta": { "value": null, "real": false },
  "platforms": {
    "perplexity": { "tracked": true, "readable": true, "rate": { "value": 0.08, "n": 60 }, "citations": 5 },
    "chatgpt":    { "tracked": true, "readable": true, "rate": { "value": 0, "n": 60 }, "citations": 0 }
  },
  "prompts": [{ "id": "prm_8f21", "text": "best crm for small teams", "citations": 2, "rate": { "value": 0.08, "n": 25 } }],
  "in_sitemap": true
}
```

The `delta` is always `{value: null, real: false}` here: the page fold has no earlier window to compare against, and the contract's word for that is "no basis", not a number.

If this comes back empty, check `GET /v1/analytics/bot` before rewriting the page — a model cannot cite a page it has never fetched.

### POST /v1/evidence/query

Read-only. No credits. MCP tool: `query_evidence`.

Your own data question as a table: one metric, grouped by up to two dimensions, filtered, over a window. It reads the same stored days and folds them the same way as the dashboard boards, so the numbers match what the dashboard shows for the same window.

The body is a JSON object with the same fields, values and limits as the MCP tool: `metric` (required), `group_by`, `days` or `from` and `to`, the filters `topic_ids`, `prompt_ids`, `engines`, `surface`, `country`, `markets`, `property_ids`, `competitor`, `intent`, `stage`, `state`, `domain` and `text_contains`, and `limit`. The [`query_evidence` reference](/mcp/tools#query_evidence) lists every metric with what it can be grouped and filtered by. A field the query does not know is refused rather than ignored, so a misspelt filter cannot come back as unfiltered numbers.

```bash Shell theme={null}
curl https://api.attensira.com/v1/evidence/query \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"metric": "gsc_clicks", "group_by": ["page"], "days": 28, "limit": 2}'
```

```json Response theme={null}
{
  "metric": "gsc_clicks",
  "group_by": ["page"],
  "window": { "from": "2026-09-05", "to": "2026-10-02", "days": 28 },
  "day_basis": "search_console",
  "unit": "count",
  "rows": [
    { "key": { "page": "acme.com/pricing" }, "value": 412, "n": 9310 },
    { "key": { "page": "acme.com/blog/crm-guide" }, "value": 0, "n": 220 }
  ],
  "gaps": [{ "key": "2026-09-30..2026-10-02", "reason": "not_recorded", "detail": "Search Console has not reported these days yet" }],
  "truncated": true
}
```

`value` is in `unit`, and `null` means unmeasured, never 0: the row's gap says why. `n` is on every row; for Search Console it is the impressions behind the row. `gaps` names everything the answer could not measure, and an empty list is the claim that nothing is missing. `day_basis` says which calendar the day labels count in (`slot_day`, `search_console` or `utc`; see [Google search](/measure/google-search#day-basis)).

A question with no honest answer — an unknown metric, a metric grouped or filtered in a way it does not support, a `from`–`to` span over 365 days, an id the workspace does not have — is a `400` with `type: "invalid_request"` and a `message` that names the problem and what to ask instead. A question too broad to answer in time is a `422` whose `message` says what to narrow. `days` and `limit` outside their ranges are clamped to the nearest bound rather than refused.

### GET /v1/deciding-sources

Read-only. No credits. MCP tool: `deciding_sources`.

Which pages and domains decided the AI answers, whether you already appear on each, and the next move for each. `GET /v1/analytics/source` ranks by citations; this ranks by influence over a decision. See [Deciding sources](/measure/deciding-sources).

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `prompt` | query | string | — | Optional. One tracked prompt, by id or exact text. Omit for every prompt |
| `days` | query | integer | 30 | 1–365 |

Returns `groups`, most influential first, one per prompt, platform and surface. Each carries a `label`, `n`, `present`, a `recommendation`, and `sources` ranked by weight, each with `domain`, `url`, `citations`, `share`, `present` and `action`. The full row is described under [`deciding_sources`](/mcp/tools#deciding_sources).

### GET /v1/win-plan

Read-only. No credits. MCP tool: `win_plan`.

The ordered plan for winning one tracked prompt. See [Win this answer](/measure/win-this-answer).

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `prompt` | query | string | — | Required. Prompt id or exact text |
| `days` | query | integer | 30 | 1–365 |

Returns `prompt`, `n`, `evidence`, the `steps` in order, `rereads_at` and `credits_estimate`, as described under [`win_plan`](/mcp/tools#win_plan).

***

## The receipts

The numbers above are folds over stored answers. These endpoints hand back the rows themselves, verbatim. See [Deep reads](/agent/deep-read).

### GET /v1/memory/answers/history

Read-only. No credits. MCP tool: `answer_history`.

Every sampled answer to one tracked prompt, verbatim and newest first, with citations and reading ids.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `prompt` | query | string | — | Required. The prompt id |
| `platform` | query | string | — | One platform; omit for all |
| `days` | query | integer | 28 | 1–365 |
| `limit` | query | integer | 10 | Readings per page, 1–50 |
| `cursor` | query | string | — | `next_cursor` from the previous page |

Returns `answers`, `readings`, `has_more` and `next_cursor`. An answer over 8 KB is cut and carries `truncated: true`, with `text_bytes` the full length. A failed sample is listed with `failed: true`, never dropped. The row shape is described under [`answer_history`](/mcp/tools#answer_history).

### GET /v1/memory/answers/slice

Read-only. No credits. MCP tool: `answer_slice`.

Every answer matching a predicate, verbatim. Terms AND together and select the sample; at least one is required.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `platform` | query | string | — | Repeat for several |
| `country` | query | string | — | ISO 3166-1 alpha-2 |
| `topic_id` | query | string | — | The prompts filed under this topic now |
| `prompt_ids` | query | string | — | Repeat, or comma-separate, for several; at most 100 |
| `surface` | query | enum | — | `consumer` or `api` |
| `model` | query | string | — | The start of the model's name, case-insensitive |
| `competitor` | query | string | — | A rival's domain the answer named or linked |
| `cited_domain` | query | string | — | A domain among the citations; subdomains match |
| `brand_named` | query | boolean | — | `true` or `false` |
| `sentiment` | query | enum | — | `positive`, `neutral`, `negative`, `none` (scored, no stance) or `unscored` (never scored) |
| `brand_position_min`, `brand_position_max` | query | integer | — | Your brand's place in the answer's order of brands, 1 for first, inclusive |
| `text_contains` | query | string | — | A phrase in the answer's text, literal and case-insensitive, at most 80 characters |
| `from`, `to` | query | string | — | `YYYY-MM-DD`, inclusive |
| `days` | query | integer | 28 | 1–365, when `from` and `to` are omitted |
| `limit`, `cursor` | query | | | As for answer history |

```bash Shell theme={null}
curl -G https://api.attensira.com/v1/memory/answers/slice \
  --data-urlencode "topic_id=top_41a2" --data-urlencode "sentiment=negative" \
  --data-urlencode "brand_position_min=3" --data-urlencode "days=60" \
  -H "Authorization: Bearer atn_live_"
```

The answer terms (`surface`, `model`, `competitor`, `cited_domain`, `brand_named`, `sentiment`, the brand positions and `text_contains`) must all hold for one sample, and only matching samples come back. Without `topic_id` or `prompt_ids`, `text_contains` covers at most 30 days; a wider window is a `400` saying what to narrow. The full semantics are in the [`answer_slice` reference](/mcp/tools#answer_slice).

### GET /v1/memory/citations/timeline

Read-only. No credits. MCP tool: `citation_timeline`.

One domain's or one page's citations per day, per platform.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `domain` | query | string | — | A site, e.g. `g2.com`. Or pass `url` for one page |
| `url` | query | string | — | One page's URL, instead of `domain` |
| `days` | query | integer | 28 | 1–365 |

Returns `domain`, `url`, `total`, `prompts` and `series`, one entry per platform, each with `points` for every day of the window: `day`, `citations`, `mean_position` (`null` when nothing that day carried a position) and `prompts`. A zero day is a real zero.

### GET /v1/memory/pages/history

Read-only. No credits. MCP tool: `page_history`.

One page's versions: every version captured, when, and what changed between them. It covers your own pages and the cited and competitor pages the workspace watches.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `url` | query | string | — | Required. Absolute: one of your own pages, or a cited or competitor page the workspace watches |

Metadata on a version taken before it was recorded reads `not_captured`, and is never reported as a change. Any other page on another domain is a `404`.

Returns `url`, `known`, and `versions` oldest first, each with what changed since the one before. The row shape is described under [`page_history`](/mcp/tools#page_history).

***

## Track

### POST /v1/prompts

Spends credits. Answers `201`. MCP tool: `add_prompts`.

Starts tracking one or more prompts. From the next daily run, each is read across every platform your plan includes.

| Field | Type | Notes |
| - | - | - |
| `prompts` | string\[] | Required. One or more prompt texts, none blank, at most 50 per call, each at most 500 characters |
| `topic` | string | Optional. Created if it does not exist |
| `country` | string | Optional ISO 3166-1 alpha-2 code, e.g. `US`. Defaults to the workspace's country |

There is no `platform` field. Which surfaces a prompt runs on comes from your plan, not from the request.

```bash Shell theme={null}
curl -X POST https://api.attensira.com/v1/prompts \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"prompts":["best crm for small teams","crm with free tier"],"topic":"buying intent"}'
```

```json 201 Response theme={null}
{
  "added": [
    { "id": "prm_8f21", "text": "best crm for small teams" },
    { "id": "prm_2c07", "text": "crm with free tier" }
  ],
  "skipped": [],
  "credits_spent": null,
  "credits_remaining": null
}
```

A prompt that was already tracked comes back in `added` with its existing id, so a repeated call is idempotent all the way out; `skipped` is empty today. `credits_spent` and `credits_remaining` are `null` because tracking is charged as the prompts are read, not on this call. Your plan caps how many prompts you can hold at once, and a paste that would exceed it is refused with a `409` naming how many slots remain — see [Plans](/account/plans).

### DELETE /v1/prompts/

Destructive. MCP tool: `remove_prompt`.

Permanently stops tracking a prompt and frees its slot. Its historical data goes with it and this cannot be undone — fetch it first if you want it.

| Parameter | In | Type | Notes |
| - | - | - | - |
| `prompt` | path | string | Required. Prompt id |

```json Response theme={null}
{ "id": "prm_8f21", "removed": true }
```

### POST /v1/competitors

Answers `201`. No credits. MCP tool: `add_competitor`.

Registers a brand to detect alongside yours in answers already being collected. It creates no new readings, so it costs nothing.

| Field | Type | Notes |
| - | - | - |
| `domain` | string | Required. Bare hostname, e.g. `rival.com` — no scheme or path |
| `name` | string | Optional display name. Defaults to the domain |

```json 201 Response theme={null}
{ "id": "cmp_10b3", "domain": "rival.com", "name": "Rival" }
```

Competitor rates use the same denominator as your own, so adding one never changes your numbers.

### DELETE /v1/competitors/

Destructive. MCP tool: `remove_competitor`.

Stops scoring a rival. Past readings keep what they already recorded, so history does not move; the rival simply stops being scored from here on.

| Parameter | In | Type | Notes |
| - | - | - | - |
| `id` | path | string | Required. Competitor id, from `GET /v1/analytics/competitor` |

```json Response theme={null}
{ "id": "cmp_10b3", "tracked": false }
```

***

## Automations

### GET /v1/automations

Read-only. No credits. MCP tool: `list_automations`.

Lists every automation with its cadence and last run. No parameters.

```json Response theme={null}
{
  "automations": [
    {
      "id": "aut_71cd",
      "name": "Monday visibility report",
      "instruction": "summarise last week's visibility and flag anything that moved",
      "trigger": "weekly",
      "tools": [],
      "status": "active",
      "timezone": "Europe/London",
      "run_at_minute": 540,
      "last_run_at": "2026-09-01T08:00:00Z"
    }
  ]
}
```

`status` is `active` (runs on its trigger), `paused` (does not), or `done` (a `once` trigger that already fired). `tools: []` means the default read-only set.

Paused and retired automations are always listed. One the product paused on its own carries `retired_reason` and `retired_at`. `next_run_at` is `null` for an event automation.

### GET /v1/automations/

Read-only. No credits. MCP tool: `get_automation`.

One automation in full, with its most recent run inline — so "did my Monday report run?" costs one call, not two.

```json Response theme={null}
{
  "automation": { "id": "aut_71cd", "name": "Monday visibility report", "trigger": "weekly", "status": "active" },
  "latest_run": { "id": "ses_9d02", "status": "complete" }
}
```

`latest_run` is `null` when the automation has never run. Pass its `id` to `GET /v1/sessions/{id}` for the full trace.

### POST /v1/automations

Answers `201`. Creating is free; each run it performs spends credits. MCP tool: `create_automation`.

| Field | Type | Default | Notes |
| - | - | - | - |
| `name` | string | — | Required |
| `instruction` | string | — | Required. What it should do each run, in plain language |
| `trigger` | enum | `daily` | `daily`, `weekly`, `monthly`, `quarterly`, `event`, `manual` or `once`. `monthly` and `quarterly` fire on the 1st. `manual` and `once` run only through `POST /v1/automations/{id}/runs`, and `once` then retires |
| `tools` | string\[] | — | Optional allow-list of tool names it may use. Omit to allow everything the plan includes |
| `timezone` | string | The workspace's | Optional IANA zone the schedule is written in, e.g. `Europe/London`. UTC only if the workspace has none |
| `run_at_minute` | integer | 540 | Minutes past local midnight, 0–1439. `540` is 09:00. Only meaningful for the clock triggers: `daily`, `weekly`, `monthly` and `quarterly` |
| `run_on_weekday` | integer | — | For `weekly`: 0 = Sunday to 6 = Saturday |
| `event_types` | string\[] | — | Required and non-empty when `trigger` is `event`; valid only then. Any of `prompt_lost`, `citation_lost`, `new_deciding_source`, `competitor_changed`, `page_changed`. See [Event automations](/agent/autopilot#event-automations) |

```bash Shell theme={null}
curl -X POST https://api.attensira.com/v1/automations \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"name":"Monday visibility report","instruction":"summarise last week","trigger":"weekly","timezone":"Europe/London","run_at_minute":540}'
```

Returns the created automation in the shape shown under `GET /v1/automations`.

```bash Shell — an event automation theme={null}
curl -X POST https://api.attensira.com/v1/automations \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"name":"React to lost citations","instruction":"For each lost citation, propose one fix","trigger":"event","event_types":["citation_lost","prompt_lost"]}'
```

### PATCH /v1/automations/

No credits. MCP tool: `update_automation`.

A partial edit. A field you leave out is left alone, so pausing an automation cannot quietly rewrite what it does. Takes `name`, `instruction`, `trigger`, `event_types`, `tools`, `status` (`active` or `paused`), `timezone`, `run_at_minute` and `run_on_weekday`, with the same rules as `POST /v1/automations`. `event_types` is valid only when the trigger is `event`.

Returns the automation in the shape shown under `GET /v1/automations`.

### POST /v1/automations//runs

Spends credits. Answers `200`. MCP tool: `run_automation`.

Runs an automation immediately, outside its cadence. Returns a session handle, not a finished result.

| Parameter | In | Type | Notes |
| - | - | - | - |
| `id` | path | string | Required |
| `Idempotency-Key` | header | string | Strongly recommended. A repeat call with the same key does not start — or bill — a second run |

```bash Shell theme={null}
curl -X POST https://api.attensira.com/v1/automations/aut_71cd/runs \
  -H "Authorization: Bearer atn_live_" \
  -H "Idempotency-Key: 2026-09-02-monday-report"
```

```json Response theme={null}
{ "session_id": "ses_9d02", "status": "running" }
```

Poll `GET /v1/sessions/{id}`. Re-sending this `POST` without an `Idempotency-Key` starts a second billed run.

### DELETE /v1/automations/

Destructive. MCP tool: `delete_automation`.

Permanently deletes an automation and its schedule. This cannot be undone — confirm with `GET /v1/automations/{id}` first. Its past runs are kept as sessions, readable through `GET /v1/sessions`.

```json Response theme={null}
{ "id": "aut_71cd", "deleted": true }
```

***

## Inbox

Everything waiting on a person, and every act the dashboard's [Inbox](/agent/inbox) offers on it. Each act goes through the same path as a click in the dashboard, so a yes or an answer means the same thing wherever it was given, and the action log records your key as the one who gave it.

Each row's `type` says which endpoint acts on it, and its `ask_kind` (`approve`, `answer`, `do` or `info`) says what it asks of a person:

| Row | `ask_kind` | Act on it with |
| - | - | - |
| `work_item` | `approve` | `POST /v1/inbox/{id}/approve` (with an edit, for an article, post or rewrite) or `/decline` |
| `work_item` | `do` | Nothing to approve: a copy handoff or an outreach draft the person sends themselves |
| `ask` | `approve` | `POST /v1/inbox/asks/{ask_id}/approve` or `/decline` |
| `ask` | `answer` | Answered in its own session, not here |
| `task` | `answer` | `POST /v1/inbox/tasks/{id}/answer` |
| `task` | `do` | `POST /v1/inbox/tasks/{id}/resolve`, once the person has done it |

Every write needs a read-and-write key; a read key gets `403`. Approve, answer and decline only on the person's instruction. Every act returns `next_item_ref` when something else is waiting: the row the ranking puts first now, readable with `GET /v1/inbox/items/{ref}`.

### GET /v1/inbox

Read-only. No credits. MCP tool: `list_inbox`.

What is waiting on a person: approvals the agent stopped for, questions, work it could not finish alone, failed automations, and approvals that expired unanswered.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `category` | query | string | `needs_you` | `needs_you` (waiting on a person) or `in_flight` (approved and running, and Done rows still being checked) |
| `view` | query | string | `list` | `list` returns every row, newest first, under `items`. `triage` returns the `needs_you` rows as decisions under `triage`, and is `400` with `category=in_flight` |
| `limit` | query | integer | 3 | `view=triage` only: how many decisions are ranked first. Held to 1–20, so a larger number is read as 20; `400` with `view=list` |

Each row in `items` carries `type` (`work_item`, `ask` or `task`), `id`, `ref`, `ask_kind`, `title`, `reason`, `session_id` and `updated_at`, plus `ask_id` on an ask. A task row also carries what it asks: `task.question` (`prompt`, up to six `options`, and `fact_ref` when it fills a profile field), `task.steps`, and `task.verification` while a Done is being checked. `list_approvable` is `true` only on a proposed change that may be approved without opening it: one whose effect can be undone and that is classed safe.

```bash Shell theme={null}
curl "https://api.attensira.com/v1/inbox?view=triage" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "triage": {
    "top": [
      {
        "key": "task:missing_integration:github",
        "ask_kind": "do",
        "label": "Connect GitHub · 7 waiting",
        "count": 7,
        "item_refs": ["t_66f1a2", "t_66f1b9", "…"],
        "oldest_waiting_since": "2026-09-28T07:12:00Z",
        "lead": { "type": "task", "ref": "t_66f1a2", "title": "Connect GitHub", "…": "…" }
      }
    ],
    "sections": [
      { "ask_kind": "answer", "label": "2 more questions", "count": 2, "decisions": ["…"] }
    ],
    "total_open": 12,
    "decisions": 5,
    "merged_count": 9
  }
}
```

The triage is the Inbox's own ranking, the same one the dashboard, the morning brief and the daily email read. Rows that wait on the same thing fold into one decision, and `top` holds the first `limit` of them; the rest are in `sections`, grouped by `ask_kind`. `total_open` counts rows, `decisions` counts decisions, and `merged_count` is how many sightings are not shown as rows of their own. See [At most three decisions first](/agent/inbox#at-most-three-decisions-first) for the order.

### GET /v1/inbox/items/

Read-only. No credits. MCP tool: `get_inbox_item`.

One row by its ref, in any state, in full: the whole proposal a work item would make, the question a task asks and its answer once given, a done row as done.

| Parameter | In | Type | Notes |
| - | - | - | - |
| `ref` | path | string | Required. A row's `ref`, or any `next_item_ref`: `t_`, `w_` or `a__` |

To approve a specific version, copy `work_item.change.proposal.captured_at` from here into the approve body. A row the duplicate check folded into another carries `merged_into`, the ref of the row to read instead; one it only suspected carries `possible_duplicate_of` and is not hidden. A malformed ref is `400`; a ref from another workspace is `404`, the same as one that names nothing.

### POST /v1/inbox/tasks//answer

Spends credits when the run that asked had already finished. MCP tool: `answer_inbox_question`.

Answers a task whose `ask_kind` is `answer` with the person's words. The answer is saved to Company facts — the business profile field the question names, or a stated fact every later run reads — the row closes, and the run that asked is put back to work.

| Field | Type | Notes |
| - | - | - |
| `text` | string | Free text, at most 2,000 characters |
| `option` | string | One of the row's `task.question.options`, instead of `text` |

```json Request theme={null}
{ "text": "The launch is on 14 October." }
```

```json Response theme={null}
{
  "task": { "id": "66f1c0", "state": "done", "…": "…" },
  "fact": { "kind": "stated_fact", "ref": "stated:66f1c3" },
  "resume": { "status": "resumed", "session_id": "ses_9d02" },
  "next_item_ref": "w_66f0aa"
}
```

`resume.status` is `resumed` (a running run reads the answer from its next step; a finished, failed or suspended one starts a new turn on it, which spends credits), `queued_after_approval` (the run reads it once its pending approval is answered), `not_started` (the run was cancelled, or no run asked) or `failed` (the answer is saved, but the run could not be handed it; retry from the row's page in the dashboard). A second answer to the same row is `409`; a row that is not a question is `400`.

### POST /v1/inbox/tasks//resolve

No credits. MCP tool: `resolve_inbox_task`.

Closes a task the agent raised.

| Field | Type | Notes |
| - | - | - |
| `state` | string | Required. `done` (it has actually been handled), `dismissed` (it does not matter) or `snoozed` |
| `snoozed_until` | string | RFC 3339. Required when `state` is `snoozed`, refused otherwise |

A task that carries a check (a DNS record, a connection, a line on a page) is not closed by `done`: it stays open with `verification.status: "pending"`, moves to `in_flight`, and closes on its own when the check passes. If the check still fails after its retries, the row comes back to `needs_you` with `verification.failure` saying why; sending `done` again checks again. Snoozing a row while its check is pending is `409`. Returns the task and `next_item_ref`.

### POST /v1/inbox//approve

No credits. MCP tool: `approve_work_item`.

Approves a work item and lets the agent carry the change out. The body is optional; no body is a plain yes.

| Field | Type | Notes |
| - | - | - |
| `expected_captured_at` | string | RFC 3339. The `work_item.change.proposal.captured_at` the person read. A proposal revised since is `409`; read the row again with `GET /v1/inbox/items/{ref}` for the current version, and show it to the person before approving that |
| `edited_value` | string | The person's full edit, at most 200 KB. Approves their version instead of the agent's |

Idempotent: approving twice approves once, so a retry after a timeout is safe. Returns the work item and `next_item_ref`.

`edited_value` is accepted only for an article, a LinkedIn post or a rewritten passage that is still waiting, and is `400` for anything else (a fix is edited in its pull request). The edited change keeps the agent's original, and is always a claim: it waits for a person in every approval mode and never goes out on its own. An edit that adds or changes code is `422`, and nothing is approved. An edit is checked against the workspace's rules like any draft; one that breaks a rule is `409`.

### POST /v1/inbox//decline

No credits. MCP tool: `decline_work_item`.

Declines a work item, with the reason why.

| Field | Type | Notes |
| - | - | - |
| `reason` | string | Required. `wrong`, `not_now`, `already_done` or `never_touch` |
| `note` | string | The person's words, at most 1,000 characters. Required with `wrong` |

The reason is what stops the same proposal coming back; the reason and note are kept on the item and in its activity. Returns the work item and `next_item_ref`.

### POST /v1/inbox/asks//approve

No credits for the call; the run it lets go carries on as before. MCP tool: `approve_ask`.

Allows a paused run, once, the exact call it stopped to ask about. `{id}` is the row's `ask_id`.

| Field | Type | Notes |
| - | - | - |
| `session_id` | string | Required. The row's `session_id`: an ask id is unique only within its session |

```json Response theme={null}
{
  "ref": "a_66f19d02_ask_66f19d02_3",
  "session_id": "66f19d02",
  "ask_id": "ask_66f19d02_3",
  "outcome": "allowed-once",
  "next_item_ref": "t_66f1a2"
}
```

An ask is never edited: the yes is bound to the exact arguments the run asked about, so `edited_value` is `400`. The run is back on the queue and has not made the call yet; poll `GET /v1/sessions/{id}`. A session in another workspace is `404`; an ask already answered, expired, or no longer the one the run is waiting on is `409`.

### POST /v1/inbox/asks//decline

No credits. MCP tool: `decline_ask`.

Refuses the call a paused run asked for. The run reads the reason and note first, then carries on without the call.

| Field | Type | Notes |
| - | - | - |
| `session_id` | string | Required. The row's `session_id` |
| `reason` | string | Required. `wrong`, `not_now`, `already_done` or `never_touch` |
| `note` | string | The person's words, at most 1,000 characters. Required with `wrong` |

Returns `outcome: "rejected"` in the same shape as approve. Same `404` and `409`.

***

## Agent

### POST /v1/ask

Spends credits. Answers `200`. MCP tool: `ask`.

Open-ended investigation the measurement endpoints cannot answer — why a prompt is losing, what to change on a page — run by the Attensira agent, which crawls pages, audits them as a model sees them, and searches the web.

Try a measurement endpoint first: `/v1/analytics/{area}` for any metric or ranked list, `/v1/prompts/{prompt}` for one named prompt, `/v1/pages` for one URL. Those are free and immediate; this costs credits and takes time.

| Field | Type | Notes |
| - | - | - |
| `question` | string | Required. In plain language |
| `session` | string | Optional session id from a previous ask, to continue that thread |

```json Response theme={null}
{
  "session_id": "ses_9d02",
  "status": "running",
  "answer": null,
  "poll_after_ms": 4000
}
```

`status` is `complete` (the answer is present), `running` (poll the session after `poll_after_ms`) or `failed` (the run ended without an answer). A `running` response is the success case, not an error.


  The agent runs on an **agent model** — the LLM powering the chat. That is a different thing from a tracked **platform**, which is the answer surface Attensira reads on your behalf. Changing your agent model does not change what is measured.


### GET /v1/sessions/

Read-only. No credits. MCP tool: `get_session`.

Fetches a session started by an ask or an automation run: its status and, once finished, its answer. This is how you poll async work.

| Parameter | In | Type | Notes |
| - | - | - | - |
| `id` | path | string | Required |
| `include` | query | string | Optional. `events` returns the step-by-step trace. The only supported value; repeat the parameter or comma-separate |

```bash Shell theme={null}
curl "https://api.attensira.com/v1/sessions/ses_9d02?include=events" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "id": "ses_9d02",
  "status": "complete",
  "state": "completed",
  "answer": "Your pricing page is cited on Perplexity but not on ChatGPT, because…",
  "events": [
    { "type": "tool.start", "seq": 4, "at": "2026-09-02T08:00:04Z", "name": "get_page", "callId": "c1" }
  ]
}
```

`status` is the three-word vocabulary above; `state` is the engine's own finer word, so a run parked on a question for you reads `awaiting_person` under a `running` status. The `events` trace shows which tools the agent called and with what arguments — the place to look when an answer seems wrong and you want to know which slice it read. `answer` is the agent's last message and is filled in for a finished session whether or not the trace was asked for.

### Listing, stopping and feeding sessions

`GET /v1/sessions`, `POST /v1/sessions/{id}/cancel`, `DELETE /v1/sessions/{id}` and `POST /v1/sessions/{id}/context` are on their own page: [Sessions](/api/sessions).

***

## Loop health

### GET /v1/loop-health

Read-only. No credits. Works on a spent balance. MCP tool: `get_loop_health`.

Whether the workspace's autopilot loop is working, idle or blocked, and why, with a day-by-day record. Call it first when nothing seems to be happening. See [Loop health](/agent/loop-health).

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `days` | query | integer | 7 | 1–30 |

```json Response theme={null}
{
  "status": "idle",
  "reasons": ["2 piece(s) of work are waiting for your approval."],
  "days": [
    {
      "date": "2026-09-22",
      "readings": { "chatgpt": { "ok": 40, "failed": 1 } },
      "runs": [{ "automation_id": "aut_71cd", "name": "Daily win plan", "status": "fired", "reason": "", "count": 1, "at": "2026-09-22T06:00:00Z" }],
      "findings": 1,
      "work_produced": 0,
      "approvals_pending": 2,
      "prs_opened": 0,
      "rereads_due": 0
    }
  ]
}
```

`status` is `working`, `idle` or `blocked`. Each `runs` entry has `status` `fired`, `skipped` or `failed`, and a skip carries a `reason`: `no_credits`, `manual_trigger`, `parked_on_ask`, `flag_disabled`, `retired`, `paused`, `cap_reached` or `in_flight`. Every count is read once for its date and is not a trend.

***

## Account

### GET /v1/account

Read-only. No credits. MCP tool: `get_account`.

The org, every workspace the credential can reach, the one this call resolved to, the credit balance, which platforms that workspace tracks, and what this credential may do. Takes an optional `project`.

```json Response theme={null}
{
  "org": { "id": "org_3a10" },
  "project": { "id": "prj_5b22", "name": "Acme", "domain": "acme.com" },
  "workspaces": [
    { "id": "prj_5b22", "name": "Acme", "domain": "acme.com" },
    { "id": "prj_7c41", "name": "Beta", "domain": "beta.example" }
  ],
  "credits": { "available": 4200, "available_usd": 42.0, "held_units": 0 },
  "scopes": ["read", "write"],
  "platforms": ["chatgpt", "perplexity"]
}
```

Call this before reporting any per-platform number: `platforms` is the authority on which surfaces were queried at all, so a platform missing from it was never measured rather than measured at zero. They belong to the resolved workspace, so the array is empty whenever `project` is `null`.

`project` is `null` when the organisation has several workspaces and the request named none. That is the answer, not an error — `workspaces` carries the ids, and every other endpoint then needs an explicit `?project=`. This is the only endpoint that answers without one, which is why the "several workspaces" error elsewhere points here.

An endpoint that fails with `403` is missing a scope listed in `scopes`.

### GET /v1/costs

Read-only. No credits. Works on a spent balance. MCP tool: `get_costs`.

What a UTC month has cost, in US dollars: the total, a month-end forecast, the spend by project, workspace, kind and deliverable type, and the work the budget committed us to against what was delivered. The same figures as the [Costs](/account/costs) page.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `period` | query | string | this month | `YYYY-MM`, a UTC month that has started. A malformed or future month is a `400` |
| `project` | query | string | — | A workspace id, to narrow the breakdowns to that workspace. **Omit for the whole organisation.** An id outside the organisation is a `404` |

```bash Shell theme={null}
curl "https://api.attensira.com/v1/costs?period=2026-09" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "period": "2026-09",
  "currency": "usd",
  "as_of": "2026-09-18T09:12:04Z",
  "workspace_id": null,
  "spent_usd": 642.5,
  "forecast_usd": 1108.82,
  "by_work_area": [
    { "workspace_id": "prj_5b22", "work_area_id": "wa_19ac", "name": "Pricing pages", "spent_usd": 410.0 },
    { "workspace_id": "prj_5b22", "work_area_id": null, "name": "Unassigned", "spent_usd": 232.5 }
  ],
  "by_workspace": [{ "workspace_id": "prj_5b22", "name": "Acme", "spent_usd": 642.5 }],
  "by_kind": [
    { "kind": "agent_work", "spent_usd": 598.1 },
    { "kind": "data", "spent_usd": 44.4 },
    { "kind": "paid_placement", "spent_usd": 0 },
    { "kind": "creators", "spent_usd": 0 }
  ],
  "by_type": [
    { "type": "article", "delivered": 2, "spent_usd": 371.2 },
    { "type": "page_fix", "delivered": 8, "spent_usd": 160.4 },
    { "type": "monitoring_day", "delivered": null, "spent_usd": 44.4 },
    { "type": "other", "delivered": null, "spent_usd": 66.5 }
  ],
  "committed": [
    { "type": "article", "committed": 4, "delivered": 2, "waiting_on_you": 1, "shortfall": 1, "credit_usd": 0 },
    { "type": "page_fix", "committed": 12, "delivered": 8, "waiting_on_you": 0, "shortfall": 4, "credit_usd": 0 },
    { "type": "listing_fix", "committed": null, "delivered": 0, "waiting_on_you": 0, "shortfall": 0, "credit_usd": 0 },
    { "type": "outreach_pitch", "committed": 20, "delivered": 0, "waiting_on_you": 0, "shortfall": 20, "credit_usd": 0 }
  ],
  "credited_back_usd": 0,
  "ad_spend": { "usd": null, "source": null, "note": "Not connected. Ad spend is paid to the ad platform directly and is never part of your budget." }
}
```

* `spent_usd` is the month's spend less refunds, and every breakdown adds up to it. Ad spend is never part of it.
* `forecast_usd` is spend divided by the days elapsed, times the days in the month: `null` for the first three days of a month, and what the month cost once it is over.
* A `null` `work_area_id` is a workspace's unassigned spend: chats and the Chief of staff. An empty `workspace_id` in `by_workspace`, named `Across workspaces`, is spend that belongs to the organisation rather than one workspace.
* `by_kind` always lists `agent_work`, `data`, `paid_placement` and `creators`; nothing is charged under the last two today. `unattributed` appears only when a charge could not be placed.
* `delivered` in `by_type`, and `committed` in `committed`, are `null` for a type we do not count. `null` is never `0`.
* `committed` and `credited_back_usd` are the organisation's even when `project` names a workspace. `credit_usd` is the credit for undelivered work: what the close credited, or for an open month what it would credit now. In the example it is `0` because the month's spend has not passed the value of what was delivered. See [Plans](/account/plans#if-we-deliver-less-than-the-budget-committed) for the rule.

The budget, its pace, the caps, the balance and how it is funded are not served here or anywhere a key can reach: a person sets them in **Settings → Billing**. The balance is on [`GET /v1/account`](#get-v1account).

### GET /v1/ai-traffic/install

Read-only. No credits. MCP tool: `get_ai_traffic_install`.

Everything needed to start sending AI-crawler hits: the ingest endpoint, your workspace's HMAC signing secret, the signature header name, the replay window, the daily cap, and a copy-pasteable edge worker. No parameters.

```json Response theme={null}
{
  "endpoint": "https://webapp.attensira.com/v1/visits",
  "method": "POST",
  "signature_header": "X-Attensira-Signature",
  "signing_secret": "…",
  "project_id": "prj_5b22",
  "replay_window_seconds": 300,
  "daily_visit_cap": 100000,
  "worker": "export default { async fetch(request, env) { … } }",
  "notes": ["Deploy in front of the tracked domain."]
}
```

Call it when [AI traffic](/traffic/ai-traffic) is empty and you need to know how to send us any. The signing secret is derived from the workspace id, so it is stable and can be shown again — unlike your API key, which is displayed exactly once.

### GET /v1/wins

Read-only. No credits. Works on a spent balance. MCP tool: `list_wins`.

The workspace's wins, newest first. A win is the first time an engine named your brand, or linked to your site, in an answer to a prompt that does not name your brand itself, after we had read that prompt on that engine on at least three earlier days with a successful answer and none of them did the same. Failed reads count toward neither half. Only the first naming (or link) can be a win: one that came before those three days of reading is not, and no later answer of the same kind on that prompt and engine becomes one.

A win is a first **since we started reading** that prompt, not evidence that any work caused it. When you show one, use its `sentence`, which says exactly that.

No parameters. Returns at most the 50 most recently found wins, and leaves dismissed wins out.

```bash Shell theme={null}
curl https://api.attensira.com/v1/wins \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "wins": [
    {
      "id": "win_4e19",
      "prompt_id": "prm_8f21",
      "prompt": "best crm for small teams",
      "engine": "ChatGPT",
      "platform": "chatgpt",
      "kind": "linked",
      "sentence": "ChatGPT linked to acme.com for the first time since we started reading this prompt on 2 September 2026, in an answer to 'best crm for small teams' on 28 September 2026.",
      "quote": "Acme publishes its pricing openly (https://acme.com/pricing), with a free tier for up to three users.",
      "quote_truncated": true,
      "cited_url": "https://acme.com/pricing",
      "date": "2026-09-28",
      "reading_since": "2026-09-02",
      "detected_at": "2026-09-28T10:05:00Z",
      "seen": true,
      "share_url": "https://app.attensira.com/w/"
    }
  ],
  "unseen": 0
}
```

`kind` is `named` (the answer named the brand) or `linked` (it cited a page on your domain); one prompt on one engine can give one win of each kind, never two of the same. `quote` is a verbatim excerpt of at most 400 characters, never edited, and `null` when no answer that day gave an excerpt that passed the honesty check; `quote_truncated` says whether it stops short of either end of the answer. `cited_url` is the first page on your domain the answer cited, or `null`. `date` is the day of the answer and `reading_since` the first day we read the prompt on that engine, both `YYYY-MM-DD` in the workspace's time zone. The list is ordered by `detected_at`, when the win was found.

`seen` is whether someone has opened the win in the dashboard, and `unseen` counts every undismissed win nobody has opened, including any beyond the 50 returned. Reading this endpoint marks nothing seen. `share_url` is the public link to the win card while it is shared, and `null` otherwise.

### GET /v1/brief

Read-only. No credits. MCP tool: `get_brief`.

The morning brief, the same body as the email. See [The morning brief](/agent/morning-brief).

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `date` | query | string | — | `YYYY-MM-DD` for one of the workspace's own past days. Omit for today |

Counts, never a day-over-day comparison, until a seven-day window exists. The fields are described under [`get_brief`](/mcp/tools#get_brief).

### PATCH /v1/settings/agents

No credits. MCP tool: `update_settings`.

How far a run may go on its own. A partial edit: a field you leave out is left alone. This is the customer's decision; change it on their instruction. See [Approval](/agent/approval).

| Field | Type | Notes |
| - | - | - |
| `unattended_mode` | enum | `ask`, `draft` or `act` |
| `act_path_allowlist` | string\[] | Path patterns act mode may change. Replaces the list; empty restores the default. Source, CI, lockfiles and dotfiles are refused whatever it says |
| `publish_without_approval` | boolean | Whether a run may put a page on the site without asking |
| `send_without_approval` | boolean | Whether a run may send email without asking |

Returns the settings in force.

### GET /v1/github/install-url

No credits. MCP tool: `connect_github`.

The GitHub App install link for this workspace, and nothing else. The install finishes in a browser, because GitHub hands its callback a code only the person completing it has. Each call mints a single-use link.

Returns `url`.

***

## Memory

Writes into what the workspace knows about itself. See [Knowledge and skills](/agent/knowledge-and-skills).

### POST /v1/observations

Answers `201`. No credits. MCP tool: `record_observation`.

| Field | Type | Notes |
| - | - | - |
| `statement` | string | Required. One sentence carrying its own sample size |
| `evidence` | object\[] | Required, at least one. Each `{kind, id, url?, day?}`, `kind` being `reading`, `citation_day` or `action` |

Every id is resolved against the workspace before anything is stored. One that does not exist there is a `400`, never a stored fact.

### POST /v1/skills

Answers `201`. No credits. MCP tool: `write_skill`.

| Field | Type | Notes |
| - | - | - |
| `title` | string | Required. The rule in a few words |
| `description` | string | Required. When the rule applies |
| `body` | string | Required. The rule itself |
| `kind` | enum | Required. `voice`, `compliance` or `playbook` |

***

## Set up a workspace

A key holder adds a workspace for a domain: the agents draft the business from its site, the person decides what to keep, and the person pays in their own browser. Nothing here can charge. Both endpoints act on the key's own account and take no `?project=`: there is no workspace to select yet, and a `?project=` is refused with that reason. A new person with no account starts from an assistant over MCP instead, where signing in comes before any account exists; see [Set up from your own assistant](/start/quickstart#set-up-from-your-own-assistant).

### POST /v1/onboarding/workspace

Needs a read-and-write key. No charge. MCP tool: `start_workspace`.

Creates the workspace for a domain, or returns the one already started for it. Sending the same domain again never creates a second workspace: it is how you poll.

| Field | Type | Notes |
| - | - | - |
| `domain` | string | Required. The business's domain, e.g. `example.com` |

Returns `workspace_id`, `status`, the drafted profile (each fact with the page it came from, or a `question` to ask when nothing was found), the suggested competitors and platforms, the proposed goals and the plans. `status` is `drafting`, `needs_input`, `ready_to_confirm`, `awaiting_payment`, `active` or `first_readings_ready`; the fields are described under [`start_workspace`](/mcp/tools#start_workspace). An account holds one workspace waiting for payment at a time, so a second unpaid domain is a `409` naming the one to finish.

### POST /v1/onboarding/workspace//confirm

Needs a read-and-write key. No charge. MCP tool: `confirm_setup`.

Saves the person's setup choices and answers their checkout link. Every call mints a new link.

| Field | Type | Notes |
| - | - | - |
| `plan` | string | Required. A plan id from the `plans` the start endpoint returned |
| `monthly_budget_usd` | number | The monthly budget the person chose, in US dollars. Not negative |
| `overage_cap_usd` | number | The most a month may be charged in all, in US dollars: from the budget up to 10% over it, and only sent with `monthly_budget_usd`. A cap above the budget lets new work run up to 10% over the budget, which stays the budget. A cap below the budget or more than 10% over it is a `400`. Omit it and work pauses at the budget, which is then the cap |
| `competitors` | string\[] | The rivals they kept, by domain |
| `platforms` | string\[] | The platforms they kept |
| `goals` | object\[] | The goals they kept, each `{id, target?}` as `proposed_goals` returned them |
| `profile_edits` | object | Corrections to the profile, a fact's field mapped to its new value |

An omitted list keeps what was proposed; an empty list keeps none of it. Returns `checkout_url`, `requires_browser` (always `true`), `expires_at` and `budget_consent`, as described under [`confirm_setup`](/mcp/tools#confirm_setup). An account that already pays for a plan is a `409`. The endpoint never charges and never uses a saved card: the person pays in their browser, and the start endpoint then reads `active`.

***

## Goals

What the agents aim at: a metric, a target and a date, with a pace verdict from the daily readings. See [Goals](/measure/goals).

### GET /v1/goals

Read-only. No credits. MCP tool: `get_goals`.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `status` | query | string | — | `active`, `hit`, `missed`, `paused`, `archived`, or `all`. Repeat it or comma-separate it for several. Omit it for every goal that is not archived |

Returns `goals`, oldest first, each with `baseline` and `current` as `{value, n}` (a `null` value is not measured yet, not zero), `target`, `target_date`, `pace` (`state` is `ahead`, `on_pace`, `behind` or `unmeasured`), the projects it drives as `work_area_ids`, and `status`. The full row is described under [`get_goals`](/mcp/tools#get_goals).

### POST /v1/goals

Needs a read-and-write key. No credits. MCP tool: `set_goal`.

Sets a goal. Set goals on the person's instruction.

| Field | Type | Notes |
| - | - | - |
| `metric` | string | Required. A metric key, such as `ai_share_of_voice` or `search_clicks` |
| `params` | object | What the metric is measured over, where it takes any |
| `target` | number | Required. In the metric's own unit |
| `target_date` | string | Required. `YYYY-MM-DD` |
| `name` | string | Optional |
| `work_area_id` | string | The project it drives. Omit for the whole workspace |
| `Idempotency-Key` | header | Makes a retried create write once |

The baseline is the first reading taken after the goal is set, never a field: a body with `baseline` is a `400`.

### PATCH /v1/goals/

Needs a read-and-write key. No credits. MCP tool: `set_goal`.

Changes a goal. A field you leave out is left alone, and a body with nothing to change is a `400`.

| Field | Type | Notes |
| - | - | - |
| `name` | string | Not empty |
| `target` | number | In the metric's own unit |
| `target_date` | string | `YYYY-MM-DD` |
| `status` | enum | `active`, `paused` or `archived`. `hit` and `missed` are verdicts reached on the target date, never set |
| `work_area_id` | string | A project id, or `null` to make the goal the whole workspace's |
| `version` | integer | The version you read. A goal that changed since is a `409` |

A goal's metric and its params never change: to measure something else, set a new goal and archive this one.

***

## Projects

A project is an area of work with its own crew of agents, and on the wire it is a **work area**. Creating a project and changing its settings are in the dashboard only. See [Projects](/agent/projects).

### GET /v1/work-areas

Read-only. No credits. MCP tool: `list_projects`.

The workspace's projects, each with `id`, `name`, `description`, `team` (each crew member with their status line), `goal_id`, `unread`, `needs_you`, `budget_cap_usd` and `last_activity_at`. Archived projects are left out.

### GET /v1/work-areas/

Read-only. No credits. MCP tool: `list_projects`.

One project: everything the list says, plus its `direction` (what people have told it, word for word), the `triggers` its crew runs on, `automation_ids`, and the `feed`'s newest 50 posts, newest first. A project outside the workspace is a `404`.

### POST /v1/work-areas//steer

Needs a read-and-write key. Posting is free; the crew's reply is an agent run and spends credits. MCP tool: `steer_project`.

Gives a project's crew the person's direction, in their words. It is kept word for word as the project's direction and posted to its feed; the crew answers it with an ordinary run that replans if the steer needs it.

| Field | Type | Notes |
| - | - | - |
| `text` | string | Required. At most 4,000 characters |
| `Idempotency-Key` | header | Makes a retried post post once |

A steer is a post and nothing else: it never approves work, changes a setting or publishes anything, and the run that answers it meets the same approvals as every run. A field other than `text` is refused. The response is described under [`steer_project`](/mcp/tools#steer_project).

***

## Deliverables

### GET /v1/deliverables

Read-only. No credits. MCP tool: `list_deliverables`.

What the crews delivered, newest first: where each piece of work lives and what the re-reads proved about it.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `type` | query | enum | — | `article`, `page`, `fix`, `social_post`, `listing_fix`, `product_copy` or `image` |
| `work_area_id` | query | string | — | One project's deliverables |
| `status` | query | string | — | `waiting_on_you`, `draft`, `open`, `merged`, `synced_draft`, `live`, `shipped` or `closed`. Repeat it or comma-separate it for several |
| `limit` | query | integer | 20 | 1–50 |
| `cursor` | query | string | — | `next_cursor` from the previous page |

Returns `deliverables` and `next_cursor` (`null` at the end). `result` is `null` until there is something to measure. The row is described under [`list_deliverables`](/mcp/tools#list_deliverables).

***

## Reports

### GET /v1/reports/latest

Read-only. No credits. MCP tool: `get_report`.

The latest published client report for a cadence. See [Client reports](/agencies/client-reports).

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `cadence` | query | enum | `week` | `week`, `month` or `quarter` |

Returns the report's period, its facts with their `n`, the share link when it is shared, and the markdown in full: unlike the MCP tool, nothing is cut. A cadence whose first report has not published yet is a `404`.

***

## MCP-only for now

These [MCP tools](/mcp/tools) have no REST endpoint yet. Their payloads are the contract, so when an endpoint lands it will answer the way the tool does today.

| Tool | Does |
| - | - |
| `get_markets`, `preview_markets`, `get_brand_family` | Where the workspace is read, what a markets change would cost, and the brand family. See [Markets](/tracking/markets) and [Brand family](/tracking/brand-family) |


# REST API errors
Source: https://docs.attensira.com/api/errors

Every failure is one envelope with a stable type, so a client needs exactly one branch for things going wrong.

Every non-2xx response from the REST API is the same shape, whether the problem was caught at the door or came back from the backend:

```json Error envelope theme={null}
{
  "error": {
    "type": "invalid_request",
    "message": "unknown area \"vibes\". Valid areas are: visibility, prompt, page, source, competitor, topic, platform, bot. Use \"visibility\" for the overall picture, \"prompt\"/\"page\" for what to fix, \"competitor\"/\"platform\"/\"source\" for where you stand",
    "status": 400
  }
}
```

There is no second shape and no plain-text response — a `404` for a URL that does not exist at all is still this envelope. One failure branch is enough.

## The fields

| Field | Use it for |
| - | - |
| `type` | Branching. Stable and machine-readable; a value is never renamed. |
| `message` | Showing a human, or handing to an agent to correct itself. Wording changes without notice — never switch on it. |
| `status` | The HTTP status, repeated in the body so a logged payload is self-describing. |

## Types

| `type` | Status | Meaning | Retry? |
| - | - | - | - |
| `invalid_request` | `400`, `413` | The request was wrong: a missing parameter, an unknown enum value, an unknown body field, a body too large. | No — fix the request. |
| `unauthorized` | `401` | Missing, malformed, revoked or expired key. See [Authentication](/api/authentication). | No. |
| `forbidden` | `403` | The key is valid but lacks the scope this endpoint needs. `GET /v1/account` lists the key's scopes. | No. |
| `not_found` | `404` | No such prompt, page, automation, session — or no such endpoint. | No. |
| `conflict` | `409` | The change collides with the current state. | No, not unchanged. |
| `rate_limited` | `429` | Too many requests for this key. | Yes, after a backoff. |
| `server_error` | `5xx` | Something failed on our side. | Yes. |

## Validation errors name the valid values

An error from this API is written to be actionable, not merely correct. Where an enum is wrong, the message lists the accepted values; where a required field is missing, it says what a real value looks like.

```json 400 — bad trigger theme={null}
{
  "error": {
    "type": "invalid_request",
    "message": "trigger is required and must be one of: manual, once, daily, weekly. \"manual\" and \"once\" never fire on a clock; \"daily\" and \"weekly\" fire at run_at_minute",
    "status": 400
  }
}
```

That is deliberate: the caller is often an agent that can fix its own request in one step if it is told how, and a human reading a `400` in a terminal wants the same sentence.

## What is safe to retry

`429` and `5xx` are the retryable failures. Everything in the `4xx` range below `429` will fail identically until the request changes.

Retry with exponential backoff and jitter. A tight retry loop against a `429` extends the window you are limited for.


  These endpoints spend credits: `POST /v1/prompts`, `POST /v1/automations/{id}/runs`, `POST /v1/ask`, `POST /v1/sessions/{id}/context` with `run: true`, `POST /v1/inbox/tasks/{id}/answer` when the run that asked had already finished, and the run that answers `POST /v1/work-areas/{id}/steer`. Send an `Idempotency-Key` header on an automation run so a retry after a timeout cannot start — or bill — a second run.


A `5xx` never echoes an internal message. The body says `temporary server error, retry`, because an internal fault is not something the caller can act on, and the internals of a failure are not theirs to read. The `X-Request-Id` on the response is what identifies it — quote that in a support request.

## Timeouts are not errors

`POST /v1/ask` and `POST /v1/automations/{id}/runs` start work that can outlive the request. They answer `200` with `status: "running"` and a `session_id`; that is the success case, not a failure.

Wait `poll_after_ms`, then poll `GET /v1/sessions/{id}` until `status` is `complete` or `failed`. Re-sending the original `POST` instead starts a second billed run.


  Still stuck? [Troubleshooting](/reference/troubleshooting) covers empty results, unexpected zeros and keys that stopped working.



# Attensira REST API
Source: https://docs.attensira.com/api/overview

Read your workspace and change it from your own code, over conventional HTTP, using the same workspace-scoped API key as the MCP server.

The REST API is Attensira for your own code. Same data, same key, and the capabilities the [MCP server](/mcp/overview) exposes, over plain HTTP instead of MCP, so a cron job, a CI step, a dashboard or a data pipeline can reach it without speaking a protocol built for assistants.

## Which door to use

The two surfaces are twins on purpose. Every REST endpoint has an MCP tool with the same payload, so a customer's answer never depends on which door they came through. The few tools without a REST endpoint yet, the markets reads, are listed under [MCP-only for now](/api/endpoints#mcp-only-for-now) and marked MCP-only in the [tool reference](/mcp/tools).

A key holder can add a workspace for a new domain with [`POST /v1/onboarding/workspace`](/api/endpoints#post-v1onboardingworkspace); the person still pays in their own browser. A new person with no account yet starts in the app, or from an assistant over MCP, where signing in comes first.

| Use | When |
| - | - |
| [MCP server](/mcp/overview) | An assistant is doing the work — Claude Code, Cursor, ChatGPT. The assistant discovers the tools and decides which to call. |
| **REST API** | Your code is doing the work — a nightly export, a deploy check, an internal dashboard, a Zapier or n8n HTTP step. |

Both take the same `atn_live_` key, so you do not need a second credential to add the other. The REST API takes a key only; OAuth connections belong to the MCP server.

## Base URL

```text Base URL theme={null}
https://api.attensira.com
```

Every endpoint lives under `/v1`. `https://api.attensira.com/healthz` is unauthenticated and answers without a key, which makes it a useful reachability check when a client is silent about why it failed.

## Your first request

```bash Shell theme={null}
curl https://api.attensira.com/v1/account \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "org": { "id": "org_3a10" },
  "project": { "id": "prj_5b22", "name": "Acme", "domain": "acme.com" },
  "workspaces": [{ "id": "prj_5b22", "name": "Acme", "domain": "acme.com" }],
  "credits": { "available": 4200, "available_usd": 42.0, "held_units": 0 },
  "scopes": ["read", "write"],
  "platforms": ["chatgpt", "perplexity"]
}
```

Call `/v1/account` first from a fresh integration: it lists every workspace the credential can reach, confirms which one it resolved to, and its `platforms` array is the authority on which AI answer surfaces were queried at all. A platform missing from that list was never measured — which is not the same as measured at zero.

## What the API can do


  A key can delete prompts, competitors, automations and sessions, and spend credits. Treat it as a workspace credential, not a read token. The endpoints that spend credits are `POST /v1/prompts`, `POST /v1/automations/{id}/runs` and `POST /v1/ask`, plus `POST /v1/sessions/{id}/context` with `run: true`, `POST /v1/inbox/tasks/{id}/answer` when the run that asked had already finished, and the run that answers a `POST /v1/work-areas/{id}/steer`. The ones that destroy data are `DELETE /v1/prompts/{prompt}`, `DELETE /v1/competitors/{id}`, `DELETE /v1/automations/{id}` and `DELETE /v1/sessions/{id}`.


| Group | Endpoints |
| - | - |
| Find and measure | `GET /v1/search`, `GET /v1/analytics/{area}`, `GET /v1/prompts/{prompt}`, `GET /v1/pages`, `POST /v1/evidence/query`, `GET /v1/deciding-sources`, `GET /v1/win-plan` |
| The receipts | `GET /v1/memory/answers/history`, `GET /v1/memory/answers/slice`, `GET /v1/memory/citations/timeline`, `GET /v1/memory/pages/history` |
| Track | `POST /v1/prompts`, `DELETE /v1/prompts/{prompt}`, `POST /v1/competitors`, `DELETE /v1/competitors/{id}` |
| Automations | `GET`/`POST /v1/automations`, `GET`/`PATCH`/`DELETE /v1/automations/{id}`, `POST /v1/automations/{id}/runs` |
| [Inbox](/api/endpoints#inbox) | `GET /v1/inbox`, `GET /v1/inbox/items/{ref}`, `POST /v1/inbox/tasks/{id}/answer`, `POST /v1/inbox/tasks/{id}/resolve`, `POST /v1/inbox/{id}/approve`, `POST /v1/inbox/{id}/decline`, `POST /v1/inbox/asks/{id}/approve`, `POST /v1/inbox/asks/{id}/decline` |
| Agent | `POST /v1/ask`, `GET /v1/sessions/{id}` |
| [Sessions](/api/sessions) | `GET /v1/sessions`, `POST /v1/sessions/{id}/cancel`, `DELETE /v1/sessions/{id}`, `POST /v1/sessions/{id}/context` |
| Loop health | `GET /v1/loop-health` |
| Account and settings | `GET /v1/account`, `GET /v1/costs`, `GET /v1/ai-traffic/install`, `GET /v1/wins`, `GET /v1/brief`, `PATCH /v1/settings/agents`, `GET /v1/github/install-url` |
| Memory | `POST /v1/observations`, `POST /v1/skills` |
| [Set up a workspace](/api/endpoints#set-up-a-workspace) | `POST /v1/onboarding/workspace`, `POST /v1/onboarding/workspace/{id}/confirm` |
| [Goals](/api/endpoints#goals) | `GET`/`POST /v1/goals`, `PATCH /v1/goals/{id}` |
| [Projects](/api/endpoints#projects) | `GET /v1/work-areas`, `GET /v1/work-areas/{id}`, `POST /v1/work-areas/{id}/steer` |
| Deliverables and reports | `GET /v1/deliverables`, `GET /v1/reports/latest` |

[Webhooks](/api/webhooks) push events to you instead: a session finished, work filed, an article written, a change shipped, a report ready, a scheduled run skipped, a goal updated. They are managed with the same API key at `https://webapp.attensira.com/v1/webhooks`.

The full parameter and response reference is on [Endpoints](/api/endpoints).

## Conventions

**Versioning.** The version is in the path. `/v1` will not change shape under you: fields may be added, never removed or retyped. Write clients that ignore fields they do not recognise.

**Content type.** Requests with a body send `Content-Type: application/json`. Every response is JSON, including every error.

**Unknown fields are rejected.** A `POST` body containing a field the endpoint does not define is a `400`, not a silent drop. A misspelled `instructions` quietly ignored is an automation that does the wrong thing forever; a `400` at the moment of the typo is cheaper.

**Numeric parameters are clamped, not rejected.** `?limit=9999` on an endpoint capped at 50 returns 50 rows, because "as many as you can give me" is what was meant. A value that is not a number at all is a mistake rather than an intent, and returns `400`.

**Paging.** `GET /v1/search` takes `limit` and `offset`, and returns `total` and `has_more`. Page on `has_more` — `total` is a floor, not a grand total, and a full page is not proof there are more. The answer reads under `/v1/memory/answers/` and `GET /v1/deliverables` page with `cursor`: pass the previous page's `next_cursor`, which is `null` at the end. Every other endpoint is bounded by `limit` instead.

**Request ids.** Every response carries `X-Request-Id`. Send your own and it is reused end to end, so one trace stays one trace. Quote it in a support request.

**Naming your client.** Send `X-Attensira-Client: `, for example `n8n`, and the work a call starts is marked with it in the app. It is optional and a label only: it never grants or limits anything. A call without it is shown as API use. See [Marked via your assistant](/mcp/connect#marked-via-your-assistant) for the names shown.


  The API is rate limited per key. A `429` comes back in the standard [error envelope](/api/errors) with `type: "rate_limited"` — back off and retry.


## Reading what comes back

Every numeric field can be `null`, and `null` never means zero. A rate is `{value, n}`, a delta is `{value, real}`, and per-platform metrics carry `{tracked, readable}`. `tracked: false` means your plan does not include that platform, so it was never queried — rendering it as 0% invents a failure that did not happen.

This matters more through the API than through the dashboard, because your code is what will coerce a `null` into a `0` on its way into a chart. [Reading the numbers](/measure/reading-the-numbers) covers null against zero, small `n`, and when a delta is worth acting on.

## Next steps


  
    Key format, the header, what a 401 means, and how scoping works.
  

  
    Every endpoint, with request and response examples.
  

  
    One envelope, seven types, and what to retry.
  



# Sessions
Source: https://docs.attensira.com/api/sessions

List the agent's sessions, stop one, delete one, or hand it more context: the REST endpoints behind list_sessions, cancel_session, delete_session and add_context.

A **session** is one piece of agent work: an `ask`, an automation run, a chat, or the [set-up run](/start/set-up-run). `GET /v1/sessions/{id}` reads one (see [Endpoints](/api/endpoints#get-v1sessionsid)). The endpoints on this page list them and act on them.

Each has an MCP twin with the same payload: `list_sessions`, `cancel_session`, `delete_session` and `add_context`. See the [tool reference](/mcp/tools#sessions).

## GET /v1/sessions

Read-only. No credits. MCP tool: `list_sessions`.

Sessions, newest first.

| Parameter | In | Type | Default | Notes |
| - | - | - | - | - |
| `status` | query | string | — | `complete`, `running` or `failed`, or a stored state: `queued`, `running`, `awaiting_person`, `suspended`, `completed`, `failed`, `cancelled`. Comma-separate or repeat for several |
| `origin` | query | string | — | `user`, `automation`, `mcp` or `setup`. Comma-separate or repeat |
| `since` | query | string | — | RFC 3339 timestamp or `YYYY-MM-DD` |
| `limit` | query | integer | 50 | 1–200 |

```bash Shell theme={null}
curl -G https://api.attensira.com/v1/sessions \
  --data-urlencode "status=running" --data-urlencode "origin=automation" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "sessions": [
    {
      "id": "ses_9d02",
      "title": "Daily win plan",
      "origin": "automation",
      "status": "running",
      "state": "running",
      "automation_id": "aut_71cd",
      "created_at": "2026-09-23T06:00:02Z",
      "ended_at": null
    }
  ]
}
```

`status` is the three-word vocabulary (`complete`, `running`, `failed`); `state` is the finer stored word. A cancelled session reads `failed` under `status` and `cancelled` under `state`.

## POST /v1/sessions/\{id}/cancel

No credits. Works on a spent balance. MCP tool: `cancel_session`.

Stops a running session before its next step. Credits already spent on it stay spent.

Idempotent: cancelling twice is the same as cancelling once. If the run already finished, the call returns that real ending unchanged; a cancel never rewrites a finished run.

```json Response theme={null}
{ "id": "ses_9d02", "status": "failed", "state": "cancelled" }
```

## DELETE /v1/sessions/\{id}

Destructive. MCP tool: `delete_session`.

Permanently deletes a finished session and its trace. A running session answers `409`: cancel it first.

```json Response theme={null}
{ "id": "ses_9d02", "deleted": true }
```

## POST /v1/sessions/\{id}/context

Spends credits only with `run: true`. MCP tool: `add_context`.

Gives a session text or links it cannot find itself. The agent reads it as a message in the thread.

| Field | Type | Notes |
| - | - | - |
| `text` | string | At most 20,000 characters |
| `urls` | string\[] | `http` or `https`, at most 10 |
| `run` | boolean | Default `false` |

Send `text`, `urls`, or both.

* **`run: false`** (default): nothing starts. The session's next turn reads the context, and a run already in flight picks it up when its current turn ends.
* **`run: true`**: the session is queued again to act on it now. That is a new turn, and it costs credits.

A cancelled session refuses new context.

```bash Shell theme={null}
curl -X POST https://api.attensira.com/v1/sessions/ses_9d02/context \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"text":"Our pricing page moved to /plans last week.","urls":["https://acme.com/plans"],"run":false}'
```

```json Response theme={null}
{ "session_id": "ses_9d02", "status": "running" }
```


# Webhooks
Source: https://docs.attensira.com/api/webhooks

Get a signed HTTPS POST when a session finishes, work is filed, an article is written, a change ships, a report is ready, a scheduled run is skipped or a goal changes: endpoints, events, signatures, retries and the delivery log.

Webhooks push events to your own endpoint instead of making you poll. Each delivery is a signed `POST` with a JSON body.

## Managing endpoints

Manage endpoints in the dashboard, or from your own code with an **API key**:

| Door | Routes | Credential |
| - | - | - |
| Dashboard | `/v2/org/webhooks` | Your signed-in session |
| Your code | `https://webapp.attensira.com/v1/webhooks` | An `atn_live_` [API key](/account/api-keys). OAuth tokens are refused: creating an endpoint mints a signing secret, and an OAuth consent must not mint a credential |

Both doors take the same calls:

| Call | Does |
| - | - |
| `GET /v1/webhooks` | Lists the org's endpoints, secrets masked, each with its last delivery status |
| `POST /v1/webhooks` | Creates an endpoint. Answers `201` |
| `PATCH /v1/webhooks/{id}` | Edits `url`, `events`, `description`, `project_id` or `enabled` |
| `DELETE /v1/webhooks/{id}` | Deletes an endpoint. Deliveries already queued for it are dropped |
| `POST /v1/webhooks/{id}/test` | Sends a `webhook.test` delivery. Answers `202` with `{delivery_id, queued}` |
| `GET /v1/webhooks/events` | Lists the event types you can subscribe to, as `{events: [{type, description}]}` |
| `GET /v1/webhooks/{id}/deliveries` | The endpoint's [delivery log](#the-delivery-log), newest first |
| `POST /v1/webhooks/{id}/deliveries/{deliveryId}/resend` | [Sends a logged delivery again](#resending-a-delivery). Answers `202` with `{delivery_id, resend, queued}` |

An org can have at most **20** endpoints.

### Endpoint fields

| Field | Notes |
| - | - |
| `url` | Required. `https` only, on a public host |
| `events` | Which events to send. Empty or `*` means all |
| `project_id` | Optional. Only send events for this workspace |
| `description` | Optional. For your own reference |
| `enabled` | Whether deliveries are sent |

```bash Shell theme={null}
curl -X POST https://webapp.attensira.com/v1/webhooks \
  -H "Authorization: Bearer atn_live_" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://hooks.example.com/attensira","events":["session.finished","work_item.created"],"description":"Ops channel"}'
```

The response to the create call is the **only time the signing secret is shown** in full. Store it then; every later read shows it masked.

An endpoint also reports how deliveries are going: `last_delivery_at`, `last_status`, `last_error` and `failing_since`. If we turned it off, `disabled_reason` and `disabled_at` say why and when (see [When an endpoint is turned off](#when-an-endpoint-is-turned-off)).

## Events

| Event | Sent when | `data` |
| - | - | - |
| `session.finished` | A session ends | `session_id`, `status` (`complete` or `failed`), `state` (`completed`, `failed` or `cancelled`), `origin`, `title`, `automation_id`, `created_at`, `ended_at` |
| `work_item.created` | The agent files a piece of work that waits on a person | `work_item_id`, `title`, `kind`, `target` |
| `document.created` | The agent writes a document for publishing, such as an article, and files it for approval | `document_id`, `version`, `session_id`, `project_id`, `work_item_id`, `title`, `kind`, `target_plugin`, `body_markdown`, `body_truncated`, `dashboard_url` |
| `change.shipped` | A change goes live | `work_item_id`, `project_id`, `kind`, `plugin`, `url`, `shipped_at`, `delivery_label` |
| `report.ready` | A [client report](/agencies/client-reports) is published | `report_id`, `kind`, `title`, `url` |
| `loop.skipped` | A scheduled automation run is skipped (see [loop health](/agent/loop-health#why-a-scheduled-run-was-skipped)) | `automation_id`, `name`, `reason`, `due_at` |
| `goal.updated` | A [goal](/measure/goals) is set or edited, paused, resumed or archived, or its pace or status changes | `goal_id`, `name`, `change`, `status`, `pace_state`, `previous_pace_state`, `current`, `target`, `target_date`, `work_area_ids` |
| `webhook.test` | You asked for a test delivery | `message` |

`GET /v1/webhooks/events` returns the same list, each with a one-line description, so your code can read what it may subscribe to. New event types are added at the end; a receiver should ignore a type it does not know.

**`work_item.created`.** `kind` is the shape of the work, such as `fix`, `rewrite`, `document`, `social_post`, `answer` or `outreach`. `target` is where approving it delivers, such as `github`, `notion`, `webflow`, `copy` (you publish it yourself) or `email`, and is empty when approving it delivers nowhere on its own.

**`document.created`.** Sent once per version of a document that became work: the agent revising an article into a new version sends it again, and a document kept beside a chat is not sent at all. `target_plugin` is where approving it delivers: `github`, `notion`, a CMS such as `webflow`, or `copy`. `body_markdown` is the whole document, up to **256 KB**. Past that it is `null` and `body_truncated` is `true`, rather than a document cut off mid-sentence; `dashboard_url` opens the chat that wrote it, where the whole document is. `session_id` and `work_item_id` may be `null`.

**`change.shipped`.** Sent once per change, however it went live: its pull request merged, an integration published it, or someone [marked it shipped](/agent/inbox#mark-shipped). `url` is where it can be opened and `delivery_label` says where it lives in words, such as "Pull request #17 on GitHub" or "Draft in Webflow"; either may be `null`. A change that landed as a draft in your own tool is not shipped until it is marked shipped.

**`goal.updated`.** `change` says what happened: `created`, `updated`, `pace_changed`, `hit`, `missed`, `paused`, `resumed` or `archived`. `pace_state` is `ahead`, `on_pace`, `behind` or `unmeasured`, and `previous_pace_state` is present only when the pace moved. `current` is `{value, n}`, with `value` `null` until the first reading. A daily reading that changes neither pace nor status sends nothing.

Payloads link to the dashboard and to where the work lives. They never carry a link that approves or merges anything: that happens where a person reads it first.

## A delivery

Each delivery is a `POST` to your URL with a JSON body:

```json Body theme={null}
{
  "id": "evt_01J8Z3",
  "type": "session.finished",
  "created_at": "2026-09-23T06:04:11Z",
  "project_id": "prj_5b22",
  "data": {
    "session_id": "ses_9d02",
    "status": "complete",
    "state": "completed",
    "origin": "automation",
    "title": "Daily win plan",
    "automation_id": "aut_71cd",
    "created_at": "2026-09-23T06:00:02Z",
    "ended_at": "2026-09-23T06:04:10Z"
  }
}
```

With these headers:

| Header | Value |
| - | - |
| `Attensira-Signature` | `t=,v1=` |
| `Attensira-Event` | The event type |
| `Attensira-Delivery` | The delivery id, the same as the body's `id`. Stable across retries and resends, so dedupe on it |

## Verifying the signature

`v1` is the hex HMAC-SHA256, keyed with the endpoint's signing secret, of the string `.`: the `t` value, a full stop, and the raw request body exactly as received.

1. Split the header on `,` and read `t` and `v1`.
2. Compute the HMAC over `t + "." + rawBody`.
3. Compare it with `v1` in constant time.
4. Reject the delivery if `t` is more than **5 minutes** old.

```js Node theme={null}
import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) <= 300;
  return fresh && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
```

## Retries

Answer with any `2xx` to accept a delivery. Any other status, or no answer within **10 seconds**, is retried up to **14 times** with exponential backoff, spread over about a day. Redirects are not followed: a `3xx` counts as a failure. A `410 Gone` is not retried: it [turns the endpoint off](#when-an-endpoint-is-turned-off).

Because a retry carries the same `Attensira-Delivery`, a receiver that stores the ids it has handled can treat a repeat as already done.

**One occurrence, one id.** Events that name a single occurrence (a work item filed, a document version, a change shipped, a report, a skipped run, a goal transition) get an id derived from it. If the same occurrence is reported to us twice, for example GitHub redelivering a merge, it renders the same id, and is not queued again while the first delivery is still held (up to a day after it finishes). Past that it may be delivered again under the same id, which is why deduping on `Attensira-Delivery` is the reliable rule.

## The delivery log

Every delivery to an endpoint is logged for **30 days**: what was sent and each attempt to send it. Read it in **Settings → Webhooks** under the endpoint's **Delivery log**, or with:

```bash Shell theme={null}
curl "https://webapp.attensira.com/v1/webhooks//deliveries?limit=20" \
  -H "Authorization: Bearer atn_live_"
```

```json Response theme={null}
{
  "deliveries": [
    {
      "id": "evt_5f0c2a9e41b7d3c8a1e6f204",
      "type": "change.shipped",
      "project_id": "prj_5b22",
      "created_at": "2026-09-30T09:12:04Z",
      "state": "failed",
      "last_status": 503,
      "resends": 0,
      "attempts": [
        {
          "at": "2026-09-30T09:12:05Z",
          "status": 503,
          "duration_ms": 412,
          "response_snippet": "Service Unavailable",
          "error": "webhook receiver did not accept the delivery: HTTP 503",
          "resend": 0
        }
      ]
    }
  ],
  "next_cursor": null
}
```

* `id` is the delivery id your server saw in `Attensira-Delivery`.
* `state` is `queued` until the first attempt, then `delivered` or `failed` according to the latest attempt.
* Each attempt records when it was made, the status your server answered (`0` when it did not answer), how long it took, and the first **2 KB** of your server's answer. Anything in that answer that looks like a secret, a token, a password or an email address is masked before it is stored. `resend` is `0` for the original send and `n` for the nth resend. A row keeps its latest 20 attempts.
* Pages are newest first. `limit` defaults to 20, up to 100; pass `next_cursor` back as `cursor` for the next, older page. It is `null` on the last page.

## Resending a delivery

`POST /v1/webhooks/{id}/deliveries/{deliveryId}/resend`, or **Resend** in the delivery log, queues a logged delivery again. It sends the same body with the same `Attensira-Delivery` id, signed afresh with a new `t`, so a receiver that already handled it treats it as done. A resend is never refused for being too soon after the original. Its attempts are added to the same log row.

A resend is refused with `409` while the endpoint is turned off, and with `404` once the delivery is older than the 30 days the log keeps.

## When an endpoint is turned off

We stop sending to an endpoint, and turn it off, when:

* your server answers `410 Gone`, which says the endpoint no longer exists; or
* a delivery fails and every delivery has failed for more than **72 hours**.

Turning it off sets `enabled` to `false`, records `disabled_reason` and `disabled_at`, and raises one [Inbox](/agent/inbox) row, "Your webhook to `` was turned off", in the workspace the event was about (or the endpoint's workspace, or your oldest workspace for an org-wide endpoint). Deliveries already queued for it are logged as not sent.

Nothing more is sent until someone turns it back on, in **Settings → Webhooks** or with `PATCH /v1/webhooks/{id}` and `{"enabled": true}`. That clears `disabled_reason`, `disabled_at` and `failing_since`, so the 72-hour clock starts again. Then resend what it missed from its delivery log. A test delivery is sent even while the endpoint is off, so you can check your server before turning it back on.


# Attensira documentation
Source: https://docs.attensira.com/index

The AI-native SEO agency: agents do the work of getting you found in Google, AI assistants and the sites they read, and you steer and approve.

Attensira is the AI-native SEO agency. Search everywhere: Google, the AI assistants (ChatGPT, Claude, Perplexity, Gemini and Google's AI), and the sites and communities those assistants read.

It is run by AI agents. You talk to one of them, the **Chief of staff**, and set the direction. Crews of agents do the work in [projects](/agent/projects): research, articles, page fixes, outreach drafts and follow-ups. They stop only when they need you, and anything that cannot be undone waits for your yes. You pay a plan fee for the platform and a monthly budget for the work, and work we don't deliver isn't charged.

Underneath the work sits measurement. Every day we put the questions your buyers ask to real assistants and record whether you were named and whether you were linked, so every change can be read before and after it ships. We promise the work, never the outcome, and we say "nothing moved that we can prove" when that is the honest answer.


  
    Who does what, what reaches you, what needs your yes, and what we promise.
  

  
    From your domain to a team at work: the set-up steps, the plan and budget, and the first readings.
  

  
    Chief of staff, project, deliverable, goal, Inbox, prompt, answer: every word, and its name on the wire.
  

  
    The plan fee, the monthly work budget and what it buys, and what happens when we deliver less.
  

  
    Connect your own assistant. It can set up a workspace, read the numbers and act, with the same approvals.
  

  
    Workspace data and actions as plain HTTP endpoints, for your own code rather than an assistant.
  

  
    The cadence, the sampling depth, and what counts as a successful run.
  

  
    What Attensira does not measure or promise, and where the numbers stop being trustworthy.
  



# Connect a client
Source: https://docs.attensira.com/mcp/connect

Complete, copy-pasteable Attensira MCP setup for Claude Code, Claude Desktop, Claude.ai, Cursor, VS Code, ChatGPT, Codex CLI, Windsurf, Zed, Cline, n8n, and any other MCP client.

Every client below connects to the same endpoint. What changes is where you put the URL, and which of the two credentials you use.


  The quickest path is in the app: **Settings → Your assistant** at [app.attensira.com/settings/access](https://app.attensira.com/settings/access). Pick the assistant you use (Claude, ChatGPT, Cursor, Claude Code, Codex or VS Code) and you get that one's steps, ready to copy, with nothing else to read through. Claude and ChatGPT connect only by signing in, so they are listed only while sign-in is available. This page has the same steps plus every other client.


| Setting | Value |
| - | - |
| Server URL | `https://mcp.attensira.com/mcp` |
| Transport | Streamable HTTP |
| Authentication | OAuth (sign in through the browser) **or** `Authorization: Bearer atn_live_` |

## Which credential to use

Both work, on the same endpoint, and neither is going away.

| | OAuth | Static API key |
| - | - | - |
| Use it for | Interactive clients with a human and a browser — Claude, Claude Code, Cursor, VS Code, ChatGPT | Headless callers — n8n, CI jobs, scripts, servers |
| Setup | Paste the URL and sign in | Mint a key, paste it into a header |
| Secret on disk | None. The client stores a token it can refresh | Yes. A long-lived key sits in a config file or a secret store |
| Expiry | Access tokens are short-lived and refresh silently; you sign in again occasionally | Never expires until you revoke it |
| Taking it away | Remove the server in the client | Revoke it in Settings → Your assistant, effective on the next request |

If a human is going to be sitting there when the client first starts, use OAuth: there is no key to paste, nothing to leak into a committed config file, and revoking one client does not break the others. If nothing can open a browser, use a key.


  You can do both at once. Claude Code on your laptop over OAuth and an n8n workflow on a static key are two independent credentials against the same server, and revoking either leaves the other alone.


## Which clients need a bridge

The Attensira server speaks **Streamable HTTP only**. Clients that speak remote HTTP natively connect directly. Clients that only launch local stdio processes cannot, and need the `mcp-remote` bridge, which runs locally over stdio and forwards to the HTTPS endpoint.

| Client | Connects directly | OAuth | Notes |
| - | - | - | - |
| Claude Code, Claude Desktop, Claude.ai | Yes | Yes | Native remote HTTP and OAuth |
| Cursor, VS Code + Copilot | Yes | Yes | Browser prompt on first connect |
| ChatGPT | Yes | Yes | Connector configured in the UI |
| Windsurf, Cline | Yes | Varies by build | Config file takes a `url` and `headers`; use a key if the build has no OAuth |
| Codex CLI | Yes | No | `codex mcp add --bearer-token-env-var` keeps the key out of config |
| n8n | Yes | No | Headless — use a static key |
| Zed | Yes | Yes | Remote server with a `url`; Zed runs OAuth when there is no `Authorization` header |
| Older client builds that launch local processes only | Via `mcp-remote` | Yes, through the bridge | stdio-only; see the bridge section |

***

# Connecting with OAuth

Nothing to mint and nothing to paste. You add the URL, a browser opens, you sign in and choose a workspace, and the client stores a token it refreshes on its own.

## Claude.ai and Claude Desktop

Go to **Settings → Connectors → Add custom connector**, name it `Attensira`, and enter:

```text Server URL theme={null}
https://mcp.attensira.com/mcp
```

Leave every authentication field alone — do not add a header, and do not paste a client ID or secret. Choose **Connect**, and Claude sends you to Attensira to sign in. Approve the access it asks for, and you land back in Claude with the connector enabled. The connection reaches every workspace your organisation owns; you name one in the conversation when there are several.

You do not need a workspace first. Once you are signed in, ask Claude to set one up for your domain, and it walks you through it — see [Set up a business from your assistant](/mcp/recipes#set-up-a-business-from-your-assistant).

Open a new chat and enable Attensira from the tools menu. Existing conversations do not pick up new connectors. The connector works in claude.ai, Claude Desktop and the Claude mobile apps.


  Availability of custom connectors depends on your Claude plan. On Team and Enterprise, an owner adds the connector first, under Organization settings → Connectors.


## Claude Code

Add the server with no credential at all, then authenticate from inside Claude Code:

```bash Shell theme={null}
claude mcp add --transport http attensira https://mcp.attensira.com/mcp
```

Then run `/mcp`, select **attensira**, and choose to authenticate. A browser opens, you sign in and approve, and the callback returns to a local port Claude Code is listening on.

Add `--scope user` to make the server available in every project rather than only the current one. Verify with `claude mcp list`, then ask Claude to call `get_account` — a successful reply lists the workspaces the credential can reach.


  Two Claude Code instances can authenticate at the same time. Each listens on its own ephemeral loopback port, and both are accepted.


## Cursor

**Add to Cursor** — one-click install link. Cursor must already be installed; clicking it opens Cursor and prompts you to add the server. It writes the URL and nothing else — no key, and nothing is read or transmitted.

You can also add it by hand. Create `.cursor/mcp.json` in the project, or `~/.cursor/mcp.json` for all projects. With OAuth there is no secret in the file, so the in-repo path is safe:

```json .cursor/mcp.json theme={null}
{
  "mcpServers": {
    "attensira": {
      "url": "https://mcp.attensira.com/mcp"
    }
  }
}
```

Reload Cursor and open Settings → MCP. The entry shows as needing authentication; click through it, sign in and approve, and the tools fill in.

## VS Code + Copilot

VS Code reads `.mcp.json` at the workspace root or `.vscode/mcp.json`. With OAuth the whole file is three lines and carries nothing secret:

```json .vscode/mcp.json theme={null}
{
  "servers": {
    "attensira": {
      "type": "http",
      "url": "https://mcp.attensira.com/mcp"
    }
  }
}
```

Start the server from the Start link above the entry. VS Code opens a browser for sign-in and stores the resulting token in its own account storage, not in the file. Then open Copilot Chat in Agent mode and check the tools picker.

## ChatGPT


  
    In ChatGPT on the web, open **Settings** and turn on **Developer mode**.
  

  
    Create an app, name it `Attensira`, enter `https://mcp.attensira.com/mcp` and choose **OAuth**. Attensira publishes standard OAuth discovery, so there is no client ID or secret to fill in.
  

  
    Sign in to Attensira and allow. The connection reaches every workspace your organisation owns.
  

  
    In a chat, open the **+** menu, choose Developer mode and turn Attensira on.
  



  Developer mode depends on your ChatGPT plan and, on Business and Enterprise, on what an admin allows. Custom MCP connectors in ChatGPT depend on your plan and workspace settings, and some surfaces restrict which tools a connector may call. If write tools such as `add_prompts` are unavailable, that is a ChatGPT-side restriction, not a scope problem — check [Connections](/account/connections) to see what you actually granted before assuming otherwise.


## Zed

Current Zed builds connect to remote MCP servers directly. In **Settings → AI → MCP Servers**, choose **Add Server → Add Remote Server**, or add the entry to your settings file yourself:

```json Zed settings.json theme={null}
{
  "context_servers": {
    "attensira": {
      "url": "https://mcp.attensira.com/mcp"
    }
  }
}
```

With no `Authorization` header, Zed runs the standard MCP OAuth flow itself: it opens a browser, you sign in and approve, and the tools appear in the Agent Panel. To use a key instead, add `"headers": { "Authorization": "Bearer atn_live_..." }` to the same entry. The indicator next to the server in Settings → AI → MCP Servers turns green when it is active.

## Older builds and stdio-only clients

A client, or an older Zed build, that can only launch local processes needs the `mcp-remote` bridge. The bridge runs locally over stdio, forwards to the HTTPS endpoint, runs the OAuth flow itself and caches the token under your home directory:

```json Zed settings.json (older builds) theme={null}
{
  "context_servers": {
    "attensira": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.attensira.com/mcp"]
    }
  }
}
```

The bridge needs Node.js. Its first run downloads the package and opens a browser, so allow a few seconds and expect a sign-in prompt before the tools appear.

## What you are approving

The consent screen names the application and the access it wants. Read both.

An OAuth connection always carries **read and write**: there is no read-only consent. Write covers everything that changes the workspace or spends credits — adding prompts, creating and running automations, deleting things, asking the agents, approving or declining what they propose. A client that can only read cannot do the half of the job you connected it for. Where you need a credential that cannot write, mint a read-only [API key](/account/api-keys) instead.


  Anyone can register an MCP client and pick its display name. Read the name before approving, and be sure about anything unfamiliar. If you did not just start a connection yourself, do not approve one — close the tab.


What an approved client can reach, and how to disconnect one, is in [Connected applications](/account/connections).

***

# Connecting with a static key

This is the path for anything without a human and a browser: n8n, CI, a scheduled script, a server-side agent. It is fully supported and is not going anywhere.

## Get a key first

Mint a key under **Settings → Your assistant** at [app.attensira.com/settings/access](https://app.attensira.com/settings/access). It is shown exactly once: only a hash is stored, so Attensira cannot re-display it. Copy it somewhere safe — a password manager or your shell environment — before you close the dialog.

The key names your workspace, so no tool asks for a workspace id. If you lose it, revoke and mint a new one; there is no recovery path.


  Export it once and reference it by name wherever a client supports environment variables:

  ```bash Shell theme={null}
  export ATTENSIRA_API_KEY="atn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  ```

  Put that line in your shell profile so GUI clients launched from a terminal inherit it.



  `.cursor/mcp.json`, `.vscode/mcp.json`, and any config file inside a repository are shared files. A key pasted into one and committed is a leaked key — assume it is public the moment it is pushed, revoke it, and mint a new one. Use the environment-variable form in repo-visible files, add local overrides to `.gitignore`, or use OAuth, which puts no secret in the file at all.


## n8n

Use the **MCP Client Tool** node inside an AI Agent workflow. Set the endpoint to `https://mcp.attensira.com/mcp` and the transport to HTTP Streamable — not SSE, which the server does not offer.

For credentials, choose the Bearer or header-auth credential type and store the key there. n8n credentials are encrypted at rest and are not written into the workflow JSON, so an exported or version-controlled workflow does not carry the key. Once connected, the node exposes the Attensira tools to the agent; restrict the tool list on the node if you do not want the agent reaching credit-spending tools such as `run_automation`.

This is exactly the case OAuth cannot serve: a workflow fires at 3am with nobody to approve a browser prompt. Use a key, give it the narrowest scope the workflow needs, and name it after the workflow so revoking it later is unambiguous.

## Codex CLI

Codex reads the token from a named environment variable at runtime and sends it as the `Authorization` header itself, so the key never enters `~/.codex/config.toml`:

```bash Shell theme={null}
codex mcp add attensira --url https://mcp.attensira.com/mcp \
  --bearer-token-env-var ATTENSIRA_API_KEY
```

That writes an `mcp_servers.attensira` table to `~/.codex/config.toml` without the key. Make sure `ATTENSIRA_API_KEY` is set in the shell profile Codex launches from.

Only if your Codex build does not support `--bearer-token-env-var`, edit `~/.codex/config.toml` directly with the key written literally:

```toml ~/.codex/config.toml theme={null}
[mcp_servers.attensira]
url = "https://mcp.attensira.com/mcp"

[mcp_servers.attensira.http_headers]
Authorization = "Bearer atn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

Restart Codex after editing the file.

## Windsurf

Windsurf reads `~/.codeium/windsurf/mcp_config.json`:

```json ~/.codeium/windsurf/mcp_config.json theme={null}
{
  "mcpServers": {
    "attensira": {
      "serverUrl": "https://mcp.attensira.com/mcp",
      "headers": {
        "Authorization": "Bearer atn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}
```

If your Windsurf version rejects `serverUrl`, use `"url"` instead — the two spellings have both shipped. Press the refresh control in the Cascade MCP panel after saving. Since this file lives in your home directory, it is not part of any repository, but it is still plaintext on disk.

## Cline

In VS Code, open Cline → MCP Servers → Configure, and add a remote entry:

```json cline_mcp_settings.json theme={null}
{
  "mcpServers": {
    "attensira": {
      "type": "streamableHttp",
      "url": "https://mcp.attensira.com/mcp",
      "headers": {
        "Authorization": "Bearer atn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}
```

Cline also has a "Remote Servers" tab where you can paste the URL and header without editing JSON. If your Cline version offers only stdio entries, use the `mcp-remote` command shown below.

## Claude Desktop and Claude Code with a key

Both support OAuth, and OAuth is the better path on a machine with a browser. Use a key only where you deliberately want a fixed credential — a shared build machine, or a container without an interactive session.

```bash Claude Code theme={null}
claude mcp add --transport http attensira https://mcp.attensira.com/mcp \
  --header "Authorization: Bearer $ATTENSIRA_API_KEY"
```

Claude Desktop does not expand shell variables in `claude_desktop_config.json`, so the key is written literally — protect the file with filesystem permissions and never copy it into a repository:

```json claude_desktop_config.json theme={null}
{
  "mcpServers": {
    "attensira": {
      "url": "https://mcp.attensira.com/mcp",
      "headers": {
        "Authorization": "Bearer atn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}
```

Restart the app afterwards. The Attensira tools appear in the tool menu of a new conversation; existing conversations do not pick up new servers.

## Marked via your assistant

Anything your assistant files in your workspace (an Inbox row, a change to approve, a question) is marked with the client it came through, such as **via Claude** or **via Cursor**. The Inbox gathers those rows under **From your assistant**, and an approval or decline your assistant makes is recorded in the item's history the same way, for example "OAuth connection via Claude". You can tell at a glance which items you asked your assistant for and which the agents raised.

The name comes from the client itself. When a client connects to the MCP server it says what it is, and the server passes that name on. Attensira shows it only when it is one it recognises:

| Shown as | For |
| - | - |
| via Claude, via Claude Code | Claude (claude.ai, Desktop, mobile) and Claude Code |
| via ChatGPT | ChatGPT |
| via Cursor, via Codex, via VS Code | Those clients |
| via n8n | n8n, which counts as API use rather than as an assistant |
| via your assistant | A client signed in through OAuth, or one that named itself, under a name Attensira does not recognise |
| via the API | A REST call with an API key that did not say what made it |

A REST caller can name itself with the `X-Attensira-Client` header, for example `X-Attensira-Client: n8n`. Work you do yourself in the dashboard is never marked.

The mark is a label and nothing more. A client chooses what it calls itself, so the name never grants or limits anything: what a client can do is decided only by the access you approved or the key's scope.

***

## Any other MCP client

Any client that supports remote MCP servers can connect with these facts:

```text Connection details theme={null}
URL:       https://mcp.attensira.com/mcp
Transport: Streamable HTTP (POST + SSE response stream). No stdio, no plain SSE transport.
Auth:      OAuth 2.1 — discovery at /.well-known/oauth-protected-resource
           or Authorization: Bearer atn_live_<32 hex>
```

Client authors: the full wire detail — endpoints, scopes, PKCE, registration, token lifetimes — is in the [OAuth reference](/mcp/oauth).

If the client speaks stdio only, run the bridge locally and point the client at it. Omit `--header` to let the bridge run OAuth for you:

```bash Shell theme={null}
npx -y mcp-remote https://mcp.attensira.com/mcp \
  --header "Authorization: Bearer ${ATTENSIRA_API_KEY}"
```

To confirm the endpoint is reachable before you debug the client, request the unauthenticated health path:

```bash Shell theme={null}
curl -i https://mcp.attensira.com/healthz
```


  Connected. Ask your assistant to call `get_account` — it answers without you supplying a workspace id, and its `workspaces` array is what every other tool's `project` takes. With one workspace it is also named as `project`; with several, `project` is `null` until you name one, which is expected rather than a fault.


If a client stays silent or shows no tools, see [Troubleshooting](/mcp/troubleshooting).


# OAuth for the MCP server
Source: https://docs.attensira.com/mcp/oauth

Discovery, scopes, PKCE and token lifetimes for connecting a client to the Attensira MCP server.

Most people never need this page. If you are connecting Claude, Claude Code, Cursor or VS Code, the client does all of this for you — go to [Connect a client](/mcp/connect) and click through the browser prompt.

This page is for the other case: you are writing an MCP client, reviewing Attensira before listing or approving it, or debugging a flow that stops somewhere in the middle and you need to know which document said what.

## Two roles, and only one of them is ours

| Host | Role | What it serves |
| - | - | - |
| `mcp.attensira.com` | Resource server | The MCP endpoint and its protected-resource metadata |
| `clerk.attensira.com` | Authorization server | Sign-in, consent, and every token |

The authorization server is **Clerk**, running under our own hostname. That is the same system that signs you in to the dashboard, so approving a client is the sign-in you already know.

The split is not cosmetic. The resource server never holds a signing key and never issues anything — it verifies what arrives and forwards it. Everything about *issuing* a token, and every rule that governs one, belongs to the authorization server.


  This page describes the resource server's behaviour precisely, because that is ours. For the authorization server it describes the shape of the flow and points you at the discovery document for the specifics. Endpoint paths, registration limits, refresh-token format and lifetimes are the authorization server's to state, and a copy of them here would be a copy that goes stale.


## Discovery

Start with an unauthenticated request to the MCP endpoint. It answers `401` with a challenge that points at the metadata:

```bash Shell theme={null}
curl -i -X POST https://mcp.attensira.com/mcp
```

```http Response header theme={null}
WWW-Authenticate: Bearer realm="attensira", error="invalid_token", error_description="The access token is invalid or expired", resource_metadata="https://mcp.attensira.com/.well-known/oauth-protected-resource/mcp"
```

Two paths serve the protected-resource document (RFC 9728), with identical bodies — clients differ on which one they probe:

```text Protected-resource metadata theme={null}
GET https://mcp.attensira.com/.well-known/oauth-protected-resource
GET https://mcp.attensira.com/.well-known/oauth-protected-resource/mcp
```

```json Body theme={null}
{
  "resource": "https://mcp.attensira.com/mcp",
  "authorization_servers": ["https://clerk.attensira.com"],
  "bearer_methods_supported": ["header"],
  "scopes_supported": ["openid", "profile", "email", "offline_access", "user:org:read"],
  "resource_documentation": "https://docs.attensira.com/mcp/overview"
}
```

`authorization_servers` names the issuer. **Read the endpoints from its own document rather than from this page** — that is what discovery is for, and it is the only copy that cannot be out of date:

```text Authorization-server metadata theme={null}
GET https://clerk.attensira.com/.well-known/oauth-authorization-server
```

It gives you `authorization_endpoint`, `token_endpoint`, `jwks_uri`, `registration_endpoint`, the supported grant types and the PKCE methods.


  Until the authorization server answers, the `401` above omits `resource_metadata`. That is deliberate: a client that sees the pointer starts a flow, and pointing one at a server that is not there fails worse than a plain refusal. If you get a `401` with no pointer, OAuth is not available yet on that deployment — use an [API key](/account/api-keys).


## Scopes

There are deliberately no Attensira scopes. The scopes a token carries are the authorization server's own built-ins — `openid`, `profile`, `email`, `offline_access` and `user:org:read` — and none of them names an Attensira permission. A token names a person; what that person may do is a question the Attensira backend already answers from the verified subject, and a second permission system here would only be a second thing to disagree with it.

Two consequences follow. **Every OAuth connection is read and write.** Approving a client grants it your organisation, and there is no read-only consent — restrict a connected assistant by what you ask it to do, or by the tool allow-list your client offers, and use a read-only [API key](/account/api-keys) where you need a credential that cannot write. And **`offline_access` is the one scope that is not optional in practice**: access tokens are short-lived and refreshing is the only way a client stays connected, so a client that does not request it will ask you to sign in again within the hour.

`user:org:read` lets the authorization server stamp an organisation on the token when you have one. It is read when present and never required: a solo workspace has no organisation to stamp, and a connection is resolved from who you are either way.

## Authorization code with PKCE

The authorization code grant with PKCE (`S256`) is what to implement. Confirm the supported methods in the discovery document before you build against it.

```text Authorize request theme={null}
GET 
  ?response_type=code
  &client_id=
  &redirect_uri=
  &code_challenge=
  &code_challenge_method=S256
  &scope=openid%20profile%20email%20offline_access%20user:org:read
  &resource=https%3A%2F%2Fmcp.attensira.com%2Fmcp
  &state=
```

The user signs in and approves. The redirect back carries the code and your `state` echoed byte for byte; exchange the code at the `token_endpoint` with your `code_verifier`.

The `resource` parameter (RFC 8707) must be exactly `https://mcp.attensira.com/mcp`. It is what binds the token to this server, and the resource server checks it on every request — see [Tokens](#tokens).


  Authorization codes are single use. Do not retry an exchange on a network error without first checking whether the original attempt succeeded: a replayed code is indistinguishable from a stolen one, and is treated as such.


## Registering a client

Clients register through dynamic client registration (RFC 7591). Client ID Metadata Documents are not offered: the authorization server's discovery document does not advertise `client_id_metadata_document_supported`, so register dynamically. The discovery document names the `registration_endpoint`; the limits and expiry that apply to a registration are the authorization server's and are stated there.

No client secrets are issued. Every MCP client is a public client, and a secret shipped inside one is not a secret.


  Anyone can register a client and choose its display name. Read the name on the consent screen before approving, and treat an unfamiliar client as a thing to be sure about: every approved connection can add prompts, run automations and spend credits, because there is no read-only consent.


## Tokens

Access tokens are RS256-signed JWTs with `aud` set to `https://mcp.attensira.com/mcp`. The resource server verifies each one locally against the authorization server's published keys: signature, issuer, audience and expiry. Nothing is re-signed and nothing is rewritten before it reaches the API.

They are short-lived, and the client refreshes them in the background. Refresh eventually runs out, at which point the client asks you to sign in again — occasional, and the point of the design rather than a fault. Lifetimes are the authorization server's to set.

The access token names you by reference — the user, and the organisation when the authorization server has one for you — and carries no email, no name and no other personal data.


  Because verification is local, the resource server never asks whether a token is still wanted. Revoking a connection stops the next refresh, but an access token already in a client's hands stays valid until it expires. If you need access cut off immediately, use an [API key](/account/api-keys) and revoke it — a key is checked on every request.


## Which workspace a token reaches

A token names your **organisation**, not one workspace. Every tool that works on a workspace takes an optional `project`, so one connection reaches every workspace the organisation owns — an agency running three domains connects once, not three times. Omit it and your only workspace is used; if there are several, name one.

`get_account` lists the ids in `workspaces`. A workspace id outside your organisation comes back as a workspace that does not exist, which is the same answer as one that never existed.

## Errors from the MCP endpoint

These are the resource server's, and they are exact.

| `error` | HTTP | Means |
| - | - | - |
| `invalid_token` | 401 | The access token is missing, malformed, expired, not for this resource, or not signed by a key the authorization server publishes |

The `401` is deliberately the same string for every cause. Telling a caller *which* one is true tells an attacker how close they are. It is the only refusal the MCP endpoint makes: there is no `403` and no scope gate, because a token that verifies may call any tool, and whether that customer is allowed the answer is decided by the backend — the one place that knows what an Attensira workspace is. The single exception is `get_ai_traffic_install`, which the backend refuses to a token because its response carries a signing secret; it says so, and the fix is an API key.

Errors raised during the flow itself — `invalid_request`, `invalid_client`, `invalid_grant`, `invalid_scope`, `invalid_target`, `access_denied` — come from the authorization server in the RFC 6749 envelope, and its documentation governs them.

## Static keys still work here

None of this replaces `atn_live_` keys. The MCP endpoint accepts an OAuth token or a static key, permanently, and headless callers such as n8n and CI should keep using keys — they cannot complete a browser sign-in. See [API keys](/account/api-keys).

The [REST API](/api/authentication) takes a static key only. OAuth tokens are for the MCP server.


# Attensira MCP server
Source: https://docs.attensira.com/mcp/overview

Query your Attensira workspace and act on it from inside your AI assistant, over Streamable HTTP with OAuth or a workspace-scoped API key.

The Attensira MCP server puts your visibility data inside the assistant you already work in. Instead of switching to the dashboard, reading a number, and carrying it back by hand, your assistant queries the workspace directly, then changes it — adding prompts, running automations — in the same conversation.

## What MCP gives you that the dashboard does not

The dashboard shows you numbers. MCP lets an assistant read those numbers and act on them without you leaving the work you are doing. That closes the loop between noticing something and changing something.

Concretely: while editing a pricing page, you can ask which prompts mention a competitor and not you, then add three new prompts covering the gap — one conversation, no tab switching. While reviewing a release, you can pull share of voice for the last 30 days, compare it against the previous window, and check whether the delta is statistically real before anyone writes it into a report.

The tools come in groups: find and measure (`search`, `get_analytics`, `deciding_sources`, `win_plan`, `get_prompt`, `get_page`, `query_evidence`), the receipts behind the numbers (`answer_history`, `answer_slice`, `citation_timeline`, `page_history`), track (`add_prompts`, `remove_prompt`, `add_competitor`, `remove_competitor`), inbox (`list_inbox`, `get_inbox_item`, `resolve_inbox_task`, `answer_inbox_question`, `approve_work_item`, `decline_work_item`, `approve_ask`, `decline_ask`), automations (`list_automations`, `get_automation`, `create_automation`, `run_automation`, `delete_automation`, `update_automation`), agent (`ask`, `get_session`), sessions (`list_sessions`, `cancel_session`, `delete_session`, `add_context`), account and settings (`get_account`, `get_brief`, `list_wins`, `get_costs`, `get_loop_health`, `get_ai_traffic_install`, `connect_github`, `update_settings`), memory (`record_observation`, `write_skill`), setting up a workspace (`start_workspace`, `confirm_setup`), goals (`get_goals`, `set_goal`), projects (`list_projects`, `steer_project`), deliverables (`list_deliverables`), reports (`get_report`), and markets (`get_markets`, `preview_markets`, `get_brand_family`).

The destructive ones are `remove_prompt`, `remove_competitor`, `delete_automation` and `delete_session`. The ones that spend credits are `add_prompts`, `run_automation` and `ask`, plus `add_context` with `run: true`, `answer_inbox_question` when the run that asked has finished, and the run that answers a `steer_project` post. Everything else is free. When a customer asks why nothing happened, call `get_loop_health` first. The [tool reference](/mcp/tools) has every argument and return shape.

## Your assistant can set up the business, too

A person does not need the dashboard to start. Connected over OAuth, an assistant can call `start_workspace` with the business's domain, show the person what the agents drafted — the profile, the rivals, the platforms, the goals and the plans — and save what they keep with `confirm_setup`. Nothing is charged from chat: `confirm_setup` returns a checkout link, the person pays in their own browser, and the agents start work once it reads `active`. See [Set up a business from your assistant](/mcp/recipes#set-up-a-business-from-your-assistant).

Work an assistant starts carries its name in the app — "via Claude", "via Cursor" — so the people on the account can tell what was asked from chat. A client Attensira does not recognise is shown as "via your assistant".

If what you are building is your own code rather than an assistant, the [REST API](/api/overview) exposes most of these capabilities as conventional HTTP endpoints. It takes an API key only — no OAuth — and those keys do not expire.


  An assistant granted write access — by an API key minted as read and write, or by any OAuth connection, which always carries write — can delete prompts, competitors, automations and sessions, and can spend credits. Treat either credential as a workspace credential, not a read token.


## How authentication works

The server accepts two credentials, and it accepts both permanently. Which one you want depends on whether a human is present when the client starts.

| | OAuth | Static API key |
| - | - | - |
| Best for | Interactive AI clients — Claude, Claude Code, Cursor, VS Code, ChatGPT | Headless callers — n8n, CI, scheduled scripts |
| What you do | Paste the server URL and sign in through the browser | Mint a key and send `Authorization: Bearer atn_live_<32 hex>` |
| What sits on disk | A refreshable token the client manages. No secret you have to place | A long-lived key in a config file or secret store |
| Expiry | Access tokens are short-lived and refresh silently; you sign in again occasionally | Never, until you revoke it |
| How you take it away | Remove the server in the client | Revoke it in Settings → Your assistant, effective on the next request |

Prefer OAuth wherever a browser can open. There is no key to paste, so there is nothing to leak into a committed config file, and revoking one client leaves the others working. Use a key when nothing can complete a browser sign-in — that case is real and supported, which is why keys are not deprecated here. Full mechanics are in the [OAuth reference](/mcp/oauth); minting and scopes are in [API keys](/account/api-keys).

Either way, the credential names your organisation. Every tool that works on a workspace takes an optional `project`, so one connection reaches every workspace the organisation owns — an agency running three domains connects once, not three times. Omit it and the default is used: for a signed-in connection that is the organisation's only workspace, and for a static key it is the workspace the key was minted in. An organisation with several workspaces and a signed-in connection has no default, so tools ask you to name one. `get_account` lists the ids and answers without a workspace.

That is still a boundary. A workspace id outside your organisation comes back as a workspace that does not exist — the same answer as one that never existed, so naming an id you do not own tells you nothing.

An API key is displayed exactly once, because only a hash is stored; Attensira cannot re-display it, and a lost key is replaced rather than recovered.

## Where the server lives

The endpoint is `https://mcp.attensira.com/mcp`, and it speaks Streamable HTTP only. A client that speaks stdio alone cannot reach it directly and needs a bridge — see [Connect a client](/mcp/connect). `https://mcp.attensira.com/healthz` is unauthenticated and answers without a key, which makes it a useful reachability check when a client is silent about why it failed.

## Next steps


  
    Copy-pasteable setup for Claude Code, Cursor, VS Code, Codex, ChatGPT, n8n and more.
  

  
    Every tool, its arguments, and which ones cost credits.
  

  
    Endpoints, scopes, PKCE and token lifetimes, for client authors and reviewers.
  



  
    Worked prompts that get useful answers on the first try.
  

  
    See which applications have access, and revoke one.
  

  
    Minting, scopes, and the one-time reveal.
  



  Something not working? [Troubleshooting](/mcp/troubleshooting) covers 401s, sign-ins that never return, missing tools, revoked credentials, empty results, and timeouts.



# MCP recipes
Source: https://docs.attensira.com/mcp/recipes

Task-shaped recipes for the Attensira MCP server — the call to make, the chain behind it, how to read the result, and what it cannot tell you.

Each recipe below is a real question someone asks on a Monday morning. Every one gives you the single call that mostly answers it, the longer chain when the single call is not enough, a table mapping what you see to what it means, and — the part that matters — what the numbers genuinely cannot tell you.

All calls assume you are connected to `https://mcp.attensira.com/mcp` — over OAuth or with a workspace API key; the recipes read the same either way. See [the tool reference](/mcp/tools) for parameters and the scope each tool needs, and [Connect a client](/mcp/connect) if you are not connected yet.

## Find out where you lost ground this week

Start with workspace-level rates over a short window, then drill into the prompts that moved.

```json theme={null}
{ "tool": "get_analytics", "arguments": { "area": "visibility", "days": 7 } }
```

**The chain**


  
    Establishes whether anything moved at all at the workspace level. If every delta comes back `real: false`, stop — there is nothing to explain.
  

  
    Ranks prompts by how badly you are doing on them, so the losses surface first rather than being averaged away.
  

  
    The same call already splits the window by platform: one row each. A drop concentrated on one platform is a different problem from a drop across all of them. `get_analytics area=platform filter=chatgpt` opens one platform end to end, prompts included.
  

  
    `get_prompt` says who was named and cited instead; `answer_history` returns the answers themselves, verbatim, which is the only place you find out *why* the model stopped naming you.
  


**How to read it**

| What you see | What it means |
| - | - |
| Delta `{real: false, value: null}` | No change you can prove. Not a decline. Do not report it as one. |
| Your share of voice flat, a competitor's rising | You did not lose ground — someone else gained. Different response. |
| Drop on one model, others flat | Likely an answer-surface change on that model, not a content problem. |
| `value: null` on a model row with `tracked: false` | That model was never queried. It is not a 0%. |
| Rate down but `n` is 7 or fewer | One or two bad readings can move this. Widen `days` before acting. |

**What this cannot tell you.** A seven-day window on Starter sampling gives you very few readings per prompt, and a rate built on single-digit `n` swings hard for reasons that have nothing to do with you. Nothing here tells you *why* a model changed its answer: model providers swap the underlying models behind a surface without notice, and a rewritten system prompt on their side looks identical to a content problem on yours. You are seeing what changed, not who changed it.

## See which sources the models trust in your category

Source share tells you which domains the models actually pull from when answering your prompts.

```json theme={null}
{ "tool": "get_analytics", "arguments": { "area": "source", "days": 90, "order": "best_first", "limit": 50 } }
```

**The chain**


  
    A long window, because source share is a distribution and short windows make it lumpy. `best_first` puts the most-cited domains at the top.
  

  
    Splits one domain's citations by platform and by day. Perplexity and ChatGPT lean on visibly different domain mixes, and a category-wide average hides that. `deciding_sources` ranks the same domains by influence over a decision rather than by count.
  

  
    Shows where your own domain lands in the same window, so you can see whether you are in the mix at all.
  


**How to read it**

| What you see | What it means |
| - | - |
| One domain above roughly a fifth of all citations | Models treat it as the reference for your category. Being absent from it costs you. |
| A long flat tail of many small domains | No consensus source. Coverage breadth matters more than any single placement. |
| High source share, low `position` index | Cited often and cited early in the list. The strongest signal available here. |
| Your domain absent entirely | You were not cited in this window. Not proof you are uncitable. |

**What this cannot tell you.** Source share is citations to a domain over all citations in the window — it is not traffic, authority, or influence on the answer text. A model can name your brand in prose while citing a competitor's blog, and a model can cite a page it disagreed with. The list also reflects your prompts, not the category: if your tracked prompts skew to one topic, the source mix skews with them. Widen the prompt set before calling it a picture of the category.

## Check whether a competitor is gaining

```json theme={null}
{ "tool": "get_analytics", "arguments": { "area": "competitor", "days": 30 } }
```

**The chain**


  
    Gives each tracked competitor's share of voice on the same denominator as yours.
  

  
    Your own rate over the same window, so you can compare movement rather than levels.
  

  
    Finds the specific prompts where the gap lives. Competitors rarely gain evenly.
  

  
    Read the answers. What the model says about them is the actual competitive intelligence.
  


**How to read it**

| What you see | What it means |
| - | - |
| Their rate up, yours flat, both deltas `real: true` | They gained. You did not lose. The market grew around you. |
| Both rates up | The models are naming more brands per answer, not reshuffling ranks. |
| Their rate up, yours down | The closest thing to genuine displacement — and worth reading answers for. |
| Rates summing well past 100% across brands | Expected. Answers name several brands, so these never sum to 1. |

**What this cannot tell you.** These are not shares of a fixed pie. Competitor share of voice uses the same denominator as yours, so it measures how often a brand appears in *your* prompts' answers — nothing about their overall market presence, spend, or growth. A competitor you have not registered with `add_competitor` is invisible here and will read as absent, which is not the same as not appearing. There is no sentiment measurement, so a mention that trashes them counts exactly like a recommendation.

## Turn a losing prompt into a plan

The one prompt you keep losing is the most actionable object in the product.

```json theme={null}
{ "tool": "get_prompt", "arguments": { "prompt": "best project management tool for agencies", "days": 90 } }
```

**The chain**


  
    Returns per-model visibility rates and, in the same call, the competitors seen in this prompt's answers and the sources those answers cited — no need to call get\_analytics separately for either.
  

  
    Tells you what already works, which is a better template than guessing.
  

  
    The answers themselves, verbatim. Read what the model actually said before deciding what to change; a rate is a summary of these rows, not a substitute for them.
  

  
    Free, and already ordered: what is deciding the answer today on each platform, the one next move per platform with the sentence of evidence behind it, and a credits estimate.
  

  
    Costs credits, and earns them here: the agent reasons across the slices you just pulled and drafts a plan. Poll `get_session` if it returns `status: "running"`.
  


**How to read it**

| What you see | What it means |
| - | - |
| Competitors listed in `get_prompt`, you absent, sources pointing to third-party lists | A placement problem. You need to be on those lists. |
| Your visibility rate low but not zero | The model names you sometimes. Consistency, not existence, is the gap. |
| A cited source you do not own, with a high `n` | The model has a go-to reference for this prompt that is not you. |
| Share of voice 0 with `n` above 20 | A real, measured absence worth working on. |

**What this cannot tell you.** Nothing here predicts that a change will work. There is no attribution path from a page edit to a later mention — you can only change something and re-measure after enough calendar days have passed to build a new `n`. Sampling is at most once per prompt per model per day, so a plan executed today shows its earliest possible signal tomorrow and a trustworthy one much later. Answers also vary run to run at the model's discretion; a single collected answer is an anecdote.

## Set up a weekly report

```json theme={null}
{ "tool": "create_automation", "arguments": {
  "name": "Monday visibility digest",
  "instruction": "Summarise the last 7 days: workspace mention and citation rates with deltas, the five worst prompts, per-model split, and any competitor whose rate moved with real:true. Ignore deltas where real is false.",
  "trigger": "weekly",
  "timezone": "Europe/London",
  "run_at_minute": 540
} }
```

**The chain**


  
    Check what already exists. Two automations doing the same job bill twice.
  

  
    Free to create. Write the instruction to name the exact areas and windows you want, and to say explicitly that unproven deltas should be ignored. Pass `tools` if you want to restrict which tools this automation may call, and `run_at_minute` with a `timezone` to pin when it fires.
  

  
    Test it once now rather than waiting a week. The key prevents a retry from producing a second billed run.
  

  
    Read the run history to confirm it produced what you expected before trusting it unattended.
  


**How to read it**

| What you see | What it means |
| - | - |
| Digest reports every delta as a change | Your instruction did not exclude `real: false`. Rewrite it. |
| A week with no findings | Normal. Most weeks contain no statistically real movement. |
| Numbers differing from the dashboard | Check the window: 7 days against a 30-day default is a different slice, not a bug. |
| A run missing from the history | The scheduled run did not execute. Re-run manually with a fresh key. |

**What this cannot tell you.** A weekly cadence over a daily sampling floor means each report rests on at most seven readings per prompt per model — three times that on Growth, Business and Agency, since sampling depth is three there. That is thin for week-over-week comparison, and a digest that dutifully reports movement every week is reporting noise.

## Audit which pages actually get cited

```json theme={null}
{ "tool": "get_analytics", "arguments": { "area": "page", "days": 90, "order": "best_first", "limit": 100 } }
```

**The chain**


  
    Ranks your own URLs by citation activity. Long window, because page-level citations are sparse.
  

  
    Shows which prompts and which models each page earns citations from.
  

  
    Shows AI crawler activity against your site — which pages were fetched at all.
  


**How to read it**

| What you see | What it means |
| - | - |
| Page cited often across many prompts | A hub. Keep it current; it carries disproportionate weight. |
| Page crawled but never cited | Reachable and not chosen. A content problem, not an access one. |
| Page never crawled and never cited | Start with access — nothing can cite a page it has not fetched. |
| Citations concentrated on one model | That surface favours your format. Others may need different structure. |

**What this cannot tell you.** Crawlers are not models: a crawler fetches pages, a model answers prompts, and a fetch is not a citation or a plan to make one. Crawler data also has a hard technical limit — AI crawlers do not execute JavaScript, so a client-side beacon, however written, has never seen one and never will; by the time the script runs, the only visitors left are humans with browsers. Use the install instructions the product generates at Settings for server-side collection. Finally, `position` is a mean index within a citation list per source domain — it is not a rank against competitors and cannot be read as one.

## Hand several pieces of work to the agents

Your assistant does not have to do the work itself, or ask for it one piece at a time. Send the work to `ask` as lines, one piece of work per line, and the Chief of staff hands each line to the project whose crew does it.

```json theme={null}
{ "tool": "ask", "arguments": { "question": "Hand these off:\nWrite an article comparing our plans with the two biggest alternatives\nFix the meta descriptions on the pricing pages\n#outreach pitch us to the three directories that decide \"best crm for startups\"" } }
```

**The chain**


  
    Up to 20 lines, each in your own words and specific enough to start on. A line that names a project with its `#slug` goes there; the rest are routed by what each project is for. See [Delegation](/agent/delegation) for the rules.
  

  
    The answer says, in a sentence or two, what went where and what happens next. The same session in the dashboard shows the **Handed off** card, one row per line.
  

  
    The session is waiting on a person: an approval of the hand-off (in Ask mode), or a choice of project for lines Attensira was not sure about. Both are answered in that session in the dashboard; `get_session` reads `running` until then.
  

  
    What the crews file as they work: drafts, pull requests and pitches waiting for approval. A task handed off is not a task done.
  


**How to read it**

| What you see | What it means |
| - | - |
| A line handed to a project | Filed there as a task of its own, queued for the project's crew. The work happens in the task, not in this session. |
| A line waiting on a choice of project | Attensira was less than 60% sure, or the line named a project it could not find. Someone picks in the dashboard, with one click. |
| The work done in the session itself | The workspace has no projects taking work, so the Chief of staff does it there instead, or offers to make it a project. |
| A line not handed off | Filing it failed, with the reason; for example, the balance is spent. Send that line again. |

**What this cannot tell you.** Routing reads each project's name and focus, not the work it will produce, so a confident route is a statement about where the line belongs, not about how the work will turn out. Over MCP you can neither approve the hand-off nor pick a project; a person does that in the dashboard. And nothing here promises an outcome: Attensira guarantees the volume of work, not visibility. Measure what the work changed with [before and after](/measure/before-and-after), once it has shipped.

## Set up a business from your assistant

A person can start from chat: "set up Attensira for example.com". The agents draft the business from its site; the person decides what to keep; the person pays in their own browser. Nothing in this chain can charge them.

```json theme={null}
{ "tool": "start_workspace", "arguments": { "domain": "example.com" } }
```

**The chain**


  
    Creates the workspace, or returns the one already started for that domain. While `status` is `drafting` the site is still being read: call it again in a little while. It never creates a second workspace.
  

  
    Walk them through `profile.facts` (each with the page it was read from), `competitor_suggestions`, `platform_suggestions`, `proposed_goals`, the `plans` and the `budget` range. When `status` is `needs_input`, ask the `question` on each fact that has no value. Ask; do not decide for them.
  

  
    Pass the `workspace_id`, the `plan` id they chose, and the competitors, platforms, goals, budget and profile corrections they kept. It returns `checkout_url`.
  

  
    Send the `checkout_url` and read them `budget_consent.line`, the top-up rule they agree to by paying, as written. They pay in their browser. If they come back later, call `confirm_setup` again for a fresh link.
  

  
    Same call, same domain. `active` means the plan is paid and the first readings are being taken; `first_readings_ready` means there is enough to read a brief from.
  

  
    `get_goals` shows what the agents are aiming at and `list_projects` the crews doing the work. `get_report` has something to return once the first report is published.
  


**How to read it**

| What you see | What it means |
| - | - |
| `status: drafting` | The site is still being read. Poll; do not invent the profile yourself. |
| `status: needs_input` with a `reason` | The site did not say enough. The questions in `profile.facts` are what to ask. |
| A fact with a `value` and a `source_url` | Read off that page. Show it, so the person can correct it. |
| `status: awaiting_payment` | The choices are saved and the checkout is theirs to finish. Nothing is charged until they pay. |
| `confirm_setup` sends them to the billing page | They already have a card on file, so they confirm the plan there, in person. |
| `start_workspace` refuses a second domain | An account holds one workspace waiting for payment at a time. Finish that one first. |

**What this cannot tell you.** The draft is the agents' reading of the site and the answers, and it can be wrong: a rival they suggest may not be one, and a fact may be out of date. The person's corrections are what make it right. Until the first readings land there are no numbers at all, so do not describe the business's visibility before `first_readings_ready`.

## Steer a project

Projects are where the crews work. A person steers one in plain words — "focus on the pricing comparison pages this month", "stop pitching review sites" — and the crew replans. Steering is direction, never approval.

```json theme={null}
{ "tool": "list_projects", "arguments": {} }
```

**The chain**


  
    Every project, with its crew, its goal, `needs_you` and `last_activity_at`. Pick the one the person means. Its `id` is the project; `project`, if you pass one, is the workspace.
  

  
    The project's `direction` so far and its latest `feed`, so you do not repeat what it was already told.
  

  
    The person's words, verbatim, at most 4,000 characters. Pass an `idempotency_key` so a retry does not post twice. Tell them the `ack`.
  

  
    The crew's reply and any plan change land in the feed as their own posts. Anything that needs a yes lands in the Inbox: `list_inbox`.
  


**How to read it**

| What you see | What it means |
| - | - |
| `plan_change: null` on the reply | Always. A steer changes no plan by itself; the crew's run decides, and posts what it changed. |
| `run_id: null` with `reply_error` | The post is saved, but the crew's reply could not start. The error says why. |
| `replayed: true` | That key was already used. Nothing was posted again. |
| `needs_you` above zero on the project | Work is waiting on a person. Steering does not clear it; approving or declining does. |

**What this cannot tell you.** A steer is not an approval and not a setting. "Approve everything and publish" posted as direction is kept as words in the feed; what the crew may ship without asking is set on the approval settings, by the person. The crew reads the direction on its next run, so nothing changes on the site the moment you post.

## What did my team ship this week, and what is waiting on me?

The Monday question. What the crews delivered, what it proved, and the decisions only a person can take.

```json theme={null}
{ "tool": "list_deliverables", "arguments": {} }
```

**The chain**


  
    Newest first. Keep the rows whose `shipped_at` (or `created_at`, for work not shipped yet) falls in the week. Each says where it lives in `location` and where it stands in `status_label`. Page with `next_cursor` until you are past the week.
  

  
    The three decisions to take first, and how many are open in all. `get_inbox_item` on a ref reads it in full.
  

  
    Platforms that named or linked the brand for the first time on a prompt. Quote each row's `sentence`.
  

  
    On Mondays the weekly note, with shipped work and early signals in the order the email reads them.
  


**How to read it**

| What you see | What it means |
| - | - |
| `result: null` | Nothing to measure yet: not shipped, or nothing tracked names it. Not a failure. |
| `result` with `verdict: null` | Shipped, and the re-read that will measure it has not come back. Its `label` says when. |
| A win's `quote: null` | No excerpt of the answer passed the honesty check. The win still stands; quote the `sentence`. |
| An empty triage `top` | Nothing is waiting on a person. The crews are not blocked on you. |

**What this cannot tell you.** Shipping is not moving. A deliverable says what was done; only a re-read says whether an answer changed afterwards, and a win is a first since readings began, not proof that a given piece of work caused it. Report what shipped, and report movement only where a re-read proved it.

## Where to go next

Before acting on any of these numbers, read [Reading the numbers](/measure/reading-the-numbers) for how `null`, zero, small `n`, and unproven deltas differ. Full parameter detail for every tool is in [the tool reference](/mcp/tools).


# MCP tool reference
Source: https://docs.attensira.com/mcp/tools

Every Attensira MCP tool — parameters, return shapes, which credential each needs, and which ones spend credits or delete things.

The Attensira MCP server serves its tools at `https://mcp.attensira.com/mcp` over Streamable HTTP. Every tool except the two that set up a new workspace takes an optional `project`; omit it and your credential's own workspace is used. Each tool below says whether it is read-only, whether it spends credits (`add_context` does only with `run: true`, and `answer_inbox_question` only when it restarts a finished run), and whether it deletes data permanently. This page lists every tool with its parameters, defaults and return shape, grouped the way the server lists them.

The agents do the SEO work; the tools are how you, or the assistant you ask, steer it: read what happened, set what to aim for, answer what is waiting on a person, approve or decline what the agents propose. A tool never approves anything on its own reading of a row. Approvals and settings change on a person's instruction.

Every tool has a twin in the [REST API](/api/endpoints) with the same payload, and each says which endpoint below, except the markets reads, which say **MCP-only**. Use MCP when an assistant is doing the work; use REST when your own code is.

[Projects](/agent/projects) are read with `list_projects` and steered with `steer_project`; [Projects over MCP](/agent/projects#projects-automations-and-the-api) describes both. Creating a project and changing its settings are in the dashboard only. A project's runs and automations are ordinary sessions and automations, so `list_sessions`, `get_session`, `list_automations` and `get_automation` include them.

## Words that collide: `project` and `id`

On every tool, `project` is the **workspace**: one domain, with its own prompts, competitors and plan. It has meant that since the first tool shipped, and renaming it would break every client already calling.

A project in the product's sense, an area of work with its own crew of agents, is a **work area** on the wire. `list_projects` and `steer_project` take its id as `id`, and every payload that points at one, a goal or a deliverable, carries it as `work_area_id`. Passing a project's id as `project` is refused with a message naming the mistake.

## Which credential each tool needs

A **workspace API key** is minted as *read* or *read and write*. Read tools work with either; the tools in the left column need a read-and-write key. Calling one with a read key returns `403` with `type: "forbidden"` and the challenge `WWW-Authenticate: Bearer error="insufficient_scope", scope="write"` — the credential is valid, it simply may not do this. Scope is fixed when the key is minted and cannot be widened: mint a read-and-write key and swap it in.

An **OAuth connection** always carries read and write. Approving a client grants it your organisation, and there is no read-only consent — the scopes in the token are the authorization server's own (`openid`, `profile`, `email`, `offline_access`, `user:org:read`), none of which names an Attensira permission. Restrict a connected assistant by what you ask it to do, or by the tool allow-list your client offers, not by scope.

| Needs write | Works with read |
| - | - |
| `add_prompts`, `remove_prompt`, `add_competitor`, `remove_competitor`, `resolve_inbox_task`, `approve_work_item`, `decline_work_item`, `answer_inbox_question`, `approve_ask`, `decline_ask`, `create_automation`, `run_automation`, `delete_automation`, `update_automation`, `ask`, `cancel_session`, `delete_session`, `add_context`, `connect_github`, `update_settings`, `record_observation`, `write_skill`, `start_workspace`, `confirm_setup`, `set_goal`, `steer_project` | `search`, `get_analytics`, `deciding_sources`, `win_plan`, `get_prompt`, `get_page`, `query_evidence`, `list_inbox`, `get_inbox_item`, `list_automations`, `get_automation`, `get_session`, `list_sessions`, `get_account`, `get_ai_traffic_install`, `get_brief`, `list_wins`, `get_costs`, `get_loop_health`, `answer_history`, `answer_slice`, `citation_timeline`, `page_history`, `get_goals`, `list_projects`, `list_deliverables`, `get_report`, `get_markets`, `preview_markets`, `get_brand_family` |

`get_account` returns the scopes in force, so call it first from a new integration rather than discovering the gap on a failed write.


  One tool is **key-only**: `get_ai_traffic_install` hands back your workspace's signing secret, and a signed-in connection is refused there on purpose — a consented OAuth token must never be able to read one credential out and turn it into another. Call it with an API key.


## What your assistant does shows its name

Work an assistant starts is labelled with it in the app. A proposal that came out of an `ask` from Claude reads "via Claude" in the Inbox, and so does the work a project's crew does in answer to a `steer_project` post sent from Claude. The MCP server passes on the name your client reports for itself; Attensira shows it only when it is one it recognises (Claude, Claude Code, ChatGPT, Cursor, Codex, VS Code), and otherwise writes "via your assistant" rather than a name it cannot vouch for. The label is display only. It grants nothing and changes nothing about what the call may do.

## Before you start: what costs money and what destroys data

These tools spend credits or destroy data. Nothing else on this page produces a bill or erases history.

| Tool | Effect | Why |
| - | - | - |
| `add_prompts` | Costs credits | Each new prompt is read across your tracked platforms from the next run onward |
| `run_automation` | Costs credits | Triggers an automation immediately instead of waiting for its schedule |
| `ask` | Costs credits | Runs the agent on an agent model |
| `add_context` with `run: true` | Costs credits | Queues the session again to act on the new context now |
| `answer_inbox_question` | Costs credits when the run that asked had finished | A finished, failed or suspended run starts a new turn on the answer. A run still going reads it without a new turn |
| `steer_project` | The reply costs credits | Posting is free; the project's crew answers the post with an ordinary agent run |
| `remove_prompt` | Destructive | Stops tracking a prompt; its history is no longer collected |
| `remove_competitor` | Destructive | Stops scoring a rival; past readings keep what they recorded |
| `delete_automation` | Destructive | Removes the automation and its schedule. Past runs are kept as sessions |
| `delete_session` | Destructive | Deletes a finished session and its trace |

`start_workspace` and `confirm_setup` never charge anything. `confirm_setup` hands back a checkout link, and the person pays in their own browser.


  `run_automation` accepts an `idempotency_key`. Pass one on every call. It maps to the `Idempotency-Key` header upstream, so a retried or duplicated call does not produce a second billed run.



  The `get_analytics` `area` values for a topic rollup and a per-surface rollup are `topic` and `platform`. A **platform** is a stable identifier for an AI answer surface (ChatGPT, Perplexity, Google AI Overviews, Claude, Gemini) that survives the underlying model being replaced, so two windows a year apart stay comparable on the same key. The older wire words `group` and `engine` are no longer accepted.


## Reading what comes back

Every rate is `{value, n}` and every delta is `{value, real}`. A `value` of `null` means not measured; `0` with `n > 0` is a measured zero. Per-platform metrics live in a `platforms` map keyed by platform, and each entry carries `{tracked, readable}`: `tracked: false` means your plan does not include that platform, so it was never queried, and `readable: false` on a tracked platform means nothing came back in the window that could be scored. Treating either as 0% invents a failure that did not happen.

Every ranked list carries `id` and `label` on each row, so the row can be handed straight to the detail tool that takes the id. For the full treatment of null against zero, small `n`, and when a delta is worth acting on, read [Reading the numbers](/measure/reading-the-numbers).

## Find and measure

Ask these tools the questions you would ask an analyst: "which prompts am I losing on Perplexity?", "what changed since last week?", "which sources decided the answers?", "does anyone cite our pricing page?".

### search

Read-only. No credits. REST: `GET /v1/search`.

One search across the workspace's prompts, pages, automations and agent sessions. Use it to locate something by wording when you do not have an id — a prompt that sounds like the one you have in mind, the page you half remember the title of.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `query` | string | — | Required |
| `limit` | integer | 20 | Maximum 50 |
| `offset` | integer | 0 | For paging through results |

Returns `total`, `has_more` and `hits`. Each hit carries `type` (`prompt`, `page`, `automation` or `session`), `id`, `title` and, where there is one, a `snippet`. A hit whose label starts with the query ranks above one that merely contains it; at equal footing prompts come before pages, then automations, then sessions. **Every string on a hit is capped at 400 characters**, so a complete model answer can never come back through `search` — for the answers themselves, use `answer_history` or `answer_slice`.

Page on `has_more`. `total` counts what matched up to the session-search ceiling, so it is a floor rather than a grand total.

### get\_analytics

Read-only. No credits. REST: `GET /v1/analytics/{area}`.

The main measurement tool. One call returns a ranked slice of your tracking data for a window.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `area` | enum | — | Required. One of `visibility`, `prompt`, `page`, `source`, `competitor`, `topic`, `platform`, `bot` |
| `days` | integer | 30 | 1–365; out of range is clamped |
| `order` | enum | `worst_first` | Or `best_first`. Rows nothing was measured for sort last under either |
| `limit` | integer | 20 | Maximum 100 |
| `filter` | string | — | Case-insensitive substring on row labels. For `area: "platform"` it names the platform and is required |

| `area` | One row per | Answers |
| - | - | - |
| `visibility` | tracked platform | Share of voice and citation rate per platform, with the workspace's own rate alongside — the orienting call |
| `prompt` | tracked prompt | Which prompts you win and lose, with topic, country and the per-platform split |
| `page` | page of yours | Which of your pages get cited, as a rate on every answer sampled |
| `source` | external domain | Which domains the models cite in your category, as a share of every citation |
| `competitor` | rival | Workspace-wide share of voice per competitor, with the id `remove_competitor` takes |
| `topic` | prompt topic | Per-topic rollup; prompts filed under no topic are one row with id `no_topic` |
| `platform` | the platform named in `filter` | That platform end to end, with its prompts listed under the envelope |
| `bot` | AI system that fetched your site | Reads, pages reached, purpose mix, when it last came |

The envelope is `{area, days, window, order, filter, rows, total_rows, truncated}` plus the area's own headline numbers. `total_rows` is how many rows survived the filter and `truncated` whether `limit` cut them, so a short list can be told from a cut one. Every row carries `id`, `label`, the rates that apply to the area and, where the area has one, a `platforms` map.

```json Response — area: prompt theme={null}
{
  "area": "prompt", "days": 30, "order": "worst_first", "total_rows": 12, "truncated": true,
  "visibility": { "value": 0.3, "n": 20 },
  "rows": [
    {
      "id": "prm_8f21", "label": "best crm for small teams",
      "topic": "buying intent", "country": "US",
      "visibility": { "value": 0.12, "n": 25 }, "citation": { "value": 0.04, "n": 25 },
      "position": null, "delta": { "value": null, "real": false },
      "platforms": {
        "chatgpt":    { "tracked": true, "readable": true,  "rate": { "value": 0.2, "n": 15 }, "delta": { "value": null, "real": false } },
        "perplexity": { "tracked": true, "readable": false, "rate": { "value": null, "n": 0 } }
      },
      "answers_read": 25, "failures": 1
    }
  ]
}
```

`area: "bot"` rows carry `visits`, `pages`, `purpose_mix`, `last_seen` (`null` when it never came) and `agents` instead of the rate fields, and answer with a `note` when no edge collector has ever reported — zero rows there means uninstrumented, not uncrawled.


  `area: "visibility"` returns share of voice per platform — the fraction of runs on that platform that named you, not a share of a category that sums to 100%. Competitor rates (`area: "competitor"`) use the same denominator as yours, so they do not sum to 1 either.


### deciding\_sources

Read-only. No credits. REST: `GET /v1/deciding-sources`.

Which pages and domains actually decided the AI answers — the listicles, reviews and docs the models leaned on — whether you already appear on each, and the next move for each. Unlike `get_analytics` with `area: "source"`, which ranks by citations, this ranks by influence over a decision. See [Deciding sources](/measure/deciding-sources).

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `prompt` | string | — | Optional. One tracked prompt, by id or exact text. Omit for every prompt |
| `days` | integer | 30 | 1–365 |

Returns `groups`, most influential first, one per prompt × platform × surface. Each group carries a `label` in words, `n` (successful answers read), `present` (whether any source in the top ten is yours), a `recommendation`, and `sources` ranked by weight: `domain`, `url` (the exact page when one carried it, else `null`), `citations`, `share`, `present`, and `action` — the next move in words, such as "get listed" or "fix your own page".

### win\_plan

Read-only. No credits. REST: `GET /v1/win-plan`.

The plan for winning one tracked prompt: what is deciding the answer today, what to change, and in what order. Ask for a prompt the workspace is losing — `deciding_sources` says which those are.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `prompt` | string | — | Required. Prompt id or exact text |
| `days` | integer | 30 | 1–365 |

Returns `prompt`, `n`, `evidence` (`measured`, `published` or `hypothesis`), `steps` in order — each with a `title`, a `why` sentence built from the readings, a `target` page or domain (or `null` when it is your own site generally), the `platform` it acts on and a `credits_estimate` — plus `rereads_at` and `credits_estimate` for the whole plan. `rereads_at` is the day the next booked re-read for that prompt is actually read (see [Before and after](/measure/before-and-after)). Nothing here claims movement: the footer uses the no-change wording until the workspace has a re-read that came back moved.

### get\_prompt

Read-only. No credits. REST: `GET /v1/prompts/{prompt}`.

Everything recorded for one prompt: where it is filed, its rate per platform, and who was named and cited instead.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `prompt` | string | — | Accepts either a prompt id **or** the prompt's exact text |
| `days` | integer | 30 | Window for the returned rates |

Returns `id`, `text`, `topic` (`null` when unfiled), `country` (`null` for the workspace default), `visibility`, `citation`, `delta`, `position`, `answers_read`, `failures`, a `platforms` map, `competitors` (each with `name` and a `rate` on this prompt's own answers), `sources` (each with `url`, `domain` and a `rate`), and `why` — the standing in sentences, each built from a number on the same response. Passing exact text saves a `search` round-trip when you already know the wording; inexact text will not match.

### get\_page

Read-only. No credits. REST: `GET /v1/pages`.

How one page performs in AI answers: its citation rate on every answer sampled, per platform, and the prompts whose answers cited it. For whether AI systems are fetching the site at all — as opposed to citing it — call `get_analytics` with `area: "bot"`; a model can only cite a page it has fetched, so that is worth checking before rewriting content.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `url` | string | — | Required. Absolute URL, exactly as it appears on the site |
| `days` | integer | 30 | Window |

Returns `url`, `title` (`null` when it could not be read), `citations` and `citation` as a rate on `total_answers`, a `platforms` map whose entries carry the page's citation rate on that platform's own answers, `prompts` — each with `id`, `text`, `citations` and a `rate` on that prompt's answers — and `in_sitemap`. The `delta` is `{value: null, real: false}`: the page fold has no earlier window to compare against, and the contract's word for that is "no basis", not a number.

### query\_evidence

Read-only. No credits. REST: `POST /v1/evidence/query`.

Your own data question as a table: one metric, grouped by up to two dimensions, filtered, over a window. "Share of voice by engine and week", "Google clicks by page", "wrong claims by topic". It reads the same stored days and folds them the same way as the dashboard boards, so the numbers match what the dashboard shows for the same window. Use `get_analytics` for the ready-made boards and `query_evidence` when the cut you need is not one of them.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `metric` | enum | — | Required. See the metric table below |
| `group_by` | string\[] | — | At most two of `day`, `week`, `topic`, `prompt`, `engine`, `surface`, `competitor`, `domain`, `url`, `page`, `intent`, `stage`, `persona`, `state`, `keyword`, `country`, `property`. Not `day` and `week` together. Omit for one total |
| `days` | integer | 30 | 1–365, ending today. Ignored when `from` and `to` are given |
| `from`, `to` | string | — | First and last day, `YYYY-MM-DD`, inclusive. Both or neither; the span is at most 365 days |
| `topic_ids`, `prompt_ids` | string\[] | — | At most 200 ids each. An id the workspace does not have is refused |
| `engines` | string\[] | — | Platforms: `chatgpt`, `perplexity`, `google_ai`, `claude`, `gemini` |
| `surface` | enum | — | `consumer` or `api` |
| `country` | string | — | One market, ISO 3166-1 alpha-2 |
| `markets` | string\[] | — | Several markets, e.g. `["US","DE"]`. Give `country` or `markets`, not both |
| `property_ids` | string\[] | — | Parts of the brand family, with `group_by: ["property"]` only |
| `competitor` | string | — | A tracked rival's domain |
| `intent` | string | — | `informational`, `comparison`, `commercial` or `transactional` |
| `stage` | string | — | `awareness`, `consideration`, `decision` or `retention` |
| `state` | enum | — | `won`, `contested`, `lost`, `absent` or `unmeasured` |
| `domain` | string | — | A cited domain |
| `text_contains` | string | — | A phrase in the prompt's text, case-insensitive, at most 80 characters |
| `limit` | integer | 50 | 1–200 rows |

| Metric | Value | Can be grouped by |
| - | - | - |
| `share_of_voice` | Share of answers that named you, 0 to 1 | time, prompt fields, `engine`, `surface`, `country`, `property` |
| `citation_rate` | Share of answers that cited your domain, 0 to 1 | time, prompt fields, `engine`, `surface`, `country` |
| `position` | Your average place in the answer's brand order | time, prompt fields, `engine`, `surface`, `country` |
| `n` | Successful answers read, a count | time, prompt fields, `engine`, `surface`, `country` |
| `source_share` | A cited domain's or page's share of every citation | time, prompt fields, `engine`, `surface`, `domain`, `url`, `country`. Needs `domain` or `url` grouped, or the `domain` filter |
| `deciding_weight` | A domain's weight in the [deciding sources](/measure/deciding-sources) | time, prompt fields, `engine`, `surface`, `domain`. Needs `domain`. By day or week, at most 90 days |
| `competitor_share` | Share of answers that named a rival, 0 to 1 | time, prompt fields, `engine`, `surface`, `competitor`, `country`. Needs `competitor` grouped or filtered |
| `competitor_position` | A rival's average place in the brand order | As `competitor_share` |
| `prompt_state_count` | Prompts in each state, a count | prompt fields only |
| `wrong_claims` | Claims about you that a confirmed fact contradicts, a count | time, prompt fields, `engine`, `surface` |
| `gsc_clicks`, `gsc_impressions`, `gsc_position` | Search Console clicks, impressions, average position | `day`, `week`, `page`, `keyword` |
| `serp_position`, `ai_overview_presence` | Your Google rank for a tracked search; share of days with an AI Overview | `day`, `week`, `keyword`, `page` |
| `ai_fetches`, `ai_referred_visits` | AI-crawler fetches of a page; visits an AI assistant sent | `page` |

"Time" is `day` or `week`; "prompt fields" are `topic`, `prompt`, `intent`, `stage`, `persona` and `state`. `page` is one of your own pages as Google and your traffic record it; `url` is a cited page, anybody's. The Google metrics are explained in [Google search](/measure/google-search).

A question with no honest answer is refused before anything is read. The tool returns an error (status `400`) whose message names the problem and says what to ask instead:

| Refused because | The message says |
| - | - |
| A metric or dimension outside the lists above | The valid ones |
| The metric cannot be grouped that way | What it can be grouped by |
| The metric does not take that filter | What it can be filtered by. The filter is refused, never ignored: ignored, it would return unfiltered numbers under a question that says they were filtered |
| A share of one rival, domain or page, without saying which | The dimension to group by or the filter to set |
| `group_by` names more than two dimensions, one twice, or both `day` and `week` | The rule it broke |
| A malformed or out-of-range value: a day that is not `YYYY-MM-DD`, a `from` after `to`, a span over 365 days, a `text_contains` over 80 characters, an unknown engine, intent, stage or state | The valid form or values |
| A topic, prompt or brand property id this workspace does not have | The id |

A question too broad to answer in time is refused with status `422` and a message that says what to narrow, never answered with whatever rows arrived first.

```json Response — metric: share_of_voice, group_by: ["engine"], days: 30 theme={null}
{
  "metric": "share_of_voice",
  "group_by": ["engine"],
  "window": { "from": "2026-09-03", "to": "2026-10-02", "days": 30 },
  "day_basis": "slot_day",
  "unit": "share",
  "rows": [
    { "key": { "engine": "chatgpt" }, "labels": { "engine": "ChatGPT" }, "value": 0.31, "n": 84,
      "ref": { "collection": "prompts", "ids": ["prm_8f21", "prm_1c07"] } },
    { "key": { "engine": "gemini" }, "labels": { "engine": "Gemini" }, "value": null, "n": 0 }
  ],
  "gaps": [{ "key": "engine=gemini", "reason": "no_readings" }],
  "truncated": false
}
```

* `value` is in the table's `unit`: `share` (0 to 1), `position`, `count` or `weight`. `null` means unmeasured, and a gap names why; `0` is a measured zero.
* `n` is on every row: successful answers for an answer rate, placed answers for a position, citations for a source share, impressions for Search Console, readings for rank tracking, the count itself for a count. Read it before the value.
* `labels` gives the readable name where a key is an id: a topic's name, a prompt's text.
* `ref` names the records the row was counted from, at most five ids. For answer metrics they are prompt ids, which `answer_history` opens.
* `gaps` lists everything the answer could not measure, as `{key, reason}`: a span of days before your first tracked prompt (`before_first_reading`), a source that is not connected, days not recorded yet, a row with no value. An empty list is the claim that nothing is missing.
* `day_basis` says which calendar the day labels count in: `slot_day`, `search_console` or `utc`. See [Google search](/measure/google-search#day-basis).
* `truncated` is `true` when there were more rows than `limit`. Without a time dimension the strongest rows are kept; with one, the newest whole days or weeks.

Grouping by `topic` uses the topic each prompt is filed under now, so moving a prompt regroups its whole history.

## The receipts

The numbers above are folds over stored answers. These tools hand back the rows themselves, verbatim, which is what `record_observation` cites and what a person asks for when a number looks wrong. Their REST twins live under `/v1/memory/`.

### answer\_history

Read-only. No credits. REST: `GET /v1/memory/answers/history`.

Every sampled answer to one tracked prompt, verbatim and newest first, with citations and reading ids — the receipts behind `get_prompt`.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `prompt` | string | — | Required. Prompt id, or the exact text |
| `platform` | string | — | Optional. One platform; omit for all |
| `days` | integer | 28 | 1–365 |
| `limit` | integer | 10 | Readings per page, 1–50. A reading is one prompt on one platform on one day and carries every sample taken |
| `cursor` | string | — | `next_cursor` from the previous page |

Returns `answers`, `readings` (on this page), `has_more` and `next_cursor` (`null` at the end). Each answer carries `reading_id` and `sample` (its index within the reading), `prompt_id` and `prompt`, `platform`, `surface` (`consumer` or `api`), `country`, `day`, `answered_at`, `text` verbatim, `text_bytes`, `truncated`, `mentioned`, `cited_own_domain`, `citations` in order (`url`, `domain`, `position`, and `context`: the sentence of the answer that carries the link, `""` when the text does not mention it, `null` when it was never looked for; see [Citations and sources](/measure/citations-and-sources#what-was-each-link-cited-for)), `competitors_named`, `position` (your place among the names you track, absent when you were not named), `stance` (`positive`, `neutral`, `negative`, `none` or `unscored`; see [Sentiment](/measure/sentiment)), `brands` (the [full brand order](/measure/share-of-voice#the-full-brand-order), each brand with its own stance), `claims` (the [factual statements](/measure/claims) the answer makes about a brand, quoted), and `failed`. `brands` and `claims` are `null` until the answer has been read for them and `[]` when it was read and holds none. Answers over 8 KB are cut on a character boundary and flagged `truncated: true`, with `text_bytes` carrying the full length. A failed sample is listed with `failed: true` and an empty `text`, never dropped: a bad week must not look like a quiet one.

### answer\_slice

Read-only. No credits. REST: `GET /v1/memory/answers/slice`.

Every answer matching a predicate, verbatim. Terms AND together and select the sample; at least one is required.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `platforms` | string\[] | — | Any of the tracked platforms |
| `country` | string | — | ISO 3166-1 alpha-2 |
| `topic_id` | string | — | The prompts filed under this topic now. A prompt moved between topics answers under the topic it is in today |
| `prompt_ids` | string\[] | — | These tracked prompts, at most 100 |
| `surface` | enum | — | `consumer` is what a person sees; `api` the model's API |
| `model` | string | — | The start of the model's name, case-insensitive: `gpt-5` matches `gpt-5` and `gpt-5-mini` |
| `competitor_named` | string | — | A rival's domain the answer named or linked |
| `cited_domain` | string | — | A domain among the citations; subdomains match |
| `brand_named` | boolean | — | `true`: named the brand; `false`: did not |
| `sentiment` | enum | — | Your brand's stance in the answer: `positive`, `neutral` or `negative`; `none` was scored and took no stance; `unscored` was never scored |
| `brand_position_min`, `brand_position_max` | integer | — | Where your brand came in the answer's order of brands, 1 for first, inclusive. Either one implies the brand was named |
| `text_contains` | string | — | A phrase in the answer's text, matched literally and case-insensitively, at most 80 characters |
| `from`, `to` | string | — | First and last day, `YYYY-MM-DD`, inclusive |
| `days` | integer | 28 | Lookback when `from` and `to` are omitted, 1–365 |
| `limit`, `cursor` | | | Same paging as `answer_history` |

Same row shape and the same 8 KB cut as `answer_history`.

**Reading terms and answer terms.** `platforms`, `country`, `topic_id`, `prompt_ids` and the dates select readings. The rest are answer terms, and they must all hold for **one sample**: an answer that named the rival and a different answer from the same reading that cited the domain do not match together. When an answer term is set, only the samples that satisfy every term come back, and failed samples are excluded, because a sample that did not happen cannot be judged. With only reading terms, every sample of the selected readings is returned, failed ones flagged.

**`none` and `unscored` are different.** `none` is an answer we scored that took no stance on your brand. `unscored` is an answer that has not been scored at all. Neither is `neutral`.

**`text_contains` reads every answer in the window**, so without `topic_id` or `prompt_ids` the window is at most 30 days; a wider one is refused with what to narrow. For answers about a subject rather than a phrase, use the agent's [recall](/agent/recall). A slice too broad to read in one go is refused with what to narrow: a shorter window, `prompt_ids` or `topic_id`, a platform, or a shorter phrase.

### citation\_timeline

Read-only. No credits. REST: `GET /v1/memory/citations/timeline`.

One domain's or one page's citations per day, per platform, with mean position and how many prompts cited it — the same rollup `get_analytics` with `area: "source"` ranks, kept day by day.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `domain_or_url` | string | — | Required. A site (`g2.com`) or one page's URL |
| `days` | integer | 28 | 1–365 |

Returns `domain`, `url` (set when the question was one page), `total`, `prompts`, and `series` — one entry per platform, most cited first, each with `points` for every day the window touches: `day`, `citations`, `mean_position` (`null` when nothing that day carried a position) and `prompts`. A zero day is a real zero.

### page\_history

Read-only. No credits. REST: `GET /v1/memory/pages/history`.

One page's versions: every version captured, when, and what changed between them. It covers the workspace's own pages and the cited and competitor pages it watches. Use it before claiming a page changed.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `url` | string | — | Required. Absolute: one of your own pages, or a cited or competitor page the workspace watches |

Returns `url` (normalised), `known` (for your own page, whether it is in the workspace's inventory; for a watched page, always `true`), `versions` and `snapshots` oldest first — each with `id`, `first_seen_at`, `status`, `title`, `description`, `word_count`, `json_ld` (`present`, `absent` or `not_captured`), `canonical`, `robots`, `changed_bits` and `changes`, the fields that moved since the version before (`field`, `from`, `to`). Metadata on a version taken before it was recorded reads `not_captured`, and is never reported as a change. Any other page on another domain is refused with a `404`.

## Track

These tools change what gets measured. Reach for them when the answer is "we are not tracking the right things": "start watching these five buying-intent prompts", "stop wasting a slot on that one", "add the competitor who keeps showing up in answers".

### add\_prompts

Costs credits. Needs a read-and-write key. REST: `POST /v1/prompts`.

Adds one or more prompts to the workspace. From the next daily run, each new prompt is read across every platform your plan tracks. There is no `platform` argument — which surfaces a prompt runs on comes from the plan, not from the caller.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `prompts` | string\[] | — | Required. One or more prompt texts, none blank, at most 50 per call, each at most 500 characters |
| `topic` | string | — | Optional topic to file them under. Created if it does not exist |
| `country` | string | — | Optional ISO 3166-1 alpha-2 code, e.g. `US`. Defaults to the workspace's country |

Returns `added` — every prompt now being tracked, with `id` and `text`. A prompt that was already tracked comes back in `added` too, with its existing id, so a repeated call is idempotent all the way out; `skipped` is empty today. `credits_spent` and `credits_remaining` are `null`: tracking is charged as the prompts are read, not on this call. Your plan caps how many prompts you can hold at once, and a paste that would exceed it is refused with a `409` naming how many slots remain — see [Plans](/account/plans).

### remove\_prompt

Destructive. Needs a read-and-write key. REST: `DELETE /v1/prompts/{prompt}`.

Stops tracking a prompt and frees its slot. Confirm the prompt with `get_prompt` first — this cannot be undone.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. Prompt id, or the exact text |

Returns `id` and `removed: true`. No new readings are collected for that prompt afterwards, so any trend line for it ends on the day you remove it.

### add\_competitor

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/competitors`.

Tracks a competitor domain so it is scored alongside the project in competitor analytics. It does not create new readings, so it costs nothing.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `domain` | string | — | Required. Bare hostname, e.g. `example.com` — no scheme or path |
| `name` | string | — | Optional display name. Defaults to the domain |

Returns the competitor: `id`, `domain`, `name`. Competitor share of voice uses the same denominator as your own — successful runs naming the brand over all successful runs — so adding a competitor never changes your numbers.

### remove\_competitor

Destructive. Needs a read-and-write key. REST: `DELETE /v1/competitors/{id}`.

Stops scoring a rival. Past readings keep what they already recorded, so history does not move — the rival simply stops being scored from here on.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. Competitor id, from `get_analytics` with `area: "competitor"` |

Returns `id` and `tracked: false`.

## Inbox

Everything waiting on a human. Natural questions here sound like "is anything blocked on me?", "what should I deal with first?", "why has nothing happened since Tuesday?", "I connected GitHub — clear that off my list", "tell it the launch is on the 14th". See [Inbox](/agent/inbox) for what ends up there and what each act does.

Each row's `type` says which tool acts on it, and its `ask_kind` says what it asks of a person:

| Row | `ask_kind` | Act on it with |
| - | - | - |
| `work_item` | `approve` | `approve_work_item` (with an edit, for an article, post or rewrite) or `decline_work_item` |
| `work_item` | `do` | Nothing to approve: a copy handoff or an outreach draft the person sends themselves |
| `ask` | `approve` | `approve_ask` or `decline_ask` |
| `ask` | `answer` | Answered in its own session (`session_id`), not here |
| `task` | `answer` | `answer_inbox_question` |
| `task` | `do` | `resolve_inbox_task`, once the person has done it |

Approve, answer and decline only on the customer's instruction, never on your own reading of a row. Every act returns `next_item_ref` when something else is waiting: the row the ranking puts first now, readable with `get_inbox_item`.

### list\_inbox

Read-only. No credits. REST: `GET /v1/inbox`.

Everything waiting on a person: approvals the agent stopped for, questions, work it could not finish alone, failed automations, and approvals that expired unanswered.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `category` | string | `needs_you` | `needs_you` (waiting on a person) or `in_flight` (approved and running, and Done rows still being checked) |
| `view` | string | `list` | `list` returns every row, newest first, under `items`. `triage` returns the `needs_you` rows as decisions under `triage`; refused with `category: "in_flight"` |

Each row in `items` carries `type` (`work_item`, `ask` or `task`), `id`, `ref`, `ask_kind` (`approve`, `answer`, `do` or `info`), `title`, `reason`, `session_id` and `updated_at`, plus `ask_id` on an ask. A task row also carries what it asks: `task.question` (`prompt`, up to six `options`, and `fact_ref` when it fills a profile field), `task.steps` (numbered steps, each with an optional `value` to copy), and `task.verification` while a Done is being checked. `list_approvable` is `true` only on a proposed change that may be approved without opening it: one whose effect can be undone and that is classed safe.

`view: "triage"` is the Inbox's own ranking, the same one the dashboard, the morning brief and the daily email read:

```json Response theme={null}
{
  "triage": {
    "top": [
      {
        "key": "task:missing_integration:github",
        "ask_kind": "do",
        "label": "Connect GitHub · 7 waiting",
        "count": 7,
        "item_refs": ["t_66f1a2", "t_66f1b9", "…"],
        "oldest_waiting_since": "2026-09-28T07:12:00Z",
        "lead": { "type": "task", "ref": "t_66f1a2", "title": "Connect GitHub", "…": "…" }
      }
    ],
    "sections": [
      { "ask_kind": "answer", "label": "2 more questions", "count": 2, "decisions": ["…"] }
    ],
    "total_open": 12,
    "decisions": 5,
    "merged_count": 9
  }
}
```

`top` holds at most three decisions; everything after them is in `sections`, grouped by `ask_kind`. `total_open` counts rows and `decisions` counts decisions, the number the dashboard's badge shows. `merged_count` is how many sightings are not shown as rows of their own: repeats a row absorbed, and duplicates folded into another row.

Worth calling before concluding the agents are idle. A workspace with rows here is one where nothing is moving until somebody answers.

### get\_inbox\_item

Read-only. No credits. REST: `GET /v1/inbox/items/{ref}`.

One row by its ref, in any state, in full: the whole proposal a work item would make, the question a task asks and its answer once given, a done row as done.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `ref` | string | — | Required. A row's `ref` from `list_inbox`, or any `next_item_ref`. `t_`, `w_` or `a__` |

Returns the row with `type`, `id`, `ref`, `title` and the `task` or `work_item` behind it. To approve a specific version, copy `work_item.change.proposal.captured_at` from here into `approve_work_item`. A row the duplicate check folded into another carries `merged_into`, the ref of the row to read instead; one it only suspected carries `possible_duplicate_of` and is not hidden. A malformed ref is `400`; a ref from another workspace is `404`, the same as one that names nothing.

### answer\_inbox\_question

Needs a read-and-write key. Costs credits when the run that asked had already finished, because it starts a new turn on the answer. REST: `POST /v1/inbox/tasks/{id}/answer`.

Answers a task whose `ask_kind` is `answer` with the person's words, never your guess. The answer is saved to Company facts — the business profile field the question names, or a stated fact every later run reads — the row closes, and the run that asked is put back to work.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `task_id` | string | — | Required. The task's `id` |
| `answer` | string | — | Free text, at most 2,000 characters |
| `option` | string | — | One of `task.question.options`, instead of `answer` |

Send `answer` or `option`, not both. Returns the closed `task`, `fact` (where the answer went: `{kind: "profile_field" | "stated_fact", ref}`), `resume` and `next_item_ref`. `resume.status` says what happened to the run:

| `resume.status` | Means |
| - | - |
| `resumed` | The run has the answer: a running run reads it from its next step, and a finished, failed or suspended one starts a new turn on it |
| `queued_after_approval` | The run is waiting on an approval and reads the answer once that is answered |
| `not_started` | The run was cancelled, or no run asked. Nothing was started |
| `failed` | The answer is saved and the row closed, but the run could not be handed it. Retry from the row's page in the dashboard |

A second answer to the same row is `409`. A row that is not a question is `400`.

### resolve\_inbox\_task

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/inbox/tasks/{id}/resolve`.

Closes a task the agents raised.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. From `list_inbox`, and only for a row whose `type` is `task` |
| `state` | enum | — | Required. `done`, `dismissed` or `snoozed` |
| `snoozed_until` | string | — | RFC 3339. Required when `state` is `snoozed`, refused otherwise |

`done` means the thing the agent needed has actually been handled — the integration connected, the record added — not that you plan to handle it. Nothing is queued to run when you send it.

A task that carries a check (a DNS record, a connection, a line on a page) is not closed by `done`: it stays open with `verification.status: "pending"`, moves to `in_flight`, and closes on its own when the check passes. If the check still fails after its retries, the row comes back to `needs_you` with `verification.failure` saying why; sending `done` again checks again. Snoozing a row while its check is pending is `409`. Returns the task and `next_item_ref`.

### approve\_work\_item

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/inbox/{id}/approve`.

Says yes to a work item the agents stopped for, and lets them carry the change out — as proposed, or as the person edited it. Only a row whose `type` is `work_item` is approved this way.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. Work item id, from `list_inbox` |
| `expected_captured_at` | string | — | RFC 3339. The `work_item.change.proposal.captured_at` the person read. If the proposal was revised since, the approval is refused with `409` |
| `edited_value` | string | — | The person's full edit, at most 200 KB. Approves their version instead of the agent's |

Idempotent: approving twice approves once, so a retry after a timeout is safe. Approving is the customer's decision to make, not one to infer from the row. Returns the work item and `next_item_ref`.

`edited_value` is accepted only for an article, a LinkedIn post or a rewritten passage that is still waiting, and is refused with `400` for anything else (a fix is edited in its pull request). The edited change keeps the agent's original, and is always a claim: it waits for a person in every approval mode and never goes out on its own. An edit that adds or changes code is refused with `422`, and nothing is approved. An edit is checked against the workspace's rules like any draft; one that breaks a rule is `409`.

### decline\_work\_item

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/inbox/{id}/decline`.

Says no to a work item, with the reason why.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. Work item id, from `list_inbox` |
| `reason` | enum | — | Required. `wrong`, `not_now`, `already_done` or `never_touch` |
| `note` | string | — | The person's words, at most 1,000 characters. Required when `reason` is `wrong` |

The reason is required and is the point of declining rather than ignoring: it is what stops the same proposal coming back. `wrong` means the proposal is incorrect, `not_now` right idea wrong time, `already_done` handled elsewhere, `never_touch` do not propose this again for this page or topic. The reason and note are kept on the item and in its activity. Returns the work item and `next_item_ref`.

### approve\_ask

Needs a read-and-write key. The call itself spends nothing; the run it lets go carries on as before. REST: `POST /v1/inbox/asks/{id}/approve`.

Allows a paused run, once, the exact call it stopped to ask about: a row whose `type` is `ask` and whose `ask_kind` is `approve`.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `session_id` | string | — | Required. The row's `session_id`: an ask id is unique only within its session |
| `ask_id` | string | — | Required. The row's `ask_id` |

An ask is never edited: the yes is bound to the exact arguments the run asked about. Returns `session_id`, `outcome` (`allowed-once`) and `next_item_ref`. The run is back on the queue and has not made the call yet; poll it with `get_session`. A session in another workspace is `404`; an ask already answered, expired, or no longer the one the run is waiting on is `409`. An ask whose `ask_kind` is `answer` is answered in its session, not here.

### decline\_ask

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/inbox/asks/{id}/decline`.

Refuses the call a paused run asked for. The run reads the reason and note first, then carries on without the call.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `session_id` | string | — | Required. The row's `session_id` |
| `ask_id` | string | — | Required. The row's `ask_id` |
| `reason` | enum | — | Required. `wrong`, `not_now`, `already_done` or `never_touch` |
| `note` | string | — | The person's words, at most 1,000 characters. Required with `wrong` |

Returns `session_id`, `outcome` (`rejected`) and `next_item_ref`. Same `404` and `409` as `approve_ask`.

## Automations

Automations are scheduled work: a weekly digest, a recurring audit, a check that runs after every deploy. Natural questions here sound like "email me a summary every Monday", "what is already scheduled?", "run last week's report again now".

### list\_automations

Read-only. No credits. REST: `GET /v1/automations`.

Lists every automation in the workspace as `automations`: each with `id`, `name`, `instruction`, `trigger`, allowed `tools`, `status` (`active`, `paused`, or `done` for a `once` that ran), `timezone`, `run_at_minute`, `run_on_weekday` and `event_types` where set, and `last_run_at` (absent when it has never run). Takes no parameters.

Paused and retired automations are always listed. One the product paused on its own (for example a Daily check-in retired by [detection](/agent/findings)) carries `retired_reason` and `retired_at`. `next_run_at` is `null` for event automations.

### get\_automation

Read-only. No credits. REST: `GET /v1/automations/{id}`.

Full definition for one automation, with its most recent run inline as `latest_run` (`{id, status, state}`, or `null` when it has never run) — so "did my Monday report run?" costs one call, not two. A paused or retired automation is returned like any other, with `retired_reason` and `retired_at` when the product paused it.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |

### create\_automation

Needs a read-and-write key. Creating costs nothing; each run it triggers does. REST: `POST /v1/automations`.

Defines a new automation that runs an instruction on a trigger. With no trigger it runs daily at 09:00 in the workspace's time zone.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `name` | string | — | Required |
| `instruction` | string | — | Required. What the automation should do each run, in plain language |
| `trigger` | enum | `daily` | One of `daily`, `weekly`, `monthly`, `quarterly`, `event`, `manual`, `once`. The clock triggers fire at `run_at_minute`, `monthly` and `quarterly` on the 1st; `event` fires on `event_types`; `manual` and `once` run only through `run_automation`, and `once` then retires |
| `tools` | string\[] | — | Optional allow-list of tool names the automation may use. Omit to allow everything the plan includes |
| `timezone` | string | The workspace's | Optional IANA zone the schedule is written in, e.g. `Europe/London`. UTC only if the workspace has none |
| `run_at_minute` | integer | 540 | Minutes past local midnight, 0–1439. `540` is 09:00. Only meaningful for the clock triggers |
| `run_on_weekday` | integer | — | For `weekly`: 0 = Sunday to 6 = Saturday |
| `event_types` | string\[] | — | Required and non-empty when `trigger` is `event`, refused otherwise. Any of `prompt_lost`, `citation_lost`, `new_deciding_source`, `competitor_changed`, `page_changed`. See [Event automations](/agent/autopilot#event-automations) |

Returns the created automation with its id. `next_run_at` is `null` for an event automation. Creation is free; every run spends credits like a manual run.

### run\_automation

Costs credits. Needs a read-and-write key. REST: `POST /v1/automations/{id}/runs`.

Triggers an automation immediately, outside its trigger. Returns a session handle — `session_id`, `status` and `poll_after_ms` — to poll with `get_session`, not the finished result.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |
| `idempotency_key` | string | — | Strongly recommended. Sent as `Idempotency-Key`; a repeat call with the same key does not bill twice |

### delete\_automation

Destructive. Needs a read-and-write key. REST: `DELETE /v1/automations/{id}`.

Permanently deletes an automation and its schedule — this cannot be undone. Its past runs are sessions and survive. Confirm it with `get_automation` first.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |

Returns `id` and `deleted: true`.

### update\_automation

Needs a read-and-write key, but does not spend credits. REST: `PATCH /v1/automations/{id}`.

Changes one automation in place — a partial edit. A field you do not pass is left alone, so pausing an automation cannot quietly rewrite what it does.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |
| `name` | string | — | New display name |
| `instruction` | string | — | New instruction. Replaces the old one outright |
| `trigger` | enum | — | `daily`, `weekly`, `monthly`, `quarterly`, `event`, `manual` or `once` |
| `event_types` | string\[] | — | Only with an `event` trigger. Same values as `create_automation` |
| `run_on_weekday` | integer | — | For `weekly`: 0 = Sunday to 6 = Saturday |
| `tools` | string\[] | — | New allow-list. Replaces the old list |
| `status` | enum | — | `active` resumes it on its trigger; `paused` stops it firing without deleting it |
| `timezone` | string | — | IANA zone the schedule is written in |
| `run_at_minute` | integer | — | Minutes past local midnight, 0–1439 |

Pausing is how a schedule is stopped without losing it — deleting is the one that cannot be undone. Returns the automation in the shape shown under `list_automations`.

## Agent

The agent tools run a chat inside your workspace, with your tracking data in reach. Use them for questions that need reasoning across several slices — "why did our share of voice drop on Perplexity last week?", "draft a plan for the three prompts we lose most" — rather than for numbers you can pull directly with `get_analytics`.

### ask

Costs credits. Needs a read-and-write key. REST: `POST /v1/ask`.

Open-ended investigation the other tools cannot answer — why a prompt is losing, what to change on a page — run by an agent that crawls pages, audits them as a model sees them, and searches the web. Try `get_analytics`, `deciding_sources`, `win_plan`, `get_prompt` or `get_page` first: those are free and immediate, this costs credits and takes time.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `question` | string | — | Required. The question, in plain language, at most 4,000 characters — pass the question, not the data; the agent reads the workspace itself |
| `session` | string | — | Session id from a previous `ask`, to continue that thread instead of starting one |

Returns `session_id`, `status` (`running` at first — that is the success case, not an error), `answer` (`null` until complete) and `poll_after_ms`. Wait that long, then poll `get_session` with the returned id until `status` is `complete`.


  The agent runs on an **agent model** — the LLM powering the chat. That is a different thing from a tracked **platform**, which is the answer surface Attensira reads on your behalf. Changing your agent model does not change what is measured.


### get\_session

Read-only. No credits. REST: `GET /v1/sessions/{id}`.

Fetches a session started by `ask` or `run_automation`: its status and, once finished, its answer. Use this to poll async work.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |
| `include` | string\[] | — | Pass `["events"]` for the step-by-step trace of what the agent did. `events` is the only supported value |

Returns `id`, `status` (`complete`, `running`, `failed`), `state` (the engine's own finer word — a run parked on a question for you reads `awaiting_person` under a `running` status), `answer` (the agent's last message once complete; `null` while running or on failure), and `events` — present only when requested, each with a `type` such as `tool.start` or `text.delta` and the fields of that event — the way to see which tools the agent called and with what arguments.

## Sessions

A session is one piece of agent work: an `ask`, an automation run, a chat or the set-up run. These tools list them and act on them. REST twins are on [Sessions](/api/sessions).

### list\_sessions

Read-only. No credits. REST: `GET /v1/sessions`.

Sessions, newest first. Use it for "what is running right now?" or "what ran since Monday?".

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `status` | string\[] | — | `complete`, `running`, `failed`, or a stored state: `queued`, `running`, `awaiting_person`, `suspended`, `completed`, `failed`, `cancelled` |
| `origin` | string\[] | — | `user`, `automation`, `mcp`, `setup` |
| `since` | string | — | RFC 3339 or `YYYY-MM-DD` |
| `limit` | integer | 50 | 1–200 |

Returns `sessions`, each with `id`, `title`, `origin`, `status`, `state`, `automation_id`, `created_at` and `ended_at`.

### cancel\_session

Needs a read-and-write key. No credits; works on a spent balance. REST: `POST /v1/sessions/{id}/cancel`.

Stops a running session before its next step. Credits already spent stay spent. Safe to repeat: if the run already finished, it returns that real ending unchanged.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |

### delete\_session

Destructive. Needs a read-and-write key. REST: `DELETE /v1/sessions/{id}`.

Permanently deletes a finished session and its trace. A running session is refused with a `409`; cancel it first.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |

Returns `id` and `deleted: true`.

### add\_context

Needs a read-and-write key. Costs credits only with `run: true`. REST: `POST /v1/sessions/{id}/context`.

Gives a session text or links it cannot find itself, as a message in its thread.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required |
| `text` | string | — | At most 20,000 characters |
| `urls` | string\[] | — | `http` or `https`, at most 10 |
| `run` | boolean | `false` | `true` queues the session again to act on it now, which costs credits. Otherwise the next turn reads it, and a run already in flight picks it up when its current turn ends |

Pass `text`, `urls`, or both. A cancelled session refuses new context. Returns `session_id` and `status`.

## Account and settings

### get\_loop\_health

Read-only. No credits. Works on a spent balance. REST: `GET /v1/loop-health`.

Whether the workspace's loop is `working`, `idle` or `blocked`, with the reasons, and a day-by-day record of readings, automation runs and skips, findings, work produced, approvals pending, pull requests opened and re-reads due. **Call it first when asked why nothing happened.** See [Loop health](/agent/loop-health).

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `days` | integer | 7 | 1–30 |

Returns `status`, `reasons` and `days`. The per-day fields are documented under [`GET /v1/loop-health`](/api/endpoints#get-v1loop-health).

### get\_account

Read-only. No credits. REST: `GET /v1/account`.

Returns the org, every workspace you can reach, the one this call resolved to, the credit balance, which platforms that workspace tracks, and what this credential may do. Takes no required parameters. Call it first from a fresh integration — to collect the ids the `project` parameter takes, to confirm which workspace the credential points at, and before reporting any per-platform number: `platforms` is the authority on which platforms were queried at all, so a platform absent from it was never measured rather than measured at zero.

Returns `org` (`id`), `project` (`id`, `name`, `domain` — the workspace this call resolved to), `workspaces` (every workspace in the org, each with `id`, `name`, `domain`), `credits` (`available`, `available_usd`, `held_units`; absent when the balance could not be read, never a zero standing in for an outage), `scopes` — what this credential may do — and `platforms`. `work` counts this month's work by type: `committed` (a `null` count is a type not tracked yet, not zero), `delivered`, `waiting_on_you` (held for a person, which is not a shortfall) and the month's `cap_usd`. When it could not be read, `work` is `null` and `work_unavailable_reason` says why.

`project` is `null` when the organisation has several workspaces and the call named none. That is the answer rather than an error: `workspaces` carries the ids, and every other tool then needs an explicit `project`. `platforms` is empty in that case too, because platforms belong to a workspace rather than to the organisation. It answers without a workspace, so it is always the right call when another tool asks you to name one.

### get\_costs

Read-only. No credits. Works on a spent balance. REST: `GET /v1/costs`.

What a UTC month has cost, in US dollars: the total, a month-end forecast, where it went, and the work the budget committed us to against what was delivered. The same figures as the [Costs](/account/costs) page. Call it when asked what Attensira cost this month, or what a client's workspace spent.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `period` | string | this month | `YYYY-MM`, a UTC month that has started. Anything else is refused with the form to send |
| `project` | string | — | A workspace id, to narrow every breakdown to that workspace. **Omit for the whole organisation**, unlike most tools, where omitting it means your own workspace |

Returns `period`, `currency` (`usd`), `as_of`, `workspace_id` (the workspace named, or `null` for the organisation), and:

| Field | What it is |
| - | - |
| `spent_usd` | The month's spend, less refunds. Every breakdown below adds up to it. Ad spend is never part of it |
| `forecast_usd` | The month's spend at its pace so far: spend divided by days elapsed, times days in the month. `null` for the first three days, never a guess. For a past month, what it cost |
| `by_work_area` | Per project: `workspace_id`, `work_area_id`, `name`, `spent_usd`. A `null` `work_area_id` is the workspace's unassigned spend: chats and the Chief of staff |
| `by_workspace` | Per workspace: `workspace_id`, `name`, `spent_usd`. An empty `workspace_id`, named `Across workspaces`, is spend that belongs to the organisation rather than one workspace |
| `by_kind` | `kind` and `spent_usd`. `agent_work` and `data` carry today's spend; `paid_placement` and `creators` are always listed and read `0`; `unattributed` appears only when a charge could not be placed |
| `by_type` | Per deliverable `type` (`article`, `page_fix`, `listing_fix`, `outreach_pitch`, `monitoring_day`, `other`): `delivered` and `spent_usd`. `delivered` is `null` for a type we do not count, never `0` |
| `committed` | The organisation's commitment for the month, per type: `committed` (`null` for a type we cannot count), `delivered`, `waiting_on_you`, `shortfall` and `credit_usd`, the credit for undelivered work. For an open month, `credit_usd` is what the close would credit now. See [Plans](/account/plans#if-we-deliver-less-than-the-budget-committed) |
| `credited_back_usd` | What the month's close credited back to the balance for this month's shortfall |
| `ad_spend` | `usd`, `source`, `note`. Paid to the ad platform directly and never part of any total. `usd` and `source` are `null` with no ad account connected |

`committed` and `credited_back_usd` are the organisation's even when `project` names a workspace: one budget commits work for every workspace in it.

The budget, its pace, the caps, the balance and how it is funded are **not** returned. They are set by a person in **Settings → Billing**, and no tool can read or change them. Read the balance with `get_account`. When the work needs more room, tell the person: only they can raise a budget or a cap.

The REST twin is [`GET /v1/costs`](/api/endpoints#get-v1costs).

### get\_brief

Read-only. No credits. REST: `GET /v1/brief`.

The same brief the morning email carries: the artifact of the day, what needs a person, counts for the window, and what re-reads when. See [The morning brief](/agent/morning-brief).

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `date` | string | — | Optional `YYYY-MM-DD` for one of this workspace's own past days. Omit for today |

Returns `date`, `edition` (`daily`, or `weekly` for the Monday note), `subject`, `text` (the brief itself, as the email body), `order` (the weekly sections in the order the text reads them: shipped work and early signals before citations and goals), and `sections`, among them `needs_you` (at most three rows, each with an inbox `id` and `title`), `goals` (each goal's `name` and `pace`) and the citation wins and re-reads. A section that is absent is not wired yet, never a zero. It reports counts, never a day-over-day comparison, until a seven-day window exists, and says "no proven change yet" until a verified re-read.

### get\_ai\_traffic\_install

Read-only. No credits. **Key-only.** REST: `GET /v1/ai-traffic/install`.

Returns everything needed to start sending AI-crawler hits to Attensira: the ingest endpoint, the HMAC signing secret for your workspace, the signature header name, the replay window, the daily cap, and a copy-pasteable edge worker. Takes no parameters.

Call it when [AI traffic](/traffic/ai-traffic) is empty and you need to know how to send us any. The signing secret is derived from the workspace id, so it is stable and can be shown again — unlike your API key, which is displayed exactly once. Because the response carries a secret, a signed-in OAuth connection is refused here with a message saying so; use an API key.

### connect\_github

Needs a read-and-write key, but does not spend credits. REST: `GET /v1/github/install-url`.

Starts connecting a GitHub repository: returns the install link for the Attensira GitHub App, scoped to this workspace. Call it when a fix needs a pull request and the workspace has no repository connected.

Takes no parameters. Returns `url` — the App install link, and nothing else. GitHub hands back a user-to-server code only to a browser that completes the install, so the connection exists only once a person has followed the link. Each call mints a fresh single-use link, so fetch a new one rather than reusing an old one.

### update\_settings

Needs a read-and-write key, but does not spend credits. REST: `PATCH /v1/settings/agents`.

Sets how far a run may go on its own, and which paths act mode may touch. Absent fields are left alone. This is the customer's decision: change it on their instruction, never to finish a task. See [Approval](/agent/approval).

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `unattended_mode` | enum | — | `ask` stops for a person, `draft` leaves the work in review, `act` carries it through |
| `act_path_allowlist` | string\[] | — | Path patterns act mode may change, e.g. `/docs/**`. Replaces the list; empty restores the default. Source, CI, lockfiles and dotfiles are always refused |
| `publish_without_approval` | boolean | — | Whether a run may put a page on the site without asking |
| `send_without_approval` | boolean | — | Whether a run may send email without asking |

At least one field is required. Returns the settings in force: `unattended_mode`, the effective `act_path_allowlist`, `act_paths_are_default`, `publish_without_approval` and `send_without_approval`.

### list\_wins

Read-only. No credits. Works on a spent balance. REST: `GET /v1/wins`.

The workspace's wins, newest first. A win is the first time an engine named your brand, or linked to your site, in an answer to a prompt that does not name your brand itself, after we had read that prompt on that engine on at least three earlier days with a successful answer and none of them did the same. A failed read counts toward neither half. A prompt and engine pair can give one win of each kind, `named` and `linked`, and never the same kind twice. Only the first naming (or link) can be a win: one that came before those three days of reading is not, and no later answer of the same kind on that prompt and engine becomes one.

A win is a first **since we started reading** that prompt. It is not evidence that any work caused it. Say it the way `sentence` does, and keep "since we started reading" when you repeat it.

Takes no parameters besides `project`. Returns at most the 50 most recently found wins. Dismissed wins are left out.

Returns `wins` and `unseen`. Each win carries:

| Field | Notes |
| - | - |
| `id` | The win's id |
| `prompt_id`, `prompt` | The tracked prompt and its text |
| `platform`, `engine` | The platform id (`chatgpt`) and its name (`ChatGPT`) |
| `kind` | `named`: the answer named the brand. `linked`: it cited a page on your domain |
| `sentence` | The win in one sentence, the same sentence the win email uses |
| `quote` | A verbatim excerpt of the answer around the mention, at most 400 characters. `null` when no answer that day gave an excerpt that passed the honesty check. A quote is never edited |
| `quote_truncated` | `true` when the excerpt stops short of either end of the answer |
| `cited_url` | The first page on your domain the answer cited, or `null` when it cited none |
| `date` | The day of the answer, `YYYY-MM-DD`, in the workspace's time zone |
| `reading_since` | The first day we read this prompt on this engine, `YYYY-MM-DD`. `null` only when it was not recorded |
| `detected_at` | When the win was found, RFC 3339. The list is ordered by this |
| `seen` | Whether someone has opened the win in the dashboard |
| `share_url` | The public link to the win card while it is shared, otherwise `null` |

`unseen` counts every win in the workspace that nobody has opened in the dashboard and nobody has dismissed, including any beyond the 50 listed. Reading the list through this tool does not mark a win seen.

```json Response theme={null}
{
  "wins": [
    {
      "id": "win_4e19",
      "prompt_id": "prm_8f21",
      "prompt": "best crm for small teams",
      "engine": "ChatGPT",
      "platform": "chatgpt",
      "kind": "named",
      "sentence": "ChatGPT named Acme for the first time since we started reading this prompt on 2 September 2026, in an answer to 'best crm for small teams' on 28 September 2026.",
      "quote": "For a team under ten people, Acme and Rival are the two most often recommended, and Acme has the simpler setup.",
      "quote_truncated": true,
      "cited_url": null,
      "date": "2026-09-28",
      "reading_since": "2026-09-02",
      "detected_at": "2026-09-28T10:05:00Z",
      "seen": false,
      "share_url": null
    }
  ],
  "unseen": 1
}
```

## Memory

Two writes into what the workspace knows about itself. See [Knowledge and skills](/agent/knowledge-and-skills) and [What we know](/agent/what-we-know).

### record\_observation

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/observations`.

Writes down something durable about this workspace so every future run starts knowing it — a pattern across answers, a source that keeps deciding.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `statement` | string | — | Required. One sentence carrying its own sample size — "7 of 9 answers that named us cited the changelog", not "the changelog does well" |
| `evidence` | object\[] | — | Required, at least one. Each with `kind` (`reading`, `citation_day` or `action`) and `id` exactly as another tool returned it — a `reading_id` from `answer_history`, for instance. Optional `url` and `day` labels are never what is checked |

Ids are resolved against the workspace before anything is stored, so a guessed one is refused rather than remembered. Returns the observation with its `id`, `statement` and `n`.

### write\_skill

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/skills`.

Records a rule this workspace wants followed — how to name things, a claim never to make, a procedure. Every future answer and draft obeys it. Write one the moment a customer states a preference as a rule ("always…", "never…"). Creates only; revising is done in the dashboard.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `title` | string | — | Required. The rule in a few words |
| `description` | string | — | Required. One line on when it applies, e.g. "On any page naming a competitor" |
| `body` | string | — | Required. The rule itself, in the customer's own words, specific enough to follow without asking |
| `kind` | enum | — | Required. `voice` is how it sounds, `compliance` what may never be claimed, `playbook` a procedure to follow |

Returns the skill with its `id`, `title` and `kind`.

## Set up a workspace

These tools let a person's assistant set up a business from its domain: the agents draft it, the person decides, and the person pays in their browser. Neither takes `project` — there is no workspace to select yet — and neither can charge anything. Sending `project` anyway is refused with the reason.

The order is: `start_workspace` → show the person what was drafted and ask → `confirm_setup` with what they kept → give them the checkout link → call `start_workspace` again until it reads `active`. The [first recipe](/mcp/recipes#set-up-a-business-from-your-assistant) walks through it.

### start\_workspace

Needs a read-and-write key or a signed-in connection. No charge. REST: `POST /v1/onboarding/workspace`.

Creates the workspace for a domain, or returns the one already started for it. Calling it again with the same domain never creates a second workspace: it is how you poll.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `domain` | string | — | Required. The business's domain, e.g. `example.com` |

Returns `workspace_id`, `domain`, `status`, `reason` (why it needs input, else `null`), `next` (what to do now, in one sentence), and the draft: `profile` (facts read off the site, each with the page it came from, or a `question` to ask when nothing was found), `competitor_suggestions`, `competitor_question`, `platform_suggestions`, `proposed_goals`, `plans` and the `budget` range.

`status` moves in this order:

| `status` | Means |
| - | - |
| `drafting` | The site is being read. Call again shortly |
| `needs_input` | The profile could not be drafted; the questions in `profile.facts` fill it |
| `ready_to_confirm` | Everything is proposed. The person decides |
| `awaiting_payment` | A plan is chosen; the checkout is the person's to finish |
| `active` | The plan is paid and the first readings are being taken |
| `first_readings_ready` | Enough of the first readings have landed to read a brief from |

A signed-in connection works before there is any workspace: the first call sets up your organisation. An account can hold one workspace waiting for payment at a time: starting a second domain before the first is paid is refused, naming the one to finish.

### confirm\_setup

Needs a read-and-write key or a signed-in connection. No charge. REST: `POST /v1/onboarding/workspace/{id}/confirm`.

Saves the person's setup choices and returns their checkout link. Every call mints a new link, so give the person the latest one.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `workspace_id` | string | — | Required. From `start_workspace` |
| `plan` | string | — | Required. A plan `id` from `start_workspace`'s `plans` |
| `monthly_budget_usd` | number | — | The monthly budget the person chose, in US dollars |
| `overage_cap_usd` | number | — | The most a month may be charged in all, in US dollars: from the budget up to 10% over it, and only sent with `monthly_budget_usd`. A cap above the budget lets new work run up to 10% over the budget, which stays the budget. A cap below the budget or more than 10% over it is a `400`. Omit it and work pauses at the budget, which is then the cap |
| `competitors` | string\[] | — | The rivals they kept, by domain |
| `platforms` | string\[] | — | The platforms they kept |
| `goals` | object\[] | — | The goals they kept, as `start_workspace` returned them in `proposed_goals` |
| `profile_edits` | object | — | Corrections to the profile, a fact's `field` mapped to its new value |

Returns `workspace_id`, `status`, `checkout_url`, `requires_browser` (always `true`), `expires_at` (`null` unless the payment provider states one), `plan`, `budget_consent` and `next`. `budget_consent.line` is the top-up rule the person agrees to by paying, in words: relay it as written. `budget_consent.monthly_budget_usd` is the budget that was agreed; it is `null` only for a rule agreed before budgets were kept apart from caps. A person who already has a card on file is sent to the billing page to confirm the plan in person, because an assistant has not been given that yes. An account that already pays for a plan has nothing to check out, and the call says so.

## Goals

What the agents aim at. A goal is a metric, a target and a date; the daily readings say whether the work is on pace.

### get\_goals

Read-only. No credits. REST: `GET /v1/goals`.

Every goal in the workspace, with its current value against its target and whether the work is on pace. Takes no parameters.

Returns `goals`, each with `id`, `name`, `metric` and `metric_label`, `unit`, `direction`, `baseline` and `current` (each `{value, n}`; a `null` value is not measured yet, not zero), `target`, `target_date`, `pace` (`state` is `ahead`, `on_pace`, `behind` or `unmeasured`, with the `expected` value and a `reason`), `sentence` (the standing in words), `work_area_id` and `work_area_ids` (the projects it drives), `status` and `created_via`. The pace verdict is Attensira's: quote it rather than comparing the numbers yourself.

### set\_goal

Needs a read-and-write key, but does not spend credits. REST: `POST /v1/goals` and `PATCH /v1/goals/{id}`.

Creates a goal, or changes one by `id`. On a change, fields you omit are left alone. Set goals on the person's instruction.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | The goal to change. Omit to create one |
| `name` | string | — | Required to create |
| `metric` | string | — | Required to create. A metric key as `get_goals` returns them, such as `ai_share_of_voice` or `search_clicks` |
| `target` | number | — | Required to create. In the metric's own unit |
| `target_date` | string | — | Required to create. `YYYY-MM-DD` |
| `work_area_id` | string | — | The project it drives |
| `idempotency_key` | string | — | Any string you choose. A retried create with the same key writes once |

Returns the goal's `id`. A change with nothing in it is refused rather than reported as a change.

## Projects

A project is an area of work with its own crew of agents. The crew plans and does the work; a person steers it. Remember that on the wire a project is a work area, and `project` is the workspace.

### list\_projects

Read-only. No credits. REST: `GET /v1/work-areas` and `GET /v1/work-areas/{id}`.

Without `id`, every project in the workspace. With `id`, one project and its latest feed.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | A project's id (its `work_area_id`) |

The list returns `projects`, each with `id`, `name`, `description`, `status`, `team` (each crew member's `role`, `name` and `status_line`), `goal_id` and `goal_text`, `unread`, `needs_you`, `live`, `autonomy`, `budget_cap_usd` (the project's monthly cap, `null` when none is set) and `last_activity_at`. One project adds its `direction` (what people have told it, word for word), `triggers`, `automation_ids` and `feed`: the latest posts, each with `kind`, `text`, `at`, `because` and its `author`.

### steer\_project

Needs a read-and-write key. Posting is free; the crew's reply is an agent run and spends credits. REST: `POST /v1/work-areas/{id}/steer`.

Gives a project's crew the person's direction, in their words. It is posted to the project's feed and kept as the project's direction, and the crew answers it with an ordinary run that replans. A steer is a post and nothing else: it never approves work, changes a setting or publishes anything, whatever the words say, and the run that answers it meets the same approvals as every run.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `id` | string | — | Required. The project's id |
| `text` | string | — | Required. The person's direction, verbatim, at most 4,000 characters |
| `idempotency_key` | string | — | A retried post with the same key posts once |

Returns `ack` (the sentence to tell the person), `feed_item_id`, `run_id` (the run answering it, `null` when it could not start, with `reply_error` saying why) and `plan_change`, which is always `null`: a steer changes no plan by itself, and each change the crew makes lands in the feed as its own post. `replayed` is `true` when the key named a post already sent, and nothing was posted again.

## Deliverables

### list\_deliverables

Read-only. No credits. REST: `GET /v1/deliverables`.

What the crews delivered, newest first: where each piece of work lives and what the re-reads proved about it.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `type` | enum | — | `article`, `page`, `fix`, `social_post`, `listing_fix`, `product_copy` or `image` |
| `work_area_id` | string | — | One project's deliverables |
| `status` | string | — | Delivery state to filter on. An unknown one is refused with the valid list |
| `cursor` | string | — | `next_cursor` from the previous page |

Returns `deliverables` and `next_cursor` (`null` at the end). Each deliverable carries `id`, `type`, `title`, `work_area_id`, `location` (`provider`, `label` and `url`: the pull request, CMS draft or post where it lives), `status` and `status_label`, `result`, `approved_by` (a name), `run_id` (the session `get_session` reads), `run_url` (the replay at the step that made it), `dashboard_url`, `created_at` and `shipped_at`. `result` is `null` when there is nothing to measure yet; otherwise it has a `state`, a `label` and a `verdict` that is `null` until a re-read reached one.

## Reports

### get\_report

Read-only. No credits. REST: `GET /v1/reports/latest`.

The latest published client report for a cadence.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `cadence` | enum | `week` | `week`, `month` or `quarter` |

Returns `id`, `title`, `cadence`, `period`, `facts` (each counted over its own `n`), `movement_proven`, `not_read` (what the report could not read), `markdown`, `published_at`, and `share_url` with `share_expires_at` when the report is shared. Through MCP the markdown is capped at 16 KiB and `truncated` says when it was cut: check it before quoting the end of a report, and send the person to the report page for the whole of it. Quote the facts as they are rather than recomputing them. See [Client reports](/agencies/client-reports).

## Markets and brand family

Which countries and languages the workspace is read in, and which domains and names count as the brand. Reading both is free. Changing either is the person's, in the dashboard: more markets means more AI responses a month, and a property changes what counts as "you" in every number.

### get\_markets

Read-only. No credits. MCP-only.

The workspace's markets, primary first, the platforms read in them, and how each platform is located in each market. Takes no parameters.

Returns `markets` and `support`. A platform that cannot be located in a market is `unsupported` there, and is not read in it: that is not measured, never zero. To compare markets, call `query_evidence` with `group_by: ["country"]`.

### preview\_markets

Read-only. No credits. MCP-only.

Prices a change to markets or platforms in AI responses a month, against what the plan includes. It changes nothing.

| Parameter | Type | Default | Notes |
| - | - | - | - |
| `markets` | object\[] | — | Required. The whole list after the change, primary first. Each `{country, language?}`: a two-letter country code and an optional BCP-47 language |
| `engines` | string\[] | — | The platforms after the change |
| `topic_ids` | string\[] | — | Scope the change to these topics, which is how a second market is usually added |

Returns `responses_per_month` for the organisation and `workspace` for this workspace alone, each now and after the change, the plan's monthly `allowance`, and `over_by`: how many responses a month the change lands over the allowance, `0` when it fits. `overage_policy` says what happens past the allowance.

### get\_brand\_family

Read-only. No credits. MCP-only.

The brand's own properties beyond its main domain: other domains, subdomains, products and sub-brands. Takes no parameters.

Returns `properties`, each with a `status` of `proposed`, `confirmed` or `rejected`. Only confirmed properties count as the brand in the numbers. For a number per property, call `query_evidence` with `group_by: ["property"]`.


# MCP troubleshooting
Source: https://docs.attensira.com/mcp/troubleshooting

Fix sign-ins that never return, 401s, scope errors, missing tools, revoked credentials, empty model results, and timeouts on the Attensira MCP server.

Most MCP failures are one of a handful of things: an OAuth sign-in that never came back, a malformed key, a client that cannot speak the transport, a revoked credential, a token missing the scope a tool needs, a model that was never queried, or a request that ran past the upstream budget. Each has a distinct signature.

## The browser opened but never came back

You started an OAuth connect, a tab opened, you signed in — and the client still says it is waiting. The sign-in worked; the redirect back to the client did not.

For a **local client** — Claude Code, `mcp-remote`, most editors — the callback goes to a loopback address such as `http://127.0.0.1:51234/callback`, on a port the client picked when it started. If that tab shows "connection refused" or hangs, the client is no longer listening on it. That happens when the client was restarted, quit, or timed out while you were signing in. Close the tab, restart the connect from the client, and complete the sign-in without leaving it sitting.

If the callback tab loads but the client never notices, something is intercepting loopback traffic. A corporate proxy or VPN configured to route *all* traffic, including `127.0.0.1`, breaks this — the browser sends the callback to the proxy and it never reaches your machine. Exempt `localhost`, `127.0.0.1` and `::1` from the proxy, or connect off the VPN once and let the client refresh from then on.

A proxy that intercepts TLS can also break the step before that. The client fetches discovery documents from `mcp.attensira.com` and `clerk.attensira.com`; if either is rewritten or blocked, the flow dies before a browser ever opens. Check both are reachable from the machine running the client:

```bash Shell theme={null}
curl -s https://mcp.attensira.com/.well-known/oauth-protected-resource/mcp
curl -s https://clerk.attensira.com/.well-known/oauth-authorization-server
```

Both should return JSON. If one returns HTML, a proxy is answering for it.


  If the browser opens to a sign-in you cannot complete because the client runs on a server with no display, that client should be using a static API key instead. See [Connect a client](/mcp/connect).


## Why did it stop working after a while?

An OAuth access token is short-lived. A connection that works, keeps working for a while, and then 401s on every call has a client that is not refreshing.

Well-behaved clients refresh silently and you never see this. When you do, it is usually one of three things:

* **The client did not store the refresh token.** Reconnect; if it comes back an hour later, the client is at fault and the workaround is a static key.
* **The client kept an old refresh token.** Refresh tokens rotate — each use returns a new one and consumes the old. Replaying a consumed refresh token revokes the whole chain, which looks exactly like being logged out for no reason. Reconnect once; if it keeps happening, the client is holding a stale copy, often from a config file it restores on start.
* **The connection was withdrawn.** Withdrawing stops the next refresh, but the access token in flight keeps working until it expires — which is precisely the "worked for a while, then died" shape. See [Connected applications](/account/connections).

Time skew produces the same symptom early: a machine whose clock is more than a minute off can have a token that reads as expired the moment it is issued. Check the clock before blaming the client.

## Why is it reading the wrong workspace?

Approving a client fixes the organisation, never a workspace, and a static key carries the workspace it was minted in. Either way it is a default rather than a limit: if `get_account` names a workspace you did not intend, you do not need to reconnect — tell the client to name the right one:


  
    Call `get_account`. Its `workspaces` array carries every workspace id in the organisation, and `project.id` is the one being used by default.
  

  
    Every tool that works on a workspace takes an optional `project`. Ask for the workspace by name and the client will pass the id — "check visibility for the acme.com workspace".
  


Working in two workspaces at once needs one connection, not two server entries. What the approval fixes is the organisation, and only reaching a different organisation needs a fresh sign-in.

## A tool says the organisation has several workspaces

The message reads roughly *"this organisation has several workspaces: pass project=\ to say which one."* A signed-in connection names your organisation and nothing narrower, so when the organisation owns more than one workspace there is no default for a tool to fall back on.

Call `get_account`. It answers without a workspace — that is why the message points at it — and its `workspaces` array carries the ids. Then name one: "check visibility for the acme.com workspace", and the client passes the id as `project`.

You will see `project: null` in that reply, which is correct and not a fault. It says no workspace resolved for *this* call, and `platforms` is empty for the same reason — platforms belong to a workspace, not to the organisation. Name a workspace and both fill in.

A static key does not hit this: a key is minted inside one workspace and carries it as its default. See [API keys](/account/api-keys).

## Why does one tool fail with 403 when the rest work?

Reads succeed and a write tool — `add_prompts`, `create_automation`, `run_automation`, `remove_prompt`, `delete_automation`, `resolve_inbox_task`, `add_competitor`, `ask` — fails. The credential is a **static key minted as read-only**, and it simply does not carry write access:

```json 403 response theme={null}
{"error":{"type":"forbidden","message":"this API key does not have the write scope","status":403}}
```

```http Response header theme={null}
WWW-Authenticate: Bearer error="insufficient_scope", scope="write"
```

It is a `403` and not a `401` deliberately — the credential is fine, so a client that throws it away and reconnects is doing the wrong thing. Keys carry a scope chosen at mint time and cannot be widened afterwards: mint a read-and-write key and swap it in. `get_account` returns the scopes in force, which is the fastest way to settle the question.

An OAuth connection never sees this. Approving a client grants read and write — there is no read-only consent — so a `403` on a signed-in connection is not a scope problem. The one tool that refuses a signed-in connection on purpose is `get_ai_traffic_install`: its response carries your workspace's signing secret, and a consented token must not be able to read one credential out and mint another. The refusal says so, and the fix is to call it with an API key.

## Why am I getting a 401?

Your credential is missing, malformed, expired, or not for this server. The MCP endpoint answers:

```json 401 response theme={null}
{"error":"invalid_token","error_description":"The access token is invalid or expired"}
```

```http Response header theme={null}
WWW-Authenticate: Bearer realm="attensira", error="invalid_token", error_description="The access token is invalid or expired", resource_metadata="https://mcp.attensira.com/.well-known/oauth-protected-resource/mcp"
```

That message is identical for every cause — missing header, wrong shape, expired token, wrong audience, bad signature. It does not narrow anything down on purpose, because a `401` that explained itself would help someone probing with stolen tokens more than it helps you.

**On OAuth**, read the two sections above first: an hour-long lifetime and a revoked connection cover most of it. A token minted for a different service will also fail here — Attensira access tokens are bound to `https://mcp.attensira.com/mcp` specifically and are not accepted anywhere else, nor is anything else accepted here.

**On a static key**, check the shape. A valid key matches `^atn_live_[0-9a-fA-F]{32}$` — the literal prefix `atn_live_` followed by exactly 32 hexadecimal characters. Count them. Copying from a terminal that soft-wraps, or from a config file where an editor trimmed the line, is the usual cause of a key that is one or two characters short. The server checks the format before it makes any upstream call, so a truncated paste fails instantly and the request never reaches your workspace.

Then check the surrounding shape. The header value must be `Bearer ` plus the key, with a single space and no quotes around the key itself. If your config uses an environment variable, confirm the variable is actually set in the environment the client runs in — a GUI app launched from the dock does not inherit a variable exported in your shell profile. Print it with `echo ${ATTENSIRA_API_KEY:0:9}` to confirm the prefix without exposing the rest.

To separate a network problem from an auth problem, hit the unauthenticated health path: `curl -i https://mcp.attensira.com/healthz`. If that answers and your authenticated call does not, the credential is the problem.

## Why can't my client see the tools?

The server connected but no tools appeared, or the client reports the server as failed with no detail. This is almost always transport: Attensira speaks **Streamable HTTP only**, and a client that speaks stdio alone cannot reach an HTTPS endpoint directly.

Clients that connect natively include Claude Code, Claude Desktop, Claude.ai, Cursor, VS Code + Copilot, Windsurf, Cline, Codex CLI, ChatGPT, Zed and n8n. In Zed, add Attensira as a remote server (a `url` entry), not a local command. Older builds of several clients launch local processes only, and need the `mcp-remote` bridge:

```bash Shell theme={null}
npx -y mcp-remote https://mcp.attensira.com/mcp \
  --header "Authorization: Bearer ${ATTENSIRA_API_KEY}"
```

The bridge requires Node.js, and its first run downloads the package, so give it a few seconds before deciding it failed. Full per-client configuration is in [Connect a client](/mcp/connect).

If your client does connect natively and still shows nothing, check two more things. Some clients only load servers added before the conversation started — open a new chat. And some cache the tool list, so a restart or an explicit refresh in the MCP panel is needed after editing config. A healthy connection lists the tools in the [tool reference](/mcp/tools), `get_account` among them.

## Why did my key stop working?

A key that worked yesterday and 401s today has been revoked, either by you or by a workspace admin under Settings → Your assistant at [app.attensira.com/settings/access](https://app.attensira.com/settings/access). Revocation takes effect on the very next request, for every client using that key. Withdrawing an OAuth connection is not as immediate: the access token already in flight survives until it expires, because the MCP server verifies tokens locally and never asks us whether one is still wanted.

Keys cannot be recovered. Only a hash is stored, so there is no screen anywhere that will show you the original value again — not for you, and not for support. The fix is always to mint a new key and update every client that used the old one.


  When you rotate, update every place the key lives: shell profiles, `claude_desktop_config.json`, `~/.cursor/mcp.json`, `~/.codex/config.toml`, VS Code secret storage, n8n credentials, and any connector configured with a header. A client left on the old key fails silently in the background. Clients connected over OAuth are unaffected by a key rotation — they hold a different credential entirely.


If nobody revoked anything, re-read the 401 section — a config edit that dropped a character produces the identical error.

## Why does a model show no data?

Three different states look like "nothing" and mean very different things. Read the fields before concluding the model failed you.

* **`tracked: false`** — this model is not configured for your workspace, so it was never queried. Reporting it as 0% would invent a failure that did not happen. A new workspace tracks four models by default, `chatgpt`, `perplexity`, `claude` and `google_ai`; Gemini (Growth and above) is switched on in the dashboard.
* **`readable: false`** — no reader exists for that surface over this route, so Attensira collects nothing from it. `google_ai` has no API reader, for example; it is still read through the consumer surface, so Google AI Overview answers do arrive.
* **`value: null`** — not measured in this window. Null is not zero.
* **`value: 0` with `n > 0`** — a real, measured zero. The model was asked `n` times and never named you. That is a finding.

Deltas work the same way. A delta is `{value, real}` and must pass a two-proportion z-test at 95% before it is reported. Below that noise floor you get `{real: false, value: null}`, which means "no change we can prove" — never "no change".

Sampling depth also explains thin results: each prompt × model × country runs at most once per workspace-local calendar day, with n=1 on Starter and n=3 on Growth, Business and Agency. A prompt added an hour ago may have no readings until the next day's run. Failed runs are stored but excluded from `n`.

For the full reading guide, see [Reading the numbers](/measure/reading-the-numbers).

## Why is my request timing out?

The upstream budget is 35 seconds. A tool call that needs longer than that upstream is cut off, and your client reports a timeout or an aborted stream rather than a partial answer.

The usual cause is an over-broad query: a wide date range across every prompt, page and source at once. Narrow it — one `area` at a time, a shorter window, a filter on a single topic or model — and the same question usually answers in a fraction of the budget.

Long-running agent work has its own path. `ask` may return `status: "running"` instead of a result; that is not a failure. Poll `get_session` for the outcome, passing `include: ["events"]` when you want the intermediate steps. Do not re-issue `ask` while a session is running — it spends credits again.

Other server limits worth knowing when you are debugging a stalled connection: request header timeout 10 seconds, full request read 60 seconds, idle connection 120 seconds, and a maximum response size of 8 MiB. A response that would exceed 8 MiB is a signal to page or filter the query rather than retry it.


  Still stuck? Start from [Connect a client](/mcp/connect) and reproduce with the exact block for your client, then check `https://mcp.attensira.com/healthz` to rule out reachability. If you are writing the client yourself, the wire detail is in the [OAuth reference](/mcp/oauth).



# The analytics overview
Source: https://docs.attensira.com/measure/analytics-overview

Analytics opens on four questions: are we getting better, where are we losing and to whom, what did we do and did it work, and what changed out there. Every answer carries its n.

**Analytics → Overview** is where the analytics open. It answers four questions, in the order they are usually asked:

1. [Are we getting better?](#are-we-getting-better)
2. [Where are we losing, and to whom?](#where-are-we-losing-and-to-whom)
3. [What did we do, and did it work?](#what-did-we-do-and-did-it-work)
4. [What changed out there?](#what-changed-out-there)

Each question is one block. Every block has **Ask about this**, which opens a chat with the agent on that block's own numbers, and **See all**, which goes to the tab the block summarises (on question 4, each of its three lists has its own **See all**).

The window control above the blocks sets 7, 30 or 90 days, and the window is part of the URL, so a reload or a pasted link lands on the same window. Arriving from a row of the **Markets** table narrows the overview to that market, and the header offers the way back to every market.

## Are we getting better?

One line: how often assistants name you (your [share of voice](/measure/share-of-voice)) per day across the window, with the number of answers read and one sentence under it.

### The sentence

The sentence is written in one place, on the server, so its wording cannot drift between surfaces. It always states **n**, and it says "a real change" only when the movement is one we can prove:

| What you see | What it means |
| - | - |
| *Named in 31% of answers, up from 26% (n=1,840), a real change.* | The change against the previous window of the same length cleared the [significance test](/measure/reading-the-numbers#why-does-a-change-say-real), and the window was read on at least 70% of its days. |
| *Named in 31% of answers (n=1,840), nothing we can prove yet.* | Any difference from the previous window is inside the noise of the sample. |
| *Named in 31% of answers (n=212, read on 9 of 30 days), nothing we can prove yet.* | The difference passed the test, but fewer than 70% of the window's days were read (or, when it says *coverage unknown*, how many days were read is not known). Two thin samples weeks apart are not a trend. |
| *No answers were read in this window yet (n=0), so there is nothing we can prove yet.* | Nothing was read. This is not a zero. |

If the markets read changed between the two windows, the sentence compares only the markets read in both, by country code, and names the rest: *Over the markets read in both windows (US, GB): named in 31% of answers (n=1,640), nothing we can prove yet. DE added, so left out of the change.* A market read before and not now is named as *no longer read*. When no market was read in both windows, the sentence says there is nothing to compare it with yet. Under the chart, a line says the same in full: *Germany added in this window. The change is measured over the markets read in both windows, so a new market does not show up as a move.*

The sentence never says why the number moved. Whether a change of ours moved it is question 3, and even there the answer is movement observed after the change, not proof of cause.

### Markers

A marker sits on each day one of our changes went live. Several changes on one day are one marker that names them. Delivered work that is not on a live page yet, such as a draft nobody has published, gets no marker, because no engine could have read it.

Hovering a day shows its rate and the n behind it.

### Goals

Under the line, each active goal is a chip with its current value, its target and whether it is on pace. A goal whose value was not measured shows a dash, not a zero. The chip opens the goal on the **Goals** tab.

## Where are we losing, and to whom?

The prompts in state **lost**: across the engines read in the window, you are named on none and a rival is named on at least one. See [Prompts](/tracking/prompts) for how a prompt's state is set.

The block shows the five worst, and says how many there are in total. Worst means:

1. lost on the most engines;
2. then the most searched (a prompt with no known search volume ranks after every prompt with one);
3. then the order of the prompts board.

Each loss shows:

* **The prompt**, which opens its drawer, with how many engines it is lost on, its topic, and how often you were named, with n.
* **Who is named instead**, on the engine where the loss is worst.
* **What decided it**: the [deciding source](/measure/deciding-sources) that carried the most weight on that engine, or across every engine if nothing was cited on that one. Your own domain is never named here. If no answer cited anything, the row says so.
* **What we are doing about it**: the latest work on that prompt and where it stands (Proposed, Approved, In progress, Declined, Delivered, or Paused at the spending cap), linked to the work item. With no work on the prompt, the row offers **Start**. Every row also links to the prompt's [win plan](/measure/win-this-answer).

**Start** does not create anything. It opens a chat with the loss written in: the prompt, how many engines it is lost on, the rival on the worst engine and how often you were named there, the deciding source and its citations, and a request to read the win plan, propose the first piece of work, and ask you before starting anything that spends. Nothing is sent until you press send.

If the work log for a prompt could not be read, the row says so and does not offer **Start**. Offering to start work that may already exist is how the same work gets paid for twice.

## What did we do, and did it work?

The last five changes that shipped in the past 90 days, newest first by the day they went live. Each change shows its kind, the day it went live (or was delivered, for work that is not on the web), the named rate before and after with n on each side, a verdict, and the reason for the verdict in one line.

The 90 days do not follow the window control. A change's last re-read lands about 34 days after it goes live, so a shorter look-back could only ever say **Too early**.

### The verdict words

The verdict comes from the change's [re-reads](/measure/before-and-after), read across every tracked prompt and engine the change was meant to move. These are the four words, with the text you see when you hover each one:

| Verdict | On hover |
| - | - |
| **Moved up** | After this went live, the prompts it was meant to move were named more often, by more than the noise in the sample. That is movement after the change, not proof the change caused it. |
| **Moved down** | After this went live, the prompts it was meant to move were named less often, by more than the noise in the sample. That is movement after the change, not proof the change caused it. |
| **Nothing we can prove yet** | We re-read the prompts it was meant to move. Any difference was within the noise in the sample, or there was too little measurement before it went live to compare against. |
| **Too early** | We re-read the prompts a change was meant to move 7, 14 and 30 days after it goes live. None of those re-reads has a verdict yet. |

How a change gets its word:

* **The latest re-read with a verdict decides.** The +30-day re-read replaces the +14, which replaces the +7, because a later window has more evidence. They are not averaged.
* **Moved up** when more prompt-engine pairs were named more often than less often, past the significance test. **Moved down** is the reverse. When some pairs went the other way, the reason line says how many.
* **Nothing we can prove yet** when no pair moved past the test, when pairs moved up and down in equal numbers, when there was too little measurement before the change to compare against, when the prompts it was meant to move are no longer tracked, when the change was never found on the live page, when its re-reads were stopped, when no tracked prompt is tied to it, or when no re-read is on record for it.
* **Too early** while no re-read has a verdict and one is still booked or being read (the reason gives the date of the next booked one), and for delivered work that is not live yet, whose re-reads are booked once it is.

The same four words, with the same rules, appear on a prompt's changes timeline in its drawer. There the verdict is about that prompt's engines only.

### Committed vs delivered

Under the changes, a **Committed vs delivered** line compares the work committed for the current period with the work delivered, counted in work units (articles, fixes and pitches), never in money. Committed and delivered work is not reported yet, so the line reads *Not available yet. Committed and delivered work is not reported yet.* in place of the counts.

## What changed out there?

Three lists of things that happened outside your site in the window: **Competitor moves**, **New deciding sources** and **Wrong claims about you**.

None of the three is reported yet. Each list shows its heading and *Not available yet. We are not watching for this yet.* instead of an empty list, because an empty list would say we looked and found nothing.

## When a block cannot be read

Every block is read on its own, with its own time limit, so one slow or failed read never takes the others down. A block is in one of three states:

* **Read**: the numbers, or a sentence saying the window had none ("No prompt is lost to a rival in this window").
* **Not available**: the data behind the block is not reported for this workspace yet. The block says *Not available yet.* and what is missing.
* **Failed**: the read went wrong or took too long. The block says *We couldn't load …* with the reason and a **Retry**. Nothing is lost by retrying; it is a read.

On every block, a dash and a zero mean different things:

> A dash means not measured: no answer was read for it. A 0 means we read the answers and the count was zero.

## Export

**Export CSV** in the title row writes the whole overview to one file. Every rate travels with its n, an unmeasured value is an empty cell rather than 0, and a block that was not read is written as a comment line with its state and reason, so a file opened months later cannot be mistaken for "nothing happened".

## More detail

Below the four questions, a closed **More detail** section keeps the engine split, the competitors and the markets tables.

## Related

* [Reading the numbers](/measure/reading-the-numbers): n, the dash, and when a change is real.
* [Before and after](/measure/before-and-after): the re-reads behind every verdict.
* [Who decides an answer](/measure/deciding-sources): the sources behind each loss, and the next move for each.


# Before and after: re-reads
Source: https://docs.attensira.com/measure/before-and-after

How Attensira checks whether a shipped change moved anything: three re-reads at +7, +14 and +30 days, each comparing a week before the change with a week around the re-read date, only once the change is live.

A change shipping is not the same as a change working. After something ships, Attensira reads the same tracked prompts again and compares them with how they read before. That comparison is the only place the product uses movement words.

## What starts the re-reads

Re-reads start when a change reaches the world:

* a pull request Attensira opened is **merged**;
* a rewrite, an article or a copy item is **marked shipped** with its live URL;
* an article is **published through a connector** such as [Notion](/agent/notion);
* an [off-site placement](/agent/off-site#how-a-placement-is-verified) is **verified**;
* a read of one of your own pages **sees a change** on it, whether we shipped it, you made it from our advice, or nobody suggested it. See [Page changes](/measure/page-changes).

The re-reads cover the tracked prompts attached to the change (for a pull request, the `prompt_ids` given to `propose_fix`). A shipped change with no tracked prompts attached is not re-read. A change seen on your page is measured on the prompts its suggestion names, else the prompts that cited the page; [Page changes](/measure/page-changes#the-before-and-after) has the order.

## Three re-reads: +7, +14 and +30 days

Each change is re-read three times, at **+7**, **+14** and **+30** days after it shipped.

Each re-read compares two windows of the prompt's daily reading slots:

* **Before:** the 7 slot days before the change. For a change seen on your page, the 7 slot days before the last read that still saw the old version, so the days the change may or may not have been live count on neither side.
* **After:** 7 slot days centred on the +N date.

A re-read is read, and its verdict sent, only once its after-window has fully happened: about 4 days after the +N date, so around **day 11, day 18 and day 34**. Both windows always end on or before the day it is read.

Every verdict states its sample size (`n`), and none is a day-over-day comparison.

## Is the change live?

Before measuring, the re-read checks that the change is on the live page:

* For a **rewrite** or **copy**, the exact new text must appear on the page.
* For a **new article**, the page must load with content.

If the change is not live, that re-read is recorded as **Not live yet**, with the reason in terms of what happened ("merged, but the new text is not on the page yet"), and nothing is measured.

A change seen on your page is not checked again: the read that saw it is the evidence it is live. If the page goes back before the re-reads run, they are cancelled.

## The verdicts

| Verdict | Meaning |
| - | - |
| **Moved** | Named more often after the change, on X of N prompt and engine pairs, across M answers, past the significance test |
| **Fell** | Named less often, past the same test |
| **No change** | The before and after windows do not differ past the test |
| **Too close to call** | A difference inside the noise |
| **Too few answers** | Not enough readings from before the change to compare against |
| **Not live** | The change was not found on the live page, so nothing was measured |
| **Re-read booked** | Not read yet; the date shown is the day it will be read |

Every result is a single dated reading. A **Moved** verdict is what lets the [morning brief](/agent/morning-brief), the [receipt](/account/receipt) and the [win plan](/measure/win-this-answer) use movement wording at all.

## The labels beside a page

Where a shipped change is shown against the page it changed, as in the **Last change** column of the [Pages](/traffic/pages) grid, its re-read result is shown as one of three labels, all read from one map:

| Stored re-read result | Label |
| - | - |
| `moved` | **moved** |
| `no_change` or `inside_noise` | **nothing we can prove yet** |
| `insufficient_baseline` | **too early** |
| No result yet: the re-read has not run | **too early** |

**moved** says the number moved after the change, past the significance test. It is observed movement, not proof that the change caused it: the re-read cannot rule out everything else that changed in the same weeks. "The windows agree" and "they differ by less than the noise" are different findings that earn the same honest sentence, so both read **nothing we can prove yet**. A result the map does not know also reads **nothing we can prove yet**, never **moved**.

Only the AI re-read carries a label. Google clicks and positions and AI-crawler fetches shown around a change are counts with their own sample sizes; no verdict is drawn from them. See [Google search](/measure/google-search).

## How a verdict reads

Each verdict, in its email and in the action log, opens with the windows it compared:

> Re-read at +14 days, read on 2026-10-06: 2026-09-15 to 2026-09-21 before the change, against 2026-09-26 to 2026-10-02 after it.

## Where you see them

* **Inbox → Done**: each shipped item shows its latest re-read result, or **Re-read booked** with its date. See [Shipped](/agent/inbox#shipped).
* **The prompt page**: **Before and after** lists this prompt's re-reads with their samples, windows and outcome. See [Prompts](/tracking/prompts#the-prompt-page).
* **The morning brief**: **What we read again, and when**, at most five prompts, soonest first.
* **The win plan**: `rereads_at` is the day the next booked re-read for that prompt is actually read.
* **Client reports**: the re-read verdicts are the only place a [client report](/agencies/client-reports) uses movement language.


# Boards
Source: https://docs.attensira.com/measure/boards

Charts kept as the questions that drew them: our boards and yours, what a pin stores, the CSV export, and a link that opens a board without signing in.

A board is a page of charts you come back to. It lives under **Analytics → Boards**, and it holds two kinds: **ours**, which we keep for every workspace, and **yours**, which hold what you pinned.

## A pin is a saved question, not a picture

When you pin a chart, the board stores the question that drew it: the metric, how it is grouped, the window and any topic or prompt it is filtered to, plus whether to draw it as a chart, a table or a single number. It never stores the numbers or an image of them.

Every time the board opens, each pin's question is asked again. A share of voice pinned on Monday shows Friday's share of voice when you open the board on Friday, and it is the same figure the rest of the dashboard shows that day.

The window behaves the way it was asked:

* A pin asked over **the last N days** keeps reading the last N days, ending today.
* A pin asked over **fixed dates** keeps reading those dates.

## Ours and yours

**Ours** are defined by us, the same for every workspace, and never stored, so a better default reaches every workspace the day it ships. They are read-only: you can open and export them, and nothing else. To change one, pin the questions you care about to a board of your own.

| Board | What it shows |
| - | - |
| **Overview** | Share of voice, and share of voice week by week over 90 days. Citation rate. Average position when named. Prompts by state. |
| **Engines** | Share of voice, citation rate and average position, each split by engine, and share of voice by engine week by week over 90 days. |
| **Search** | Clicks from Google and impressions in Google, week by week over 90 days. Average Google position. Pages by clicks from Google (top 20). Tracked keywords' rank. AI Overview presence by keyword. |
| **Traffic** | [AI crawler](/traffic/ai-traffic) fetches, and visits referred by AI answers, in total and by page (top 20 each). |
| **Ads** | Every Google Ads campaign with where it stands with you, and spend only for campaigns you approved. |

Unless a row says otherwise, a pin on our boards reads the last 30 days.

A board whose source this workspace has not connected still opens. The pins that read that source say why they have nothing to show, rather than drawing an empty chart that looks like a zero.

**Yours** are what you pinned. A workspace keeps up to **50** boards of its own, and each board holds up to **24** pins. Everyone in the workspace sees the same boards.

### The Ads board

The Ads board lists every campaign with its approval state: drafted, waiting for your approval, approved, failed before anything was built, or found in your Google Ads account but not managed by us.

**Spend is shown only for campaigns you approved.** A draft, or a campaign waiting for your yes, never carries a spend figure, whatever the Google Ads account reports, so nothing on the board reads as running before you said it could. Spend is month to date, in your account's currency. Each campaign without a figure says why: not approved yet, waiting for your approval, failed before anything was built, not managed by us, or not yet reported by Google this month.


  In the dashboard the Ads board does not draw its campaign list yet. **Export CSV** on the Ads board writes one line per campaign: its name in `group_1_value`, its approval state in `group_2_value`, and, for approved campaigns, its spend in `value`. That spend is in micros (millionths) of the currency in `unit`, as the `metric` column `ad_spend_micros` says, and a campaign without a figure carries its reason in `error`.


## Pinning a chart

**Pin to board** sits under:

* the **Are we getting better?** chart on the Analytics **Overview**, and
* any chart, table or metric the agent draws in a [chat](/agent/what-the-agent-shows) from your measurements.

Press it, then pick one of your boards or name a new one; a new board is made and the pin goes on it. Only your own boards are offered, because ours take no pins. A board that already holds 24 pins cannot take another.

A view that was not drawn from a measurement, such as a draft or a card, has no question to keep, so it has no Pin control.

When a pin names a topic or a prompt, the question is checked as you pin it. A topic or prompt that is not in this workspace is refused then, rather than shown broken every time the board opens.

On your own boards you can also:

* **Rename** the board from its title.
* **Remove** one pin.
* **Delete** the board. Its pins go with it; they are only questions, and the data they read is untouched. Deleting a board also turns its link off.

Making a board, pinning, renaming and reordering are refused when the org's credits are used up. Removing a pin, deleting a board, exporting, sharing and turning a link off always work.

## Reading a pin

Every pin carries its **n**: how many observations its rows stand on, summed across the rows. Read it first, for the reasons in [Reading the numbers](/measure/reading-the-numbers).

* A cell nobody measured is drawn as an **em dash**, never as 0. A **0** means we read the answers and the count was zero.
* A pin with no rows in its window says "Nothing in this window yet."
* A pin that hit its row limit says how many rows it is showing.
* Coverage gaps are named under the pin.

**One pin failing never fails the board.** A pin whose question could not be answered says so in its own place, and every other pin still shows its answer. A topic or prompt deleted after it was pinned is one way this happens.

## Exporting a board

**Export CSV** downloads the board as one table, with one line per row of every pin. Both our boards and yours export.

| Column | What it holds |
| - | - |
| `board`, `pin` | The board's name and the pin's title. |
| `metric` | The metric the pin asks for. |
| `window_from`, `window_to` | The days the answer covers, both inclusive. |
| `group_1`, `group_1_value`, `group_2`, `group_2_value` | What the row is grouped by, and its value: an engine, a week, a page. Empty for a single number. |
| `value`, `unit` | The answer and what it is measured in. |
| `n` | What that row stands on. |
| `error` | Why the pin has no answer, when it has none. |

Two rules carry over from the dashboard:

* **An unmeasured value is an empty cell, never 0**, and its `n` is still written beside it, so "measured, none" and "not measured" stay apart in a spreadsheet too.
* **A pin with no answer is still a line**, carrying its error, so the file never drops a pin the board showed as broken.

Text a spreadsheet would run as a formula, such as a prompt that starts with `=`, is written with a leading apostrophe so it opens as text.

## Sharing a board with someone who has no account

**Share** on one of your boards makes a link and copies it. Anyone with the link can open the board without signing in. Our boards cannot be shared; pin what you need to a board of your own and share that.

* The link lasts **180 days** from when it was made. The shared page says until when.
* **Pressing Share again gives back the same link**, so copying it twice never breaks the copy you already sent.
* **Turn off link** stops it at once. Whoever opens it after that is told the board's owner turned it off. Your other shared links keep working: other boards, [client reports](/agencies/client-reports) and [receipts](/account/receipt).
* After you turn a link off, **Share** makes a new link with a fresh 180 days. The old one stays off.

### What the reader sees

The board's name, and each pin's title, chart, numbers and n, drawn exactly as you see them, with no controls. Row labels show as they do for you, so a pin grouped by prompt or page shows those prompts and pages; pin only what you are happy for the link's holder to read.

The reader does not see your workspace, your org, who made the board, or anything about cost or credits. The page is not indexed, and a pasted link previews only as "Shared board" and "A page shared with you."

A shared board's reading is kept for **10 minutes**: everyone who opens the link inside that window sees the same reading. A reading in which a pin could not be read on our side is not kept, so the next open asks again. Any change to the board, such as a pin added or removed or a rename, shows on the next open.

If the link will not open, the page says which of these it is:

| The page says | What happened |
| - | - |
| This link was turned off by its owner | The board's link was turned off, or the board was deleted. Ask for a new link. |
| This link has expired | It is more than 180 days old. Ask for a new link. |
| This link isn't valid | It was copied only in part, or the workspace's shared links were rotated. |
| This board is being opened a lot right now | The link was opened very often in a short time. Try again in a minute. |
| We couldn't load this board just now | The board could not be read at that moment; the link itself may be fine. Reload in a moment. |

### Turning off every shared link at once

Board links are signed with the same workspace secret as receipts, shared rewrites and client reports. **Rotate** on the [receipt](/account/receipt#sharing-a-receipt-without-an-account) stops all of them together, board links included. To give a reader a working link again after a rotation, press **Share** on the board; the new link keeps the old one's expiry date.

## Over the API

Dashboard routes (signed-in session):

* `GET /v2/projects/{id}/boards`: ours and yours, with the limits.
* `POST /v2/projects/{id}/boards` with `{"name": "..."}` makes an empty board.
* `GET /v2/projects/{id}/boards/{boardId}`: the board with every pin asked now. Each pin carries its table (columns, rows, `n`, unit, window and gaps) or its error. `not_connected` lists each source the board reads that this workspace has not connected, and on the Ads board `campaigns` holds the campaign list.
* `PATCH /v2/projects/{id}/boards/{boardId}` renames a board, or reorders its pins with `pin_order` and the `pins_version` you read it at. A reorder against a board someone else changed in the meantime is refused, so reload and reorder again.
* `DELETE /v2/projects/{id}/boards/{boardId}` deletes a board and turns its link off.
* `POST /v2/projects/{id}/boards/{boardId}/pins` with `{title, query, view: {component: "chart" | "table" | "metric", props}}` pins a question. `query` names a `metric`, up to two `group_by` dimensions, the window as `days` or as `from` and `to`, and any filters (`topic_ids`, `prompt_ids`, `engines`, `country` and the like), all at its top level. An unknown field is refused rather than ignored.
* `DELETE /v2/projects/{id}/boards/{boardId}/pins/{pinId}` removes one pin.
* `GET /v2/projects/{id}/boards/{boardId}/export.csv`: the CSV above.
* `POST /v2/projects/{id}/boards/{boardId}/share` returns `{token, url, issued_at, expires_at, expires_in_days}`, the same link while one is live. `DELETE` on the same path turns it off.

Our boards are addressed by name (`overview`, `engines`, `search`, `traffic`, `ads`), and any edit to one answers `405`.

Public route, where the token is the credential:

* `GET /v1/boards/{token}`: the shared board. A turned-off link answers `404`; an expired or invalid one answers `401`; a link opened too often answers `429`.

Boards are not MCP tools.


# Citation rate
Source: https://docs.attensira.com/measure/citation-rate

How often a model attaches a link to your own domain, measured as successful runs citing you divided by all successful runs in the window.

Citation rate is the share of successful runs in which a model attached at least one link to your own domain. It is successful runs citing your domain, divided by every successful run in the window — the same denominator as [share of voice](/measure/share-of-voice), so the two are directly comparable.

## What counts as a citation?

A citation is one URL the model attached to its answer, pointing at a domain you own. Links appear as footnotes, source lists, inline references, or a sources panel depending on the model surface; Attensira normalises all of them to the same thing — a URL, attributed to a domain.

As with mentions, the unit is a **run**. A run where the model links four of your pages counts once toward citation rate. Those four URLs still matter, but they matter to [citations and sources](/measure/citations-and-sources), which counts URLs rather than runs.

```text theme={null}
citation rate = successful runs citing your own domain
              ÷ all successful runs in the window
```

Failed runs are excluded from the denominator. Runs where the model wrote an answer with no links at all still count in the denominator — a linkless answer is a real answer, and it is a real absence of a citation.

## Why is citation rate lower than share of voice?

Almost always, yes — and the gap is the point, not a bug. Two independent things have to happen for a citation. The model has to decide your brand belongs in the answer, and then it has to decide a page on your domain is worth linking. The first is a condition of the second in practice, so citation rate is bounded from above by how often you are discussed at all.

Several ordinary mechanics widen the gap:

* **Models answer from memory.** A model naming you from training or internal knowledge has no URL to attach. It knows you without having just read you.
* **Not every surface links.** Some model surfaces attach sources on nearly every answer; others rarely do. Your citation rate is partly a property of which [models](/tracking/models) you track.
* **The link may go somewhere else.** A model can recommend you and cite a review site, a marketplace listing, or a comparison article that talks about you. That is a mention with a citation to someone else's domain.


  The engine's own framing: **"Mentioned is not cited: a model can name a brand it never links."** A wide gap between your two rates is a legible, actionable signal — you are known but not read.


## What do I do with a wide gap?

Treat share of voice and citation rate as answering different questions. Share of voice asks whether the model considers you part of the answer. Citation rate asks whether your own pages are the material it drew on. A brand with high mentions and low citations is being described through other people's writing; the lever there is publishing pages that answer the prompts directly, and seeing which of your [pages](/traffic/pages) get picked up.

A brand with a low share of voice does not have a citation problem yet. Fix the first number first — no amount of linkable content helps in answers where you are never named.

Before reading a gap as meaningful, check `n` on both rates and check whether the difference cleared the significance gate described in [How we collect data](/measure/how-we-collect-data). Two rates built on a handful of runs can differ by a wide margin purely by chance.


# Citations and sources
Source: https://docs.attensira.com/measure/citations-and-sources

The difference between a mention, a citation and a source, plus how source share and average citation position are calculated.

A **mention** is your name in the answer text with no link attached. A **citation** is one URL the model attached. A **source** is a domain bucket across many citations. Three different objects, three different counts — and confusing them is the single most common misreading of AI visibility data.

## Mention, citation, source: what is the difference?

| Term | What it is | Counted as | Answers |
| - | - | - | - |
| Mention | Your brand named in the answer text, no link required | One per run | Does the model talk about you? |
| Citation | One URL the model attached to an answer | One per URL | Which pages did it link? |
| Source | A domain bucket aggregating many citations | One per domain, per window | Which sites feed the answers? |


  In the engine's words: **"Mentioned is not cited: a model can name a brand it never links."** The reverse also happens — a model can cite a page on your domain in an answer that never names you, because the page was useful evidence for something else.


The nesting is straightforward. Many citations roll up into one source. Many runs roll up into one [share of voice](/measure/share-of-voice) or [citation rate](/measure/citation-rate). Rates count runs; sources count URLs. Never compare a rate to a source share and expect them to reconcile — they have different denominators and different units.

## What is source share?

Source share is a domain's slice of all citations in the window: citations to that domain, divided by every citation Attensira recorded across all your tracked runs in that window. Unlike mention and citation rates, source shares **do** describe parts of a whole, because each citation belongs to exactly one domain.

```text theme={null}
source share = citations to a domain
             ÷ all citations in the window
```

That makes source share a map of the citation landscape around your prompts: which publications, review sites, forums, documentation hubs and competitors the models actually draw on. Your own domain appears in this list like any other. A high source share for a review site means models lean on it when answering your prompts — which tells you where being present is worth the effort.

Source share is sensitive to which prompts and which models you track, since those choose the runs the citations came from. Widen your prompt set and the source mix will shift.

## What does average citation position mean?

Average citation position is the mean index of a URL within the model's citation list, calculated **per source domain**. If a domain's URLs tend to appear as the model's first attached link, its average position sits near the top of the list; if they show up after several others, it sits lower.


  Average position is an index into the citation list. It is **not** a rank of your brand, not a search ranking, and not a competitive leaderboard position. Where your brand came in the answer's text is a different number, counted among the names you track; see [Share of voice](/measure/share-of-voice#where-did-you-come-in-the-answer).


Read it as a weak ordering signal and nothing stronger. Models order citations for reasons that are not documented and not stable, and an answer with two citations produces very different index arithmetic than an answer with twelve. Position moves are worth noticing only alongside a change in source share or citation rate; on its own, a shift from 2.4 to 2.1 is not evidence of anything.

## What was each link cited for?

A citation says the answer leaned on a page. It does not say what for. So every citation carries its **context**: the sentence of the answer that carries the link, verbatim. "Acme is the easiest to set up \[2]" beside the review it cites tells you which claim that page is propping up, and that is the claim you would have to answer on that page or elsewhere.

The context is found in the answer's own text, by the link itself, then by its footnote marker (`[2]`, `[^2]`, `【2】`), then by its domain written in the text. The answer's sentences are searched first and its reference lines (a footnote definition, a sources list) only after, because a sources list names the page without saying why it was cited. In a list item or a table row the context is that line. A context is at most 300 characters; a longer sentence is cut to a window around the link, on a word boundary.

The context is never invented. It is always a substring of the answer, and when the text carries no trace of a link, the context is empty:

| What you see | What it means |
| - | - |
| A quoted sentence | The sentence or line of the answer that carries this link |
| "The answer's text does not mention this link." (`context: ""`) | We looked, and the answer's text does not mention or mark this link. The model attached it without saying what it was for |
| — "recorded before we kept what each link was cited for" (`context: null`) | We have not looked. The answer is older than 90 days and was read before contexts were kept |

Contexts are kept for answers from the last 90 days: the stored answers in that window were read again when contexts were introduced, and every new answer gets them as it is read. An older answer shows the dash, not an empty context, because an empty context would claim we looked.

On the prompt page, pick a day and an engine on the day-by-day strip to open that day's answers: each lists its links with their context under **Links and what they were cited for**, beside its brand order. Over MCP and the REST API, each citation on `answer_history` and `answer_slice` rows carries `context` beside `url`, `domain` and `position`.

## How to read these numbers

Like every other number in the product, source and position figures carry sample information. See [Reading the numbers](/measure/reading-the-numbers) for how to handle small samples and unmeasured cells.


# Claims about you
Source: https://docs.attensira.com/measure/claims

The factual statements AI answers make about your brand, quoted verbatim and checked every night against the facts you confirmed in your brand profile. Only confirmed facts count.

An answer can name you, recommend you, and still get your price wrong. Attensira pulls the checkable statements out of every answer and checks the ones about you against the facts **you** confirmed. A wrong one is flagged once, and an automation can act on it.

## What a claim is

A claim is one factual statement an answer makes about a brand: what it costs, what it does or lacks, what it integrates with, a certification, where it is available, or a fact about the company. Opinions are not claims. "Acme starts at \$49 per seat" is a claim; "Acme is the easiest to use" is a stance, and is scored by [sentiment](/measure/sentiment) instead.

Every claim is a receipt, not a paraphrase:

* **The quote is the answer's own words.** A claim is kept only when its quote appears in the answer's text, with spacing normalized. A statement reworded by the model that reads the answer is dropped.
* **It names who it is about.** You (your brand, or a part of your brand family you confirmed), a tracked competitor, or a domain the answer cited. A claim about a brand that resolves to none of these is not kept, because nothing could ever check it.
* **It carries its sources.** The links the answer attached to the sentence the claim is in, when it attached any.

Claims are pulled from an answer in the same read as its [brand order](/measure/share-of-voice#the-full-brand-order), for answers from the last 90 days. An answer that has not been read yet has no claims list, which is different from a list that is empty: an empty list means the answer was read and made no claim we could check.

## Which facts a claim is checked against

Only claims about **you** are checked, and only against facts a person confirmed in your [brand profile](/agent/knowledge-and-skills#company):

* fields in **What you sell**, **Differentiators** and **Proof**, plus any field whose name is about pricing, wherever it is filed;
* that someone on the workspace **confirmed**. A field Attensira drafted from your site is our guess, not your word, and is never used;
* that are not **stale**. A confirmed field whose source page has changed since you confirmed it may no longer be true, and is set aside until it is confirmed again.

With no confirmed fact to check against, nothing is asked and every claim about you is `not_covered`. Confirm your pricing, your integrations and your certifications first: those are the facts an answer gets wrong in a way that costs a sale.

## The verdict

Each claim about you gets one of three verdicts:

| Verdict | Shown as | Means |
| - | - | - |
| `contradicts` | **Wrong** | A confirmed fact shows the claim is false |
| `matches` | **Correct** | A confirmed fact confirms the claim |
| `not_covered` | **Nothing to check against** | No confirmed fact speaks to it |

A claim not checked yet shows **Not checked yet**.

**Nothing to check against is not a wrong claim**, and it is not a right one either. Your facts are silent on it, and silence is not a contradiction. If a claim keeps landing here and it matters, confirm the fact that settles it.

The verdict is a decision, made with a confidence. A contradiction is only treated as a wrong claim when that confidence is **at least 80%**. Below it, the verdict is kept with its confidence and nothing is raised.

### When claims are checked

The check runs **every night at 04:15 UTC**, newest answers first, over the claims whose verdict is missing or out of date. A verdict goes out of date when:

* the answer is read again and its claims change;
* **you confirm, edit or un-confirm a fact.** Every claim checked against the old facts is checked again against the new ones, starting the next night and newest answers first. You never re-run anything by hand.

When a workspace is left with no confirmed fact to check against, every verdict is settled to "Nothing to check against" without asking again, so a contradiction of a fact nobody stands behind any more does not stay on the list.

## The `claim_wrong` event

A wrong claim raises one `claim_wrong` event. It is raised **once per distinct wrong statement against a fact**: the same sentence, said by three engines on ten days, is one event. It is raised again only if the fact it contradicts is edited, because then it is a different question.

The check only flags. It never edits a fact, never files work and never approves anything. What happens next is up to whoever listens for the event.

### Acting on it with an automation

`claim_wrong` is recorded in the same event log as the other [event types](/agent/autopilot#event-automations), but it is not one you can pick yet: the dashboard, MCP and the REST API offer only the five listed there, and the built-in Event responder does not listen for it. Only an event automation saved with an empty event list, which listens for every type, acts on it. Such a run's cause reads: "Because an AI answer said something about you that your confirmed company facts contradict."

The event carries what a run needs without reading anything else:

| Field | What it is |
| - | - |
| `quote` | The claim, verbatim |
| `kind` | `pricing`, `feature`, `integration`, `compliance`, `availability`, `company` or `other` |
| `fact_key`, `fact_label`, `fact` | The confirmed fact it contradicts, and that fact's value |
| `p` | The confidence of the verdict |
| `prompt_id`, `platform`, `surface`, `slot_day` | Where and when the answer said it |
| `reading_id`, `claim_id` | The answer and the claim, for `answer_history` and the receipts |
| `cited_urls` | The pages the answer attached to the claim, when it attached any |

When an automation does act on it, the event limits apply as for every event: at most 10 events per run and 3 runs per automation per rolling 24 hours, and events older than 48 hours are not acted on.

## Where you see claims

* **Over MCP and the REST API**, `answer_history` and `answer_slice` rows carry each answer's extracted `claims`, each with `kind`, `subject` (`self` for you, otherwise a domain), `quote` and `citation_positions`. `null` means the answer has not been read for claims yet; `[]` means it was read and made none. Verdicts are not on the answer rows.
* **`query_evidence`** counts wrong claims with the `wrong_claims` metric, by day or week, prompt field, engine and surface. See [MCP tools](/mcp/tools).
* **In the dashboard**, the prompt page's claims section and the analytics overview's wrong-claims list do not show verdicts yet. Each says so in place of a list, rather than showing an empty one that would read as "nothing wrong".

## What it gets wrong

The verdict is a judgement a model makes against the facts you confirmed, and it can be wrong. It errs toward silence: a contradiction it is unsure about is kept, not raised. It can only be as right as your facts: a confirmed fact that is out of date makes a correct answer look wrong. Keep the facts in your brand profile current, and read the quote before you act on a flag.


# Who decides an answer
Source: https://docs.attensira.com/measure/deciding-sources

For every prompt, on every model, the pages the answers actually leaned on — whether you are on each one, and the next move.

Share of voice and citation rate tell you whether you won a prompt. This page tells you **why** — which pages the answer read, how much weight each one carried, and what to do about the ones that are not yours.

## The grain

One group per prompt, per model, per surface. Surface is in the grouping for the same reason it is everywhere else in the product: a consumer answer and an API answer draw on different pages, and averaging them produces a plan that fits neither.

Country is deliberately **not** in the grouping. A page that decides a prompt in the US decides it in the UK too, and splitting by country here would quarter every sample for no gain in what to do next.

## Weight, not raw citations

Sources are ranked by **citation share × position**, not by how many times a page was cited. A page cited once at the top of every answer decided more than a page cited twice near the bottom of one, and a ranking that could not tell them apart would keep sending you after the loudest source instead of the one that actually decides.

Position weight is `1 / mean position`. A citation the model attached without a clear position is scored as if it sat at the back of the list — stated as such, rather than dropped or quietly treated as first.

## Classification: yours, a competitor's, or someone else's

Every source in a group is classified the same way the [sources report](/measure/citations-and-sources) classifies it, so a domain is never "ours" on one screen and "third party" on another:

* **Yours** — a page on your own domain.
* **A competitor's** — a page on a domain you track as a competitor.
* **Someone else's** — everything else: review sites, forums, documentation hubs, comparison pages, listicles.

## "You are on it"

A source is marked **on it** in either of two cases:

* it is your own domain, or
* you have a placement there whose execution status is **verified**.

An unverified or unknown placement is never shown as on it. This is the same discipline as everywhere else in the product: a claim without evidence does not get to look true.

## The next move

Every source carries two things: **where you stand on it**, and **what kind of source decides answers on that engine**. Together they say what to do next.

### Where you stand: new, better or fix

Every source in a group gets one of three words. Only the group's top ten sources are looked at when deciding whether a competitor ranks above you:

| Word | When |
| - | - |
| `new` | You are not on it. Nothing of yours is there to be cited yet. |
| `better` | You are on it, and a competitor's domain in the top ten ranks above it. |
| `fix` | You are on it, and no competitor in the top ten ranks above it. The work is the page itself. |

The group gets the same words, read off your best-ranked source: `new` when nothing of yours is in its top ten, `better` when a competitor ranks above your best, `fix` otherwise.

### On the Sources tab: one move per domain

**Analytics → Sources** has one row per domain for the whole workspace, so the per-group words are folded into four columns:

* **Type**: what sort of place the domain is (see below).
* **You're on it**: your own site, or a placement we verified.
* **Prompts decided**: the tracked prompts where this domain is in the top ten sources on at least one engine. A 0 is a measurement: the domain was cited, and never high enough to decide.
* **Next move**: what to do about this domain.

A domain that decides several prompts can have a different word on each. The move with the most work in it wins: `better` over `new` over `fix`. A domain we are ahead on for one prompt and behind a competitor on for another needs the `better` work.

The next move is the word and the type together, said as a verb:

| Where you stand | Type | Next move |
| - | - | - |
| `new` | Reddit, Community | Reply in the thread |
| `new` | G2, Directory | Get listed |
| `new` | Media | Pitch |
| `new` | Competitor | Publish our answer |
| `new` | Reference, AI platform | Get cited |
| `better` | any | Improve our entry |
| `fix` | any | Fix our page |

A domain that is not in the top ten sources for any prompt in the window has no move. Its cell is a dash, and hovering it says why.

The types:

* **Yours**: your own domain, or another domain confirmed as yours.
* **Competitor**: a domain you track as a competitor, whatever kind of site it is.
* **Reddit** and **G2**: each its own type, because each has its own way in.
* **Directory**: review sites and listing directories such as Capterra, TrustRadius, Trustpilot and Product Hunt, where there is a form to fill in rather than an editor to pitch.
* **Community**: social and user-written sites other than Reddit, such as YouTube, Quora, LinkedIn, Medium and GitHub, where the author is not the site.
* **Media**: the rest of the third-party web: publishers, blogs, listicles and trade press.
* **Reference**: institutional sources such as Wikipedia, `.gov` and `.edu` domains.
* **AI platform**: an assistant or AI tool's own site.

The type is inferred from the domain and cannot be corrected yet.

**The pill does not start anything.** Pressing it opens a chat with the move written in: the domain, how often it was cited, how many prompts it decides, whether you are on it, and the move, with a request to the agent to plan it and do the work, asking you only for what it cannot find itself. Nothing is sent until you press send.

### What decides on each engine

Alongside the word, every source and group carries what kind of source decides answers on that engine, and the move that works there. Today, every workspace gets this from one static, per-engine table, because the observations layer this could instead draw on does not have enough evidence yet:

| Engine | What decides answers there | The proven move |
| - | - | - |
| ChatGPT | Your own docs and pricing pages | Fix your own page |
| Claude | Review sites and listicles | Get listed |
| Perplexity | Threads, YouTube and roundups | Be in the thread or roundup |
| Gemini | Threads, YouTube and roundups | Be in the thread or roundup |
| Google AI Overview | Threads, YouTube and roundups (closest documented behavior) | Be in the thread or roundup |

An engine not in the table gets the threads and roundups row. Once your own workspace has read enough answers on a given source (at least 20 successful reads), this switches from the static table to what your own history shows. It always says which one it came from, so you know whether you are looking at a general rule about the engine or a pattern proven in your own data.


  Every row and every group carries **n**: the successful answers behind it, in the same window and counted the same way as the rest of the product. A share without its n is noise in a confident font.


## Where to find it

* On **Analytics → Overview**, each lost prompt names the source that decided it. See [The analytics overview](/measure/analytics-overview#where-are-we-losing-and-to-whom).
* On **Analytics → Sources**, in the **Type**, **You're on it**, **Prompts decided** and **Next move** columns.
* In a prompt's [win plan](/measure/win-this-answer), engine by engine.
* As the agent tool **`deciding_sources`**, so a chat or automation can pull the same map, as the same-named [MCP tool](/mcp/tools#deciding_sources) from your own assistant, and over REST as `GET /v1/deciding-sources`. Each takes `prompt` to narrow to one prompt and `days` to set the window.

```json MCP theme={null}
{
  "name": "deciding_sources",
  "arguments": { "prompt": "prm_9c2", "days": 30 }
}
```

See [Reading the numbers](/measure/reading-the-numbers) for how to treat `n`, `null`, and significance everywhere else these shapes appear.


# Goals
Source: https://docs.attensira.com/measure/goals

A goal is one number from a fixed catalog, a target and a date. Attensira measures where it starts, reads it daily by 06:00, and says whether it is ahead, on pace, behind or unmeasured with the same arithmetic everywhere.

A goal is one number Attensira can read, a target for it, and a date: *named in 30 of 60 prompts on ChatGPT by 31 December*, *2,400 Search Console clicks over 28 days by 31 March*. You say what you want in your own words; the agent turns it into a goal you can measure, and you decide whether to create it. After that the readings, the pace and the re-planning are the agents' job.

Every goal is held to three rules:

* **Where it starts is measured, never typed.** The baseline is the first successful reading Attensira takes after the goal is created. No person, assistant or model can set it.
* **A number that could not be read is never a 0.** The goal reads as **unmeasured**, with the reason ("Connect Search Console").
* **A goal reports where a number stands, never why.** Our shipped changes appear beside the chart as markers. No goal sentence says a number moved *because* of our work, and nothing is ever described as won, driven or earned.

## Setting a goal from your own words

On **Analytics → Goals**, type what you want into **Add a goal in your own words**, for example *Double demo requests from search by March*, and press **Propose a goal**. A chat opens in goal mode, and the Chief of staff takes it from there:

1. It reads the goals you already have, so it does not propose one twice.
2. It picks the one metric from the [catalog](#the-metric-catalog) that measures what you described, scoped to the assistant, topics or keyword you named. If the words are ambiguous (which event is a demo request, which assistant), it asks first, at most three questions.
3. It proposes the goal as a card. The card shows the metric, where the number comes from, **today's value with its `n`** (read from the source when the card is drawn, and not stored), the target and date, and the pace line between them.
4. You press **Create goal**, or **Not now**. Nothing exists until you press Create. Accepting the same card twice creates one goal.

If the metric's source cannot be read in your workspace (Search Console not connected, the AI traffic snippet not installed, Google Analytics or Stripe), the card says why and offers no Create. The Chief of staff offers the nearest metric that can be read, called a signal, and proposes it only if you agree.

The same goal mode starts from **Propose the next** on a goal that was hit or missed. Typing `/goal` in the chat composer does not start it yet.


  The goal chat is a chat like any other and spends [credits](/account/credits) the same way. Setting a goal, editing it and its daily readings spend none, and they keep working when the credit balance is spent.


### Starting goals at signup

During setup, Attensira proposes starting goals from what the first scan found: **become visible** on each AI assistant you left on, **be recommended** against the competitors you confirmed, and **grow clicks from Google Search** once Search Console is connected and Google Search is on. Each has a date 90 days out. You keep or switch off each one. The ones you keep are created when your plan starts, with each target set so it is never below where the goal starts.

## The metric catalog

A goal measures exactly one metric from this list. A metric outside it is refused, and so is a parameter the metric does not take: a goal that silently ignored *only the pricing topic* would read a different number from the one you asked for.

| Metric | What it counts | One reading covers | Better | Parameters |
| - | - | - | - | - |
| `ai_named_prompts` | Tracked prompts the assistant names you in, out of those in scope | The latest answer per prompt in the last 7 days | Higher | `platform` (required), `topic_ids` |
| `ai_share_of_voice` | [Share of voice](/measure/share-of-voice): answers that name you, out of every successful answer in scope | The last 7 days | Higher | `platform`, `topic_ids` |
| `ai_citation_rate` | [Citation rate](/measure/citation-rate): answers that cite your domain, out of every successful answer in scope | The last 7 days | Higher | `platform`, `topic_ids` |
| `google_keywords_top10` | Tracked Google keywords where you rank in the top 10, out of those read | The latest position per keyword in the last 7 days | Higher | `keyword_ids` |
| `google_keyword_position` | Your Google position for one keyword | The latest position in the last 7 days | Lower | `keyword_id` (required) |
| `search_clicks` | Search Console clicks | 28 days ending 4 days ago | Higher | — |
| `search_impressions` | Search Console impressions | 28 days ending 4 days ago | Higher | — |
| `ai_agent_fetches` | Fetches of your pages by AI systems, from the [AI traffic snippet](/traffic/ai-traffic) | 28 days ending 2 days ago | Higher | — |
| `ai_referred_visits` | Visits referred by AI assistants, from the AI traffic snippet | 28 days ending 2 days ago | Higher | — |
| `ga4_key_events` | Google Analytics key events | 28 days ending 2 days ago | Higher | `ga4_event` (required), `ga4_channel` |
| `ga4_sessions` | Google Analytics sessions | 28 days ending 2 days ago | Higher | `ga4_channel` |
| `stripe_new_customers` | New Stripe customers | The last 28 days | Higher | — |

* `platform` is one AI assistant your workspace tracks: `chatgpt`, `perplexity`, `google_ai`, `claude` or `gemini`. Without it, a share or rate goal reads across every tracked assistant. See [Models](/tracking/models).
* `topic_ids` narrows an AI goal to prompts in those [topics](/tracking/topics), at most 50. Every id must be a topic of this workspace.
* `ga4_channel` is `organic`, `ai_referred` or `all` (the default).
* An AI goal counts the same prompts the [Prompts](/tracking/prompts) board shows for that assistant, with the same rule for what *named* means. A prompt whose every read failed is in the whole it counts out of, never counted as a miss.

### Business signals

Four metrics are **business signals**: `ai_referred_visits`, `ga4_key_events`, `ga4_sessions` and `stripe_new_customers`. They are shown beside our work and never credited to it: a signal's sentence opens with *A business signal:*, and nothing in the product turns one into revenue we drove. There is no revenue-in-currency metric.

### What each workspace can read today

The catalog is the same for everyone. Whether a metric can be read is per workspace, and the metric picker, the proposal card and the API all say which and why:

| Source | Readable when | Otherwise the goal says |
| - | - | - |
| AI answers we read | Always | — |
| Google rank tracking | The workspace has tracked Google keywords | *No Google keywords are tracked yet* |
| Search Console | Search Console is [connected](/account/connections) and its history has been pulled | *Connect Search Console*, *Search Console needs reconnecting*, or *Search Console history hasn't been pulled yet* |
| AI traffic snippet | The snippet has reported at least once | *Install the edge snippet* |
| Google Analytics | Not yet: there is no Google Analytics connector | *Google Analytics isn't a connector yet* |
| Stripe | Not yet: there is no Stripe connector | *Stripe isn't a connector yet* |

A goal on a source that cannot be read can still exist (over the API, for example). It reads as unmeasured with that reason every day, and starts measuring the first morning its source can be read.

## Targets, dates and limits

| Rule | Value |
| - | - |
| Target, for a count | A whole number, at least 1 |
| Target, for a share or rate | Between 0 and 1: `0.4` is 40% |
| Target, for a Google position | A whole position from 1 to 100 |
| Target date | 7 to 400 days after the goal starts, and after today, as a `YYYY-MM-DD` day in your workspace's time zone |
| Name | At most 120 characters. Without one, the goal is named after its metric |
| Goals per workspace | At most **12** that are not archived. A 13th is refused with `409` until you archive one |

A goal's metric and parameters never change: a different metric is a different goal. You can change its name, target, date and status. A new target must beat the measured baseline.

## The first reading and the daily reading

**The first reading** is queued the moment a goal is created and lands within about 15 minutes. Until then the goal says *First reading by 09:15* (the time in your workspace's zone). Its first reading that succeeds becomes the **baseline**, the number the goal starts from. If its source cannot be read yet, the baseline waits for the first morning it can.

**Every morning**, from 04:00 on your workspace's own clock, Attensira reads each active goal once for that day. The readings normally land before the morning goal-pace check at 05:00, and always by **06:00**, ahead of the daily win plan. Each goal has at most one reading a day; a repeated or retried read is harmless.

A reading is one of:

| Reading | Means |
| - | - |
| **ok** | A value, with its `n` (prompts, answers, keywords or days behind it) and, for a count, the whole it is out of |
| **unavailable** | The source could not be read for a known reason, which the reading carries. No value, and never a 0 |
| **error** | The read was tried and failed. The next pass tries again |

A Google position reading that finds you outside the top 100 is a measured reading (*not in the top 100*), worse than any position, never unmeasured.

Only active goals are read. A goal that is hit, missed, paused or archived is not read until it is active again, so its chart stops at its last reading. Goal readings are part of the monitoring the platform runs, and spend no credits.

## Pace: ahead, on pace, behind or unmeasured

Every reading recomputes the goal's pace from stored readings, with fixed arithmetic. No model decides whether a goal is on pace, and the Goals page, the chip, the morning brief, the board report, MCP and the API all show the same stored verdict.

```text theme={null}
start     = the later of the goal's start day and its baseline's day
f         = (today − start) ÷ (target date − start), clamped to 0..1, in whole local days
expected  = baseline + (target − baseline) × f              the pace line
current   = mean of the ≤ 3 newest ok readings in the last 7 days
            (the median, for a Google position)
band      = the noise band for the metric's unit, below

ahead     current beats expected by more than the band
behind    current trails expected by more than the band
on pace   anything inside the band
```

"Beats" and "trails" follow the metric's direction: for a Google position, lower is better.

The pace line starts at the baseline's own day when the first good reading came after the goal was set (a source connected weeks later), so a goal never reads as behind for days nothing could measure.

The band keeps day-to-day noise from flipping the verdict. It is computed from the newest reading's sample:

| Unit | Band |
| - | - |
| A count out of N (prompts named, keywords in the top 10) | `max(1, √(N × p × (1 − p)))`, where `p = expected ÷ N`, held between 0.05 and 0.95 |
| A share or rate with `n` answers | `max(0.02, √(p × (1 − p) ÷ n))`, where `p = expected` |
| A 28-day count (clicks, impressions, fetches, visits, events) | `max(1, √expected)` |
| A Google position | 1 |

A goal is **unmeasured** when there is nothing honest to compare, and always says why:

| Reason | Means |
| - | - |
| *First reading by 09:15*, *Waiting for the first reading* | No baseline yet |
| The source's own reason (*Connect Search Console*) | The newest reading in the last 7 days could not be read |
| *No reading in the last 7 days* | Nothing was read in the window |
| *paused* | You paused the goal |

### The sentence

Each goal carries one sentence, written by the server and shown unchanged everywhere. It is built from counts with their `n`, places the value against the pace line, and never says why the number moved:

> Named in 21 of 60 prompts on ChatGPT (n = 58). 19 expected by today, so on pace for 36 by 30 Nov.

> Cited in 14% of AI answers on Perplexity against 10% expected by today (n = 180), so ahead of pace for 20% by 31 Dec.

> Search clicks: not measured yet. Connect Search Console.

A share read from a single answer is not a share, and says so: *too few answers read to give a share yet (n = 1)*.

## Hit, missed, paused and archived

| Status | How a goal gets there | How it leaves |
| - | - | - |
| **active** | Created, or re-opened | — |
| **hit** | A reading finds the smoothed current value at or past the target. It is not read again while hit | Raise the target, and the goal is active again |
| **missed** | Its date passes without being hit. It is not read again while missed | Move the date later, and the goal is active again |
| **paused** | You pause an active goal. It is not read while paused | Resume it |
| **archived** | You archive it, from any status. Archived goals leave the Goals page and the lists, and cannot be edited | Final |

Hit and missed come only from readings, and stay until you edit the target or the date. When a goal is hit or missed, a row appears in your [Inbox](/agent/inbox): *Goal hit: … Propose the next?* or *… missed its date. Move the date or propose the next?* Its button opens a chat with the ask filled in, where the Chief of staff proposes the next goal or a new date for you to accept.

## When a goal falls behind

At 05:00 on your workspace's clock, after the readings, Attensira looks at every active goal whose pace is **behind** and decides whether the gap is worth re-planning the work:

* More than **30%** of the climb from baseline to target behind, with fewer than **14 days** left, always counts.
* Otherwise the agent weighs the numbers (the gap, the days left, the readings with their `n`, the work already shipped and waiting for its [re-read](/measure/before-and-after), the last re-plan) and re-plans only on a confident yes.

What happens next follows your [approval mode](/agent/approval). In **Draft** or **Act**, one re-plan run of the daily win plan starts, named for the goal (*Re-plan: … is behind pace*), and the goal's chip reads **Behind, re-planning**. It is an ordinary run under your approval mode and change caps. In **Ask**, nothing starts: one Inbox row offers the re-plan (*… is behind pace: re-plan the work toward it?*).

A goal is re-planned at most once a week. A re-plan you cancelled, or an offer you dismissed, is not raised again that week. The check never edits a goal, a target or a setting.

## When a source breaks

If a connection the readings use stops answering (Search Console's grant was revoked), the Inbox gets one **Reconnect Search Console** row naming the goals waiting on it. It is the same row the connector's own health check raises, so you see one, not several. The next reading that succeeds closes it. *Not connected* and *not a connector yet* are states the goal shows, not failures, and raise nothing.

## Where goals show up

* **Analytics → Goals**, the first Analytics tab. Each goal is a tile with its current value, target, date, pace chip, sentence and source. Opening one shows its daily readings against the pace line, the days our changes went live as markers, and the controls you steer with: target, date, pause or resume, archive. A day that could not be read stays on the chart with its reason. Edits name the version you were looking at; if the goal changed since you opened it, the sheet reloads it and says so.
* **The Analytics overview**: each goal as a chip with its value and target.
* **The Monday [morning brief](/agent/morning-brief)**: a status line counting goals by pace, and each goal's sentence.
* **The board report**: each goal's pace for the period. See [client reports](/agencies/client-reports).
* **Your [Inbox](/agent/inbox)**: hit, missed, behind (in Ask mode) and reconnect rows.
* **Your assistant and your code**: below.

## From your assistant or your own code

The [MCP server](/mcp/overview) has the goal tools below, and the [REST API](/api/overview) has their twins:

| MCP tool | REST | Does |
| - | - | - |
| `get_goals` | `GET /v1/goals` | The workspace's goals, oldest first. Archived goals are left out. Over REST only, `?status=` takes a comma-separated list of `active`, `hit`, `missed`, `paused`, `archived`, or `all` |
| — | `GET /v1/goals/metrics` | The catalog as this workspace sees it: each metric's key, label, source, unit, direction and parameters, whether it can be read here (`available`, with `reason` when not), and the AI platforms the workspace tracks |
| `set_goal` without `id` | `POST /v1/goals` | Sets a goal: `{name?, metric, params?, target, target_date, work_area_id?}`. Answers `201` |
| `set_goal` with `id` | `PATCH /v1/goals/{id}` | Edits a goal: `{version?, name?, target?, target_date?, status?, work_area_id?}`. `status` is `active` (resume), `paused` or `archived`; `work_area_id: null` moves the goal to the whole workspace |

The writes need write access: a read-and-write API key, or an OAuth connection. They spend no credits and work when the credit balance is spent. Call `set_goal` only on the person's instruction.

* **The baseline is not a field.** A body carrying `baseline` is a `400`, and so is an edit that tries to change `metric` or `params`.
* **Retries.** Send an `Idempotency-Key` header (`idempotency_key` on `set_goal`) with a create: a retry answers `200` with the goal the first call set. The same key reused for a goal on a different metric is a `422` carrying that goal.
* **Versions.** Without `version`, an edit applies to the goal as it is now. With one, an edit of a goal that changed since you read it is a `409` carrying the goal as it now stands (`details.goal`).
* **Limits.** Past 12 goals that are not archived, a create is a `409` with `{limit}`.

Each goal comes back with:

| Field | Notes |
| - | - |
| `id`, `name`, `metric`, `metric_label`, `params` | What it measures |
| `source`, `source_status` | Where the number comes from, and `{available, reason}` for this workspace today |
| `unit`, `direction`, `signal` | `count_of`, `rate`, `rolling_count` or `position`; `up` or `down`; `signal` is `true` for a business signal |
| `baseline`, `current` | `{value, n}`. `value` is `null` when nothing was measured, never `0` |
| `target`, `target_date`, `start_day` | In the metric's unit; days are `YYYY-MM-DD` in the workspace's zone |
| `pace` | `{state, expected: {value}, reason}`. `state` is `ahead`, `on_pace`, `behind` or `unmeasured`; `expected.value` is the pace line today, `null` while unmeasured |
| `sentence` | The server's sentence. Quote it rather than writing your own |
| `status`, `status_reason` | `active`, `hit`, `missed`, `paused` or `archived` |
| `replanning` | `{since, session_id}` while a re-plan from the last 7 days is under way, else `null` |
| `scope`, `work_area_id`, `work_area_ids` | `workspace`, or the project that owns the goal; and every project serving it |
| `created_via` | `person`, `chief_of_staff`, `onboarding` or `assistant` |
| `version`, `created_at`, `updated_at` | Name `version` on an edit to refuse a stale one |

A goal set over MCP or the API is recorded as set by your assistant (`created_via: "assistant"`), on behalf of the key's person.


  `set_goal` takes `name`, `metric`, `target`, `target_date` and `work_area_id`, and nothing else. It sends no `params`, so a metric that needs one (`ai_named_prompts` needs `platform`, `google_keyword_position` needs `keyword_id`, `ga4_key_events` needs `ga4_event`) is set with `POST /v1/goals`, or from your own words in the app. Pausing, resuming and archiving (`status`) and naming a `version` are REST only.


### Webhooks

Every transition sends one [`goal.updated`](/api/webhooks#events) event: a goal set, edited, paused, resumed or archived, or a reading that moved its pace or status. A daily reading that changes neither sends nothing, and a pace that flips more than once in a local day announces only the first flip. The event carries the pace state, never the numbers behind the band.

## What goals do not do yet

* **Google Analytics and Stripe cannot be read.** Their metrics are in the catalog so a goal can name them; every such goal reads as unmeasured with the reason.
* **Keyword-scoped Google goals cannot be set yet.** A goal that names keyword ids (`keyword_id`, or `keyword_ids`) is refused with *keyword tracking isn't set up for this workspace yet*. `google_keywords_top10` across every tracked keyword can be set.
* **No forecast.** The pace line is a straight line from where the goal started to its target. Attensira says where a number stands against it, never where the number will land.


# Google search
Source: https://docs.attensira.com/measure/google-search

What Attensira records from Google: Search Console clicks, impressions and positions kept day by day with a 16-month backfill, and daily rank tracking with the AI Overview on each results page.

Attensira keeps two records of how your site does in Google, beside what the AI answer engines say about you:

* **Search Console**: what Google counted for your own site. Clicks, impressions and average position, per page and per search query, for every day Google reports.
* **Rank tracking**: where your site ranks for a list of tracked Google searches, read once a day. Each reading also keeps who else is on the page and whether Google showed an AI Overview.

Both are stored day by day, so you can compare a page's Google numbers with its AI citations, and both have the same rules as every other number in Attensira: a number nobody measured is `—` with a reason, never `0`. See [Reading the numbers](/measure/reading-the-numbers).

## Connecting Search Console

Search Console is a [plugin](/agent/plugins). Connect **Google Search Console** and choose the property for this workspace's site. Until it is connected, nothing is pulled and every Google column reads `—` with the reason *Search Console is not connected for this workspace*, never a zero.

### The first pull: sixteen months of history

Google keeps sixteen months of Search Console data and drops the oldest day every day. A day that falls off the end cannot be read again by anyone.

So the first time Attensira finds the connection, it pulls the whole sixteen months, one day at a time, oldest first: the oldest days are the ones about to be lost. The pull is paced at up to 30 days an hour per workspace, so a full history takes most of a day to arrive. It saves its place after every day, so an interruption costs at most the day it was on.

Once a day is stored, Attensira keeps it. Your history keeps growing past Google's own sixteen months.

If you switch the connection to a different property, the pull starts again from sixteen months back. Days from the old property are not mixed into the new one: until the new pull reaches them, they are not counted.

### Every day after that

Google reports Search Console with a lag of a few days, and a day's first figures still change for a day or two after that. Once a day Attensira pulls the day three days back and pulls the two days before it again, which is where a provisional day settles into its final numbers. A re-pull replaces the day; it never adds to it.

Days missed while the connection was broken are filled in on later passes, oldest first, up to two weeks of them per pass.

The newest days of any window are usually not reported yet. They read as *not recorded*, not as days with no clicks.

### When the connection breaks

If Google stops answering for your property, two things happen:

* An **Inbox** item, *Reconnect Search Console*, explains what is not being recorded. Reconnecting closes it, and the missed days fill in on their own.
* The Search Console lines on [Loop health](/agent/loop-health) show the pass as failed, with the reason.

Reconnect promptly. A day that falls past Google's sixteen months while the connection is broken is gone.

## Page totals and query detail

Search Console data is stored at two grains, and they are not interchangeable.

| Grain | One row per | Complete? |
| - | - | - |
| **Page totals** | page × day | Exact. Every impression Google counted for the page, including impressions for queries Google anonymises |
| **Query detail** | search query × page × day | The top 5,000 rows of each day by impressions. Anonymised queries are not in it at all |

Google leaves anonymised queries (rare or personal searches) out of any report that lists queries. Adding up query detail by page therefore **under-counts** every page that gets them, often by a large share. That is why page totals are their own pull and are never computed from query detail. Every per-page number in Attensira, including the **Google position** and **Google clicks** columns on the [Pages](/traffic/pages) grid, comes from page totals.

Query detail is what you read when you ask about a search query. When a day had more rows than are kept, that day is marked truncated, and any number by query that includes it is a floor: the queries kept are the top ones, not all of them.

### Position

Position is Google's average position, weighted by impressions: the same figure Search Console shows for the same pages and days. A row Google reported without a position adds its clicks and impressions but nothing to the average. A page with no impressions in the window has no position, `—`, never 0 or first.

### Which days count

A window counts only the days Attensira holds a pull of. Within those days, a page Google did not show at all is a measured 0 clicks and 0 impressions. Outside them (before the backfill reached them, or newer than Google has reported) the days are *not recorded*, and an answer that touches them says so.

## Rank tracking

Rank tracking reads Google's results for a list of tracked searches once a day.

### Which searches are tracked

The list is seeded from Search Console: each day, the searches that brought your site at least 10 impressions over the last four weeks of stored days, most impressions first, fill up to 50 slots. A workspace tracks at most 100 searches. Without Search Console there is nothing to seed from, so rank tracking starts once Search Console is connected and reporting.

Each search runs as a desktop Google search in your workspace's country and its language. The countries with their own location today are the US, UK, Canada, Australia, New Zealand, Ireland, India, Germany, France, Spain, Italy, the Netherlands, Sweden and Estonia. A workspace in any other country is searched from the US, in English, and every reading records the location it was searched in.

### What one reading holds

* **Your position**: the best organic position of any page on your domain in the first 30 results. Subdomains count (`blog.example.com` counts for `example.com`); a lookalike domain does not. If you are not in the first 30, there is no position: `—`, never 0.
* **Your ranking page**: the URL that held that position.
* **The top ten**: the first ten organic results, with each one's domain, URL and title, so you can see who outranks you.
* **The AI Overview**: whether Google showed one, which pages it cited, and whether any of them is yours.

### When a reading fails

A search that fails, is refused, or has not come back within 24 hours is stored as **unavailable**, with the reason in words. It has no position and no AI Overview reading. A missing reading is never written as a position, and it is never read as "not ranking".

### AI Overview: present, absent or unknown

Whether Google showed an AI Overview has three values, because "there was none" and "we could not see one" are different findings.

| Value | Meaning |
| - | - |
| `true` | Google showed an AI Overview. Its cited pages are kept |
| `false` | We asked Google's results to load the AI Overview, and none came back |
| `unknown` | The reading cannot say. Every unavailable day is `unknown` |

The share of days with an AI Overview counts only `true` and `false` days. Days that are `unknown` are left out of the share, not counted as absent, and the answer says how many there were.

## Day basis

Google's numbers and the AI answer engines' numbers are counted on different calendars, and every answer from `query_evidence` names which one in `day_basis`:

| `day_basis` | Calendar | Used by |
| - | - | - |
| `slot_day` | Your workspace's own day, the local day a prompt is read on | Share of voice, citations, positions in answers, deciding sources, claims |
| `search_console` | Google's own report day. Google counts Search Console days in Pacific Time | `gsc_clicks`, `gsc_impressions`, `gsc_position` |
| `utc` | The UTC calendar day | Rank tracking, AI traffic |

The same date label on two bases does not cover the same 24 hours. Compare trends across them by week or by window, not day against day.

## Asking for these numbers

The agent, and you over [MCP](/mcp/tools#query_evidence) or the [REST API](/api/endpoints#post-v1evidencequery), read the stored history with `query_evidence`:

| Metric | Value | `n` is | Group by |
| - | - | - | - |
| `gsc_clicks` | Clicks, a count | Impressions | `day`, `week`, `page`, `keyword` |
| `gsc_impressions` | Impressions, a count | Impressions | `day`, `week`, `page`, `keyword` |
| `gsc_position` | Average position | Impressions that carried a position | `day`, `week`, `page`, `keyword` |
| `serp_position` | Average of your daily positions | Days you ranked | `day`, `week`, `keyword`, `page` |
| `ai_overview_presence` | Share of days with an AI Overview, 0 to 1 | Days with a known answer | `day`, `week`, `keyword`, `page` |

For the Search Console metrics, `keyword` is the search query and reads query detail; without it, totals come from page totals. For rank tracking, `keyword` is the tracked search and `page` is your ranking page. A `page` key is the URL without its scheme, a leading `www.` or a trailing slash (`acme.com/pricing`), so the spellings Google and your site use for one page land on one row. These metrics take no filters other than the window.

What could not be measured comes back in `gaps`, by reason:

| Reason | Meaning |
| - | - |
| `not_connected` | Search Console is not connected for this workspace |
| `not_recorded` | These days have not been pulled yet: Google has not reported them, or the backfill has not reached them |
| `truncated_day` | Query detail on this day kept only the top rows, so numbers by query are a floor |
| `truncated` | The question had more groups than one answer reads; the ones with the most impressions are kept |
| `no_impressions` | A Search Console position over no impression that carried one |
| `not_populated` | No tracked search was read in this window |
| `not_ranked` | You were not in the results read, so there is no position |
| `unavailable` | Rank-tracking days that could not be read |
| `ai_overview_unknown` | Days that could not say whether an AI Overview was shown, left out of the share |

The agent's own live Search Console look-up, through the plugin, reads at most 100 rows a call. The daily pull and the backfill are not something an agent runs: they are the stored history, and `query_evidence` is how an agent reads it.

## Related


  
    Every page across AI answers and Google, side by side.
  

  
    How a shipped change is re-read, and the labels its result reads in.
  

  
    Why `—` and `0` are different findings.
  

  
    Where a failed or blocked pull shows up.
  



# How we collect data
Source: https://docs.attensira.com/measure/how-we-collect-data

The full measurement methodology: which model surfaces are read, how often, how deeply we sample, and the significance test every delta has to pass.

Attensira sends your prompts to real model surfaces on a fixed daily cadence, records what comes back, and computes every rate over **runs** — one prompt, one model, one country, one point in time. This page documents that pipeline end to end, including where it is thin, so you can judge the numbers yourself.

## Which model surfaces do we read?

A **model** in Attensira is a model surface, addressed by a stable wire id called a channel: `chatgpt`, `perplexity`, `google_ai`, `claude`, `gemini`. The channel survives the provider swapping the underlying model beneath it, so your history stays continuous when a vendor ships a new version.

**We read the surface your buyers actually use, not the API behind it.** Where a consumer surface can be read, that is the primary path: we visit chatgpt.com, perplexity.ai, Google's AI Overview and Gemini the way a person would. The model APIs are a **labelled fallback**, used only where no consumer surface can be read.

This distinction is the difference between measuring the product and measuring a proxy for it. Asking a model through its API is not what someone sees when they type into chatgpt.com: different model version, different system prompt, different retrieval. A visibility number built on the API answers a question nobody asked.

Consumer-surface reading covers `chatgpt`, `perplexity`, `google_ai` and `gemini`. API readers exist for `chatgpt`, `perplexity`, `claude` and `gemini`. **`google_ai` has no API at all** — it is readable only through the consumer surface, which is precisely why AI Overview visibility was unmeasurable before.

Every run records which surface produced it, `consumer` or `api`, and the two are never averaged together. A window spanning both reports as `mixed` rather than silently blending them.

A new workspace tracks four channels by default: `chatgpt`, `perplexity`, `claude` and `google_ai`. On Growth and above you switch `gemini` on yourself in [Models](/tracking/models). A model you have not configured is never queried, and its metrics come back `tracked:false` rather than as a zero — see [Reading the numbers](/measure/reading-the-numbers).


  An **agent model** is a different concept: it is the LLM your [chats](/agent/chats) run on. It has nothing to do with the model surfaces measured here.


## Which country is a prompt asked from?

Every run records the country it was asked for: your workspace's country, or the one a topic, a prompt or a [market](/tracking/markets) sets. How the run is located there depends on the surface, and the two ways are not the same thing:

* **Consumer surfaces** (`chatgpt`, `perplexity`, `google_ai`, `gemini`) are read from a browser in that country, so the answer is the one a person there sees. This holds in the [42 countries that can be read locally](/tracking/markets#which-countries-can-be-read-locally); a workspace country outside them is still read, but its answers may not be local.
* **API reads**, which is every `claude` run and any API fallback, are not located anywhere. The country is stated at the end of the question, as `User location: DE`. That tells you how the model answers someone who says where they are. It is not a local search.

A Claude rate is therefore never a reading taken from a country, in your primary market or any other. In a secondary market, a run whose location was only stated is labelled that way and is never compared with a local one. A consumer surface that cannot be read from a secondary market's country is not read at all, and is shown as not measured rather than as zero. See [Markets](/tracking/markets#how-is-each-model-located-in-a-market) for the full table and the countries that can be read locally.

## How often does a prompt run?

An in-process ticker runs hourly and enqueues due work. That hourly tick is a scheduling heartbeat, not a measurement frequency. The rule that governs your data is the one below it:

**Each prompt × model × country combination runs at most once per day, on that prompt's own slot.**

A prompt's slot is not the calendar day rolling over at midnight — it is roughly twenty-four hours after the slot before it, opening at the time of day the prompt was first tracked, in your workspace's own zone. Two prompts added minutes apart can settle into different slots, and that is by design: every count in the product is keyed to a prompt's own slot, not to the clock, so what matters is that each prompt is read at most once per slot, not that every prompt in the workspace is read at the same moment. Twenty prompts across two models in one country still produce forty run-slots a day; they just do not all open together.

## How deep is the sample?

Sampling depth is the number of runs issued per prompt × model × country, per day. It is set by plan:

| Plan | Sampling depth per day | Issued |
| - | - | - |
| Starter | n = 1 | — |
| Growth | n = 3 | Concurrently |
| Business | n = 3 | Concurrently |

Growth, Business and Agency runs are issued **concurrently**, not spaced through the day. Sending them together means all three see the same model state, so the variation you observe is the model's own answer-to-answer variance rather than a change in the world between morning and evening.

Runs that fail — a timeout, a blocked request, a malformed response — are **stored but excluded from n**. They are kept for debugging and never enter a numerator or a denominator. An outage therefore shrinks your sample rather than depressing your rate.

## Why are rates computed over runs?

Every rate in the product uses runs as the unit, never readings, answers, sentences or keyword occurrences. A run either named you or it did not; it either cited your domain or it did not. An answer that names you nine times is one run in the numerator, exactly like an answer that names you once.

This is what makes rates comparable across prompts, models and windows: the denominator is always a count of questions actually asked and successfully answered.

## What is the significance gate?

Every delta Attensira shows has passed a **two-proportion z-test at 95% confidence**. The current window's rate and the prior window's rate are compared as two proportions with their own sample sizes. If the difference does not clear that bar, the delta comes back as `{real: false}` and the value is `null` — never a number, never zero.

**A window with fewer than two runs on either side is never a change.** One run against one run has no dispersion to measure: an answer that named you yesterday and did not today is a single answer changing its mind, not a hundred-point move. Both windows need at least two runs before a delta can be `real: true` at all.

`real: false` means one thing precisely: **no change we can prove.** It is not a claim that nothing moved. It is a refusal to present movement as a finding when the sample cannot distinguish it from noise.


  **At n = 1, most deltas will not clear the gate.** With one run per prompt per model per day, a seven-day window holds seven observations per cell, and a two-proportion test on seven-versus-seven observations only detects large swings. Expect many `real: false` results on Starter. That is the method working, not the product failing to measure.


Three ways to get more deltas that clear the gate, in order of effect:


  
    Aggregating fourteen or thirty days instead of seven multiplies the runs behind each rate without changing anything about your setup.
  

  
    Read a rate at the topic or model level rather than for one prompt on one model. Coarser cells pool more runs.
  

  
    Growth, Business and Agency sample three runs where Starter samples one, tripling the observations per day for the same prompts.
  


## What this method cannot tell you


  
    Attensira asks your prompts on a schedule. It does not see the questions real people asked, or how many of them there were. It measures how the models answer, not how often anyone asks.
  

  
    Coverage is limited to the channels listed above, in the countries you configure. A surface you do not track is not measured, and never reported as a zero. A configured country is a local reading only on the consumer surfaces; on Claude and the API fallbacks it is a location stated in the question.
  

  
    The same prompt can produce different answers minutes apart with nothing having changed. This is exactly why sampling depth and the significance gate exist, and why a single run is an observation rather than a fact about the model.
  

  
    Page fetches by AI crawlers are a separate dataset with separate mechanics. See [AI traffic](/traffic/ai-traffic).
  


Data retention follows your plan: 3 months on Starter, 12 on Growth, unlimited on Business. Handling of the data itself is covered by the [privacy policy](https://attensira.com/privacy) and [terms of service](https://attensira.com/tos).


# Page changes
Source: https://docs.attensira.com/measure/page-changes

How Attensira sees a change on one of your own pages, matches it to what we suggested or shipped, and measures the weeks before and after it, without ever saying the change caused anything.

Every read of one of your own pages is compared with the version we hold of it. When a read finds the page different, Attensira keeps the new version, records what changed, checks it against the suggestions and deliveries it has on file for that page, and books the same [before-and-after re-reads](/measure/before-and-after) a merged pull request gets. That covers a change we shipped, a change you made from our advice without telling us, and a change nobody suggested.

All of it is observation. Seeing a change never marks a work item shipped, approves anything or changes its state. A person confirms that, if they want it recorded.

## How often a page is read

The pages in your site's [inventory](/traffic/pages#how-does-attensira-know-which-pages-you-have) are read on a schedule:

* **Weekly.** A page is read again 7 days after its last read.
* **Daily while a suggestion is open.** A page that one of your open suggestions targets is read about once a day, until 30 days after the suggestion was filed. Open means the suggestion has not been declined. At most **25 pages** per workspace are on the daily read at once; when a new one would be the 26th, the watch that ends soonest goes back to weekly.

A daily read is an ordinary page read and is metered like one. See [Credits](/account/credits).

When the agent reads a page live during a chat or a run, that read is kept as a version too.

A page is read as the HTML your server returns. JavaScript is not run, so a title or a paragraph that only appears after scripts run is not seen, and a change to it is not seen either.

## What a version records

Each version keeps the fields a person edits on purpose:

| Field | What is kept |
| - | - |
| Title | The `` text, up to 300 characters |
| Meta description | The description, in either attribute order, up to 300 characters |
| Main headings and subheadings | The H1s and H2s, in page order, from the first 25 headings on the page |
| Canonical URL, robots directive | The canonical link and the robots meta tag |
| Social title and description | The Open Graph title and description |
| Structured data | The JSON-LD `@type` values the page declares |
| FAQ and list structure | How many FAQ questions it answers (from FAQ schema, then `<details>` blocks, then H2/H3 headings ending in "?"), and how many lists, list items and tables it has |
| Body text | The first 1,500 words of visible text, a fingerprint of all of it, and its full word count |
| Status | The HTTP status the page answered with |

A page that answers with an error (a 404, say) is recorded too: the version is its status. A page we could not reach at all is not recorded, because there was no answer to keep.

### Not captured is not absent

Versions taken before Attensira recorded a field cannot say what that field was. In a comparison, such a field is shown as **not captured**, never as removed, added or changed. The same goes for every field of a read that was not a 200: an error page has a status and nothing else to compare.

## What counts as a change

A read is compared with the **current version** of the page, not with the last read. A new version is recorded when:

* any of the fields above other than the body differs (title, description, H1, H2, canonical, robots, social title and description, structured data types, structure);
* the status differs, for example 200 to 404; or
* the body text differs from the current version by more than noise: more than 3 bits of 64 in a similarity fingerprint taken over the title, the description and the first 1,500 words.

What does not count:

* **Rotating tokens.** Words containing a digit (dates, counters, "12 people viewing", prices in a banner) and long hexadecimal strings (session ids, build hashes) are left out of the body comparison. A price that changes in the body text alone is therefore not a change; one that changes in the title is.
* **Small body drift.** A body edit too small to clear the threshold is the same version, seen again. Because every read is compared with the current version, several small edits that add up past the threshold are recorded as a change on the read that crosses it.
* **Reordering.** H1s, H2s and structured data types are compared as sets, so moving a subheading up the page is not a change.
* **A better reader.** When Attensira starts recording more about a page, the first read with the new reader is stored as a **recapture**. It is never treated as a change, and comparisons start from it.

### Reverts

A change whose fields are the same as an earlier version's, from the last 180 days, is a **revert**: the page went back. A page that returns to exactly the bytes it served before is matched to that earlier version too, so a revert is never lost.

A revert is listed as its own page change. When it goes back on a change that is still being measured, that change's outstanding re-reads are cancelled and the change is marked **reverted**, with the dates: "the change was reverted on the page between 2 Sep 2026 and 9 Sep 2026". There is nothing left on the page to measure.

## When a change happened: the seen window

A read only tells us what the page says now. So a change is dated by two reads:

* **After:** the last read that still saw the old version.
* **By:** the first read that saw the new one.

The edit happened somewhere in between. On the weekly read that window is about 7 days, longer when a read runs late; on the daily read it is usually one. Every sentence about a change is dated from these two reads: "first seen live on 9 Sep 2026", or "changed between 2 Sep 2026 and 9 Sep 2026". When the window is wider than **10 days**, the card says so: "We read this page rarely: the change happened somewhere in a window wider than 10 days."

## How a change is matched to a suggestion

A change is compared with the work items in this workspace that target the same page (its URL with or without `www` or a trailing slash). Each suggestion names a field, such as the title or the meta description; a pull request may move any field, since the file it changes can render any part of the page.

A suggestion filed after the read that first saw the change cannot explain it: the page was already that way.

| Match | When | What the card says |
| - | - | - |
| **Shipped by us** | A pull request, CMS draft or rewrite of ours went live no later than the read that saw the change, and the field it targets changed | "Our pull request for this page's title: merged 3 Sep 2026, first seen live 9 Sep 2026." |
| **Applied suggestion** | Nothing of ours was delivered, the suggestion came first, and the words we suggested are in that field on the live page | "We suggested this title on 3 Sep 2026. It was first seen live on 9 Sep 2026." |
| **Close to suggestion** | The field changed to words close to ours: of all the words in either, at least 60% are in both (title, description and main heading only) | "We suggested a new title on 3 Sep 2026. A similar title was first seen live on 9 Sep 2026." |
| **Field flagged** | The field we suggested changing changed, to other words | "We flagged this page's title on 3 Sep 2026. It changed on the page between 2 Sep 2026 and 9 Sep 2026." |
| **Draft published** | A page we had never read appeared, and its title or main heading is an article we drafted in the last 90 days (or it is the URL you gave when you marked the article shipped) | "The article we drafted on 3 Sep 2026 was first seen live at [https://example.com/blog/post](https://example.com/blog/post) on 9 Sep 2026." |
| **Unattributed** | Nothing on file matches | "This page's title changed between 2 Sep 2026 and 9 Sep 2026. We had not suggested it." |
| **Revert** | The page went back to an earlier version | "The title was changed back to the earlier version between 2 Sep 2026 and 9 Sep 2026." |

The strength of a match is shown with it, in three words: **exact** (our words, or our delivery, are on the page), **close**, and **field** (the field moved, to other words). Unattributed changes and reverts have none.

* **Exact** for a pull request or an article means the field's new words, at least three of them, are in the file we wrote. A file is never on the page word for word.
* **Timing.** A suggestion filed inside the seen window may have come after the edit. Only an exact match is kept then, and the card adds: "We cannot tell whether this happened before or after our suggestion."
* **Declined suggestions still match.** If you declined a suggestion and then made the change, it is still advice followed, and the card says when you declined it.
* **Several matches.** The strongest match wins, then a delivery of ours over advice, then the most recent suggestion. The others are kept as also matched.
* **New pages.** A page we had never read is filed only when it matches an article we drafted. A new page nothing explains is not a change.
* **Advice that was never filed.** A suggestion made only in a chat, never filed as a work item, cannot be matched.

## The before and after

A change is measured on tracked prompts, picked in this order:

1. the matched suggestion's own prompts;
2. else the prompts of every suggestion that targets the page;
3. else the prompts whose answers cited the page in the 28 days before the last read that still saw the old version, most cited first.

At most **25 prompts**. When none apply, nothing is measured and the card says "no tracked prompt targets or cites this page".

The re-reads are the usual three, at **+7, +14 and +30 days** from the read that first saw the change. Each compares two windows of the prompts' daily reading slots:

* **Before:** the 7 slot days ending the day before the last read that still saw the old version. The days of the seen window, when the change may or may not have been live, belong to neither side.
* **After:** the 7 slot days centred on the +N date.

A re-read booked this way does not check the page for our words again: the read that saw the change is the evidence that it is live. A change that is reverted before its re-reads run has them cancelled.

**Nothing is measured twice.** When the matched suggestion already has re-reads from a merge or from being marked shipped, the page change uses those. When a change matched its suggestion exactly or closely (or was our delivery) and the crawl booked the re-reads first, marking the suggestion shipped later books nothing new. A **field flagged** change is measured as a page change only, not as the suggestion's result: the words on the page are not the ones we suggested.

### One line for the page, decided once

Each re-read has its own verdict per prompt and engine, exactly as on [Before and after](/measure/before-and-after). When the +30 re-read resolves, its answers are summed across every prompt and engine into one before and one after, the same significance test is run once on the sums, and the result is stored as the change's **pooled** line. It is never recomputed when you look at it again.

### Sample-size floors

| Shown | Only when |
| - | - |
| The pooled line as two rates | At least **30** successful answers in each window, and a before window read on at least **5** slot days |
| One engine's row | At least **10** successful answers in each window |

Below a floor the card shows the counts and no percentage: "Too early to say (n=108 before, n=112 after)."

### The labels

Every change carries one of three labels:

| Label | Means |
| - | - |
| **moved** | The pooled line cleared the significance test, above the floors |
| **too early** | The +30 re-read has not happened yet, the floors are not met, or there was too little before the change to compare against |
| **nothing we can prove yet** | The windows do not differ past the test, or the change was reverted, or there was nothing to measure |

Only **moved** gets the measurement sentence, and it puts the two rates side by side:

> In the 7 days around 16 Sep 2026, this page's 4 prompts were named in 31% of answers (n=112), against 12% (n=108) in the 7 days before it changed.

## Why we never say "because"

A re-read shows that the numbers differ between two windows. It cannot rule out everything else that changed in the same weeks: a model update, a competitor's new page, another edit on your site. So the change and the measurement are always two separate sentences, and no sentence about a page change uses "because", "after we shipped", "since we", "thanks to", "led to", "drove", "resulted in" or "as a result". Every template is tested against that list.

If two changes on one page fall in the same weeks, each is measured on its own, over the same answers. Neither can be separated from the other.

## Citations and AI-crawler fetches

The card also shows two descriptive counts. Neither carries a verdict.

* **Citations:** how often answers to the change's prompts cited the page in the re-read's before and after windows, beside how many answers each window had.
* **AI-crawler fetches:** how often AI crawlers fetched the page in the days before the change and the same number of days since it was first seen (up to 7), with a split by provider since first seen. These need the [AI traffic](/traffic/ai-traffic) snippet. Without it the card says fetches are not counted. When the before window is older than the AI-traffic history held for your site, the before count is left out rather than shown as 0.

## Where you see page changes

* **Pages → Changes.** Open a page in [Pages](/traffic/pages) and choose the **Changes** tab: every version we hold, when each change was first seen and what moved, reverts marked, and a field-by-field comparison of any two versions you pick.
* **Work.** One row per workspace, **Page changes on your site**, raised when a change is matched to something of ours and when a change's pooled line is decided. Later events update the same row rather than adding rows. A change nobody suggested, or a revert, does not raise it when it is first seen. The evidence card also appears under the suggestion it matched.
* **Mark shipped from the row.** When our suggested words are on the page from a copy handoff nobody has marked shipped, the row offers **Mark shipped**, which opens the item where you confirm it. See [Mark shipped](/agent/inbox#mark-shipped).
* **The morning brief.** Up to **three** lines under "Changes we saw on your pages:", moved first, for changes first seen or decided in the brief's window (the day before; the week before on Monday). See [Morning brief](/agent/morning-brief).
* **Client reports.** Up to **ten** changes matched to our work in the period, worded as the card words them. Unattributed changes stay on the card. See [Client reports](/agencies/client-reports).

The dashboard reads these from `GET /v2/projects/{id}/pages/versions?url=`, `GET /v2/projects/{id}/pages/diff?url=&from=&to=`, `GET /v2/projects/{id}/page-changes` (filters `state` = `open`, `measured` or `reverted`, `attribution`, `cursor`, `limit` up to 100) and `GET /v2/projects/{id}/page-changes/{change_id}`. These answer a signed-in session only; page changes are not on the REST API or the MCP server yet.

## The agent's page\_changes tool

The agent has a free, read-only tool, `page_changes`, that compares two versions of one of your own pages field by field. With only `url` it compares the current version with the one it replaced; `from` and `to` take any two version ids from the list it returns, in either order. A field one version never read comes back as `not_captured`, with the reason, and the agent is told to say so rather than guess. The URL must be on your workspace's own domain.

It sits in the agent's **Your own data** tool group next to [`page_history`](/agent/deep-read#page_history), which lists a page's versions one after another.


# Prompt state
Source: https://docs.attensira.com/measure/prompt-state

Where you stand on a prompt, in one word per engine and one word overall: who had the answer, read from the latest successful answers, with a dash where nothing was measured.

A rate tells you how often you were named. It does not tell you who had the answer when you were not, and "named in 0% of answers" reads the same whether a rival took every answer or nobody was named at all. Those are different jobs. **State** is the word for that difference: one per engine (the **cell**), and one for the prompt across its engines.

State is folded from the same answers as the rates beside it, so a cell and the percentage next to it never come from two different samples.

## Cell state: one prompt on one engine

A cell is read from the **latest reading in the window that has at least one successful answer** on that engine. Failed answers do not count; a reading where every answer failed is skipped and the one before it is used. With `n` successful answers in that reading:

| State | Shown as | When |
| - | - | - |
| **Named and linked** | ● | You are named in at least half of the answers, **and** your domain is cited in at least half of them |
| **Named** | ◐ | You are named in at least half of the answers |
| **Lost** | ✕ | You are not, and one tracked competitor is named in at least half of them, in the answer's own words |
| **Absent** | ○ | Otherwise: nobody you track is named often enough to hold the answer |
| **Not measured** | — | No successful answer on this engine in the window |

The rules are checked in that order, and the first that holds wins.

* **At least half, not at least one.** A cell is a label on `n` answers. At sampling depth 3, a label that one answer in three could flip would move inside the sampling noise. Ties go to you: named in 1 of 2 answers is **Named**.
* **Lost needs a rival in the words.** A competitor counts only when the answer names it in its text. A competitor that appears only among the links is a source, not a name the answer chose. A lost cell says which rival, the one named in the most answers (ties broken alphabetically), and when the reading was taken.
* **Absent is a measurement.** It means the answers were read and nobody you track was named in enough of them. **Not measured** means there was nothing to read. The grid draws them differently, and never one in place of the other.

A cell that is not measured always says why, on hover:

| Reason | Means |
| - | - |
| not read in this window | The prompt is tracked on this engine and has no reading in the window |
| every read in this window failed | Readings exist and every answer failed. The commonest reason we can show you follows it |
| no answers came back in this window | Readings exist with no answers in them at all |
| this prompt is not read on this engine | Another prompt is read on this engine; this one is not |

## Prompt state: one prompt across its engines

The prompt's state is folded from its cells. Only measured cells count: let **M** be the number of engines with a measured cell.

| State | When |
| - | - |
| **Won** | Named (or named and linked) on at least half of the M engines, rounded up, **and** named and linked on at least one |
| **Contested** | Named on at least one engine, but not enough to have won it |
| **Lost** | Not named on any engine, and lost on at least one |
| **Absent** | Measured on at least one engine, and every measured engine is absent: you are named on none and lost on none |
| **Not measured** | M is 0: no engine has a successful reading in the window. Shown as — |

The precedence is won, then contested, then lost, then absent. A prompt named on one engine and lost on two is **Contested**, not lost: you are in the answer somewhere.

An engine that is not measured is left out of M. It neither helps nor hurts the prompt, because counting it would turn a gap in our coverage into a claim about your brand. A prompt read on four engines, where one failed for the whole window, is judged on the other three.

The board also counts the engines a prompt is **lost on**. That number sorts the losses first, and it is what the analytics overview's "Where are we losing" block ranks by.

## Where you see it

* **Prompts**: the **Standing** column is the prompt state, and each engine column is that engine's cell. You can filter and group by standing; grouped, the groups run worst first (lost, absent, contested, won), with not measured last because "never read" is not a low score. A group's summary counts its prompts in each state, for example "3 lost · 1 contested · 2 won".
* **The prompt page**: each engine shows its state with the `n` it was read over and when, from the same rule, so the page and the board never disagree.
* **CSV export**: a `state` and `lost_on` column per prompt, and a `<engine>_state` and `<engine>_state_n` column per engine. An empty cell is not measured, which is not absent.
* **Over MCP**: `query_evidence` counts prompts by state (`prompt_state_count`) and filters any metric by `state`. See [MCP tools](/mcp/tools).

## Reading it

State is a word, not a trend. It describes the latest successful answers in the window, so it moves as soon as one reading moves, and a single reading at sampling depth 1 can only ever be all or nothing. Read the `n` beside a cell before you act on it, and use the rates and their [significance test](/measure/reading-the-numbers#why-does-a-change-say-real) for whether anything changed.

Next: [Share of voice](/measure/share-of-voice) for the rate behind **Named**, and [Competitors](/tracking/competitors) for the rivals a **Lost** cell names.


# Reading the numbers
Source: https://docs.attensira.com/measure/reading-the-numbers

How to tell a measured zero from an unmeasured cell, why a rate carries a sample count, and when a change is real.

Every number Attensira reports carries the evidence behind it. A rate arrives with the number of observations it was built from, a change arrives with whether it cleared a significance test, and anything we never measured comes back as `null` rather than as a zero. This page explains how to read all three, in the dashboard and over [MCP](/mcp/overview).

The three rules, in the words the dashboard uses:

* **`0` and `—` are different facts.** A dash means not measured. A 0 means we read the answers and the count was zero.
* **Every rate carries its n.** A percentage never appears without the number of answers behind it.
* **"Nothing we can prove yet"** is what a change says when the sample cannot tell it apart from noise.

## Why is a rate two values instead of one?

A rate is `{value, n}`. `value` is the rate itself, between 0 and 1. `n` is how many successful runs it was built from.

```json theme={null}
{ "value": 0.31, "n": 84 }
```

Read `n` first. A rate of 0.33 built from three runs and a rate of 0.33 built from three hundred are the same number describing very different amounts of evidence. The first will move dramatically on the next reading; the second will not. Small samples move in large steps, and a jump from 0 to 0.33 at `n` of 3 is one answer changing its mind, not a trend.

## Where is n in the dashboard?

Beside the rate. The analytics screens print the count with every rate they show:

| Where | How the rate is written |
| - | - |
| Prompts grid, each rate cell | The percentage, with the count under it: `25%` over `1 of 4 answers`. Below 20 answers the count ends in `· thin`. |
| Prompts grid, share of voice | The same, counted in brand mentions rather than answers: `40%` over `2 of 5 mentions`. |
| Prompts grid, `n` column | The answers read for that prompt in the window. |
| Prompts grid, group headers | `n = 12 prompts`, then each rate as `31% of 214 answers`, or `not read` when nothing in the group was read. |
| Overview headline | The percentage from 20 answers up, and below 20 the count instead: `3 of 12`. Either way `n=… answers` sits beside it. |

Below 20 answers a percentage claims a precision the sample does not have. "0 of 4" says everything "0%" says, and carries its own evidence, which is why the Overview switches to the count and the grid marks the count thin. The 20-answer line is a display rule, not a statistical test; the significance test for changes is described below.

## What does null mean?

`null` means **not measured**. It never means zero.

| What you see | What it means |
| - | - |
| `{"value": 0.24, "n": 96}` | Measured. You were named in 24% of runs. |
| `{"value": 0, "n": 96}` | Measured. The models were asked 96 times and never named you. |
| `{"value": null, "n": 0}` | Not measured. Nothing was read in this window. |

The difference matters because the two failures have opposite remedies. A measured zero is a product problem — the models know your category and do not know you. An unmeasured cell is a configuration or coverage problem, and the number tells you nothing about your visibility at all.

<Warning>
  Never render an unmeasured cell as 0%. Reporting `null` as zero invents a failure that was never observed. The dashboard shows an em dash for these, and the API returns `null` so your own tooling can make the same distinction.
</Warning>

## How does the dashboard show a zero and a dash?

On every screen, an unmeasured value is an em dash (—) and a measured zero is a `0` or `0%`. On the Overview, hover a dash, or the n beside the headline figure, and the dashboard says:

> A dash means not measured: no answer was read for it. A 0 means we read the answers and the count was zero.

In the prompts grid a dash is never bare. Each one carries the reason it is a dash, on hover and for screen readers. The reason tells you which gap you are looking at, because the remedies differ:

| Reason on the dash | What it means |
| - | - |
| not read in this window | The prompt is tracked on this model, and no reading was taken in the window you are looking at. |
| every read in this window failed | Readings were attempted and every one failed. The commonest failure reason follows it when there is one. |
| no answers came back in this window | Readings exist but carry no answers at all. |
| this prompt is not read on this engine | Other prompts are read on this model; this one is not. |
| Intent is not classified yet. | The prompt has no intent label yet. This is a missing attribute, not a measurement. |

The prompts grid applies the same rule to standing: `○ Absent` means we read the answers and nobody you track was named, and `— Not measured` means there was nothing to read. See [where you stand on a prompt](/tracking/prompts#where-do-you-stand-on-a-prompt).

The CSV exports from the prompts, pages, sources and competitors grids keep the distinction too. Each file states, in its header, that an empty value is not measured and a 0 is a measured zero. Every rate column has an `_n` column beside it, so you can tell which of the two an empty cell is.

## Why does a change say "real"?

A change is `{value, real}`.

```json theme={null}
{ "value": 0.06, "real": true }
```

`value` is the movement against the immediately preceding window of equal length. `real` says whether that movement cleared a two-proportion z-test at 95% confidence.

When `real` is `false`, `value` is `null`. That is deliberate. Models are not deterministic, and a rate built from a modest sample wanders on its own without anything changing in the world. Rather than draw a line through that noise, Attensira reports no proven change.

So `{"value": null, "real": false}` means **"nothing we can prove yet"**, not "no movement" and not "zero". It is the honest answer to a question the sample cannot yet settle.

## Where does "nothing we can prove yet" appear?

Wherever a change would otherwise be stated:

* **The Overview's trend sentence.** It always includes n, and ends either "a real change" or "nothing we can prove yet". For example: "Named in 31% of answers, up from 26% (n=1,840), a real change." or "Named in 31% of answers (n=214), nothing we can prove yet." A change that clears the significance test is still "nothing we can prove yet" when fewer than 70% of the window's days were read, and the sentence says how many were, for example "read on 4 of 28 days". Two thin samples weeks apart are not a trend. When nothing was read at all, it says "No answers were read in this window yet (n=0), so there is nothing we can prove yet."
* **The prompts grid's Trend column.** A prompt's movement is shown in points (`+6 pts`) only when it cleared the test. Otherwise the cell reads `not proven`, and its hover says: "No change this sample can prove against the window before — nothing we can prove yet."
* **Re-reads after a change ships.** Where a screen sums up a change's re-reads in one word, such as the Overview's "What did we do, and did it work?" block, the **No change** and **Too close to call** verdicts both read "Nothing we can prove yet": two different findings, the same honest sentence. On the Overview, a change with too few answers from before it to compare against reads the same, and so does one that moved up on some prompts and down on as many others. See [Before and after](/measure/before-and-after) for the full verdicts.

None of these say why a number moved. A real change means the number moved by more than the noise. It does not mean a particular piece of work moved it.

<Note>
  At the Starter sampling depth of one run per prompt per model per day, most movements will not clear the gate. That is the test working, not the product failing. [How we collect data](/measure/how-we-collect-data) explains sampling depth, and [Plans](/account/plans) shows which depth each plan runs at.
</Note>

## Why was a finding held as noise?

The hourly [detection](/agent/findings) pass applies the same idea to what it files in your Inbox. A rule that fires on three quiet days is not yet proof that anything changed, so before a finding is filed its last three days are compared with the seven before them, on successful reads only.

* **Significant at p \< 0.01** on Fisher's exact test: it is filed. That bar is stricter than the 95% test above, because a filed finding asks for your attention.
* **Not significant:** a [decision](/agent/decisions#noise-or-a-real-change) reads the same counts, day by day, and judges whether the change is real or noise. It reads numbers only.
* **Held as noise:** the decision was confident the change is not real. The finding is not filed and starts no run, and it is checked again an hour later on fresh readings.

Each hold is recorded with its `n`: the successful reads, across both windows, it was judged on. As everywhere else, `n` is the first thing to read. A held finding at an `n` of 12 is a handful of answers that wandered. A dip of the same size at an `n` of 300 is far more likely to clear the test, and a change that clears it is filed without a question.

## What do tracked and readable mean?

Per-model metrics carry two booleans that both look like a failure and are not.

**`tracked: false`** — this model is not configured for this workspace, so it was never asked. There is no visibility number because no question was put to it. Reporting it as 0% would invent a failure. Add the model in [Models](/tracking/models) if you want it measured.

**`readable: false`** — no reader exists for that surface, so it cannot produce a reading at all. This is a property of the model, not of your workspace or your plan, and adding the model will not change it.

Both are different again from `{"value": 0, "n": 40}`, which means the model *was* asked forty times and did not name you. Only the last of the three is a result.

## What is the unit behind every rate?

A **run**: one prompt, sent to one model, for one country, at one moment. Not a reading, not an answer, not an occurrence of your name.

Every rate on the site flattens all the runs in the window into a single pool and divides. A prompt read for thirty days at a sampling depth of three contributes ninety runs to that pool, weighted the same as any other ninety. Runs that failed are stored for debugging but excluded from `n`, so a model outage shrinks your sample rather than depressing your rate.

## A dash, a zero, and an empty list

The same rule holds for everything that is not a rate:

| What you see | What it means |
| - | - |
| **—** (`null` on the wire) | Not measured, or not known. Nothing was read, or the reading that would answer it has not happened yet |
| **0** | Measured, and the count or rate was zero |
| An empty list (`[]`) | Looked, and found none |

A dash never stands alone. Wherever the product shows one, it says why on hover or in place: "not read in this window", "not scored yet", "recorded before we kept the full brand order". A dash without a reason asks you to guess which of several very different gaps it was.

Lists follow the same split. An answer's [brand order](/measure/share-of-voice#the-full-brand-order) is `null` until the answer has been read for it and `[]` once it has been read and named no brand. A link's [context](/measure/citations-and-sources#what-was-each-link-cited-for) is `null` when we never looked and `""` when we looked and the answer's text does not mention the link. A [stance](/measure/sentiment) is `unscored` when nobody has read the answer for it and `none` when it was read and takes no position. In each pair, only the second is a reading.

## Gaps

A table that is missing rows is not the same as a table whose rows are zero. So when `query_evidence` answers a question it cannot fully measure, it lists every hole as a **gap**: a `key` saying where the hole is, and a `reason` saying why.

```json theme={null}
"gaps": [
  { "key": "2026-08-01..2026-08-09", "reason": "before_first_reading" },
  { "key": "engine=gemini", "reason": "no_readings" }
]
```

The key is a source (`search_console`), a run of days (`2026-08-01..2026-08-09`), one day (`day:2026-08-04`), or one row's key (`engine=gemini`). Consecutive missing days are one gap, not one per day. The reasons include days before your first tracked prompt, a source that is not connected, days not recorded yet, a value not read yet, and a day that was cut short. An empty `gaps` list is the claim that nothing is missing.

Read the gaps before you read the rows. A 0 next to a gap is not a measurement of that gap.

## Truncated days and truncated tables

Two different things can be cut short, and both are said.

* **A truncated day.** Where a source keeps only the top rows of a day, as Search Console's detail by search query does, that day's numbers for a single query are a floor, not a total. The day carries a `truncated_day` gap. A page's own totals are not affected: they are stored whole, not added up from the queries.
* **A truncated table.** When an answer has more rows than its `limit`, `truncated` is `true` and the rows are the strongest, or the newest days or weeks when grouped by time. A count shown from a truncated read is "at least N", never N.

## Reading these over MCP

The shapes are identical through the [MCP server](/mcp/overview) — `get_analytics` returns the same `{value, n}` and `{value, real}` objects, and the same `tracked` and `readable` flags per model.

If you are building on top of the API, carry the distinction through rather than flattening it. Coercing `null` to `0` at the edge of your system is the single most common way an accurate measurement becomes a wrong report.

## What do the verdicts on a shipped change mean?

The [analytics overview](/measure/analytics-overview) and a prompt's changes timeline judge each shipped change in one of four words: **Moved up**, **Moved down**, **Nothing we can prove yet** and **Too early**. None of them says the change caused the movement. [The verdict words](/measure/analytics-overview#the-verdict-words) gives each one's meaning, the text shown on hover, and how a change's re-reads decide which word it gets.


# Sentiment
Source: https://docs.attensira.com/measure/sentiment

How answers talk about your brand where they take a position at all. An answer that names you without judging you is left out of the score, never counted as neutral.

Sentiment answers one question: when an answer has an opinion about your brand, is it a good one? It does not answer "how do answers feel about us on average", because most answers that name a brand hold no opinion about it, and scoring those would bury every real opinion under a pile of price quotes and feature lists.

## The one rule: no stance is not neutral

Every successful answer is read for its **stance** on your brand. The stance is one of three things, or nothing:

| Stance | Means | In the score? |
| - | - | - |
| **Positive**, **Neutral** or **Negative** | The answer takes a position on your brand: praise, criticism, a recommendation, a reservation, a balanced verdict | Yes |
| **No clear stance** | The answer was read and judges nothing: a price, a feature, a bare entry in a list | No |
| **Not scored yet** | Nobody has read the answer for its stance yet | No |

**Neutral** and **No clear stance** look alike and mean opposite things. "Ledgerly is one of several reasonable options" is a neutral verdict and counts. "Ledgerly starts at \$12 per seat" names you and judges nothing; it is left out. Counting it as neutral would move your score whenever the mix of questions you track moved, which is not sentiment.

**Not scored yet** is different again: it is the absence of a reading, not a reading. A new answer is not scored yet until it has been read for its stance, which happens after it is stored, never while it is being sampled.

## How it is calculated

Every number on the Sentiment page divides by **scored answers**: the answers with a stance. Never by answers read.

```text theme={null}
positive share = answers with a positive stance
               ÷ answers with any stance (scored)
```

The neutral and negative shares are the same over the same denominator, so the three sum to 100% of the scored answers.

The **sentiment score** is the average stance of the scored answers on a scale from **-1 to +1**. Above +0.15 reads as mostly positive; below -0.15, mostly negative. A score of 0 is a real reading: the opinions cancelled out. A score of `null`, shown as —, means no answer in the window took a position, and there is nothing to average.

The page shows both numbers beside each other, with their counts: the scored answers, the answers read, and the answers not scored yet. When the scored count is small, read it before the score. Five scored answers out of three hundred read is five opinions, not a verdict on your brand.

## The Sentiment page

**Analytics → Sentiment** shows, for the window:

* the score and the positive, neutral and negative shares, with the scored count;
* the positive share's change against the previous window of the same length, only when it clears the [significance test](/measure/reading-the-numbers#why-does-a-change-say-real);
* the score by day and the positive share by day, as two separate charts. A day with no scored answer is a gap in the line, not a zero;
* one row per answer engine and per topic, each dividing by its **own** scored count;
* the scored answers themselves, each with the sentence that carries the stance, so you can check the score against the words.

Filtering by engine or topic recomputes every number for that cut. Filtering by stance only narrows the list of answers: the shares stay what they are, because a share filtered down to itself would always read 100%.

### Flagging a score

If a stance looks wrong, flag the answer. A flag is a note that somebody on the workspace disagrees with the score. It is an annotation, never an edit: it changes no number on the page.

## Per-brand stance in the brand order

Each answer's [brand order](/measure/share-of-voice#the-full-brand-order) lists every brand it names. Each brand in it carries its own stance under the same rule: a brand the answer names without judging has no stance, not a neutral one.

On the prompt page, your own entry in the brand order shows the answer's stance on you: Positive, Neutral, Negative, or **No clear stance**. A competitor's entry shows a dash with "not scored yet": the Sentiment page and the prompt page do not score competitors.

Over MCP and the REST API, `answer_history` and `answer_slice` rows carry the stance on you as `stance` (`positive`, `neutral`, `negative`, `none` or `unscored`), and the brand order as `brands`, where each brand has its own `stance_label` and `stance_score`. A brand with no stance has no `stance_label` and a `stance_score` of `null`. A `stance_score` of `0` is a balanced verdict.

## Filtering answers by sentiment

`answer_slice` takes a `sentiment` term over your brand's stance. It keeps the same distinction:

* `positive`, `neutral` or `negative`: answers with that stance;
* `none`: answers that were scored and took no stance;
* `unscored`: answers nobody has scored yet.

`none` and `unscored` are never folded into `neutral`, or into each other. See [MCP tools](/mcp/tools#answer_slice).

## What it covers

Stance is read for answers from the last 90 days. An answer older than that, read before stance scoring existed, stays not scored yet and is not in any score.

The stance is read from the answer's text by a model. It can be wrong, and that is why every scored answer is listed with its sentence and can be flagged. Like every other reading, the text is stored verbatim, so when the scoring improves it is re-run over the stored answers in that window rather than starting a new series.


# Share of voice
Source: https://docs.attensira.com/measure/share-of-voice

How often a model names your brand in its answer, measured as successful runs naming you divided by all successful runs in the window.

Share of voice is the fraction of successful runs in which a model named your brand in its answer text. It is the count of successful runs where your brand was named, divided by every successful run in the window. Nothing else enters the calculation: not link counts, not answer length, not position.

<Warning>
  Share of voice here is a **rate, not a slice of a pie**. It answers "in what fraction of answers were you named?" — never "what fraction of all brand mentions were yours?" It does not sum to 100% across brands. Most tools in this category use the name for the pie version; we do not, for the reason in [Why ours does not sum to 100%](#why-ours-does-not-sum-to-100) below.
</Warning>

## What counts?

A mention is your brand name appearing in the answer the model wrote. No link is required. A model can describe you at length, recommend you, and never attach a URL — that still counts. A link to your domain is a separate thing entirely, counted as a [citation](/measure/citation-rate).

The unit of measurement is a **run**: one prompt, sent to one model, for one country, at one point in time. Not a reading, not an answer paragraph, not a keyword occurrence. If your name appears eleven times in one answer, that run contributes exactly one mention, the same as an answer that names you once.

## How is it calculated?

```text theme={null}
share of voice = successful runs naming your brand
               ÷ all successful runs in the window
```

Failed runs are stored for debugging but excluded from the denominator, so a model outage lowers your sample size rather than your rate. See [How we collect data](/measure/how-we-collect-data) for what makes a run succeed.

## Does sampling more often change my number?

No, and this is deliberate. Reading the same prompt three times instead of once triples the runs behind the number — and roughly triples the URLs those answers carried — but both the numerator and the denominator grow together, so the rate holds and only `n` grows. Sampling deeper buys you confidence, not a different number.

The same property is what makes windows comparable. A window read at greater depth than the one before it is not "up"; the [significance gate](/measure/how-we-collect-data) compares two rates using both sample sizes, so a change has to clear the noise floor for the `n` actually involved.

Two things do scale with sampling depth, and they are counts rather than rates: the number of citations collected, and the number of answers read. Read those as evidence volume, never as movement.

## Why ours does not sum to 100%

Competitor share of voice uses the **same denominator** — all successful runs in the window. That has a consequence worth internalising: competitor rates do not sum to 1. Several brands can be named in one answer, or none can. A window where you sit at 40% and three competitors sit at 55%, 30% and 20% is perfectly ordinary and describes reality accurately.

We could compute the version that does sum to 1 — your mentions divided by every tracked brand's mentions. We deliberately do not, because its denominator would be **the competitor list you typed**. Add a competitor and your headline number falls; remove one and it rises. Nothing about your visibility would have changed. A number that moves when you edit a settings page is not a measurement, and we would rather publish one that only moves when the models do.

This means our share of voice is not comparable to a number under the same name from a tool that computes the pie version. Ours will generally look larger, because it is answering a different question.

## How do I read the number?

Every rate arrives as `{value, n}`. `value` is the rate; `n` is the number of successful runs behind it. A `value` of `null` means the cell was never measured — that is not zero. A `value` of `0` with `n` above zero is a real, measured zero: the model was asked and did not name you.

Read `n` before you read `value`. At a sampling depth of one run per prompt per model per day, a single week gives you seven runs per prompt-model pair, and a rate built on seven runs moves in large steps. [Reading the numbers](/measure/reading-the-numbers) covers small samples, deltas and unmeasured cells in detail.

## Where did you come in the answer?

Being named is the rate. Where you were named is a separate reading, and Attensira keeps two versions of it on purpose.

### Your position: among the names you track

Your **position** is where your brand first appears in an answer, counting only the names you track: you and your tracked competitors. 1 means no tracked name came before yours. It is always shown with its base, as in "#2 of 5 tracked names", where the base is you plus every tracked competitor that answer also named.

This is the position every board, chart and average reports, and it stays that way. It is measured from the answer's text against a list you control, so it is the same reading every time the answer is read. The base is what keeps it honest: "#2" alone reads as second in the answer, and it means second among the names you asked us to look for. Track one more competitor and an answer that named it before you moves you from #1 of 2 to #2 of 3, with nothing about the answer changed.

An answer that did not name you has no position, never position 0, and is left out of the average.

### The full brand order

Each answer also carries its **brand order**: every brand it names, tracked or not, in the order it first names them. It is read from the answer's text after the answer is stored, and the order is fixed by where each name first appears in the text, not by the model that reads it. A name the text does not contain is dropped, and a brand is linked to a domain only when it matches you, a tracked competitor or a site the answer cited.

On the prompt page, every answer you open from the day-by-day strip lists its brand order with each brand's [stance](/measure/sentiment#per-brand-stance-in-the-brand-order) and marks which one is you. Over MCP and the REST API it is the `brands` list on `answer_history` and `answer_slice` rows: each brand with `name`, `domain`, `position`, `tracked`, `is_self` and, for a tracked rival, `competitor_domain`.

Your place in the brand order can be further down than your position, because untracked brands count in it. That is why the brand order is shown per answer, as evidence, and the boards keep your position among tracked names.

A competitor's average place, where a rival tends to come when it is named, is read from the brand order, because there is no tracked-name position for a rival. It is reported by `query_evidence` as `competitor_position`, and it is `null` until at least one answer naming that rival has been read for its brand order.

Answers from the last 90 days have a brand order. An answer older than that, read before the brand order was kept, shows a dash and "recorded before we kept the full brand order", never an empty order: it may well have named five brands. A brand order of `[]` is a reading: the answer named no brand at all.

## What this measure gets wrong

Detecting a mention means finding your brand name in the answer text, as a whole word. That handles the ordinary cases — possessives, hyphenation, your domain inside a URL — and it will not count "architecture" as a mention of a brand called Arc.

It cannot yet tell a brand from its own name used as an ordinary word. If your brand is called Linear, an answer explaining that something "scales in linear time" is counted as a mention. The error runs one way — brands whose names are common words read **high**, never low — and it is worth knowing about when you read your own number. We store every answer verbatim, so when the detection improves we can re-run it over history rather than starting a new series.

If your brand name is a common word, open a few answers on the [prompt detail](/measure/reading-the-numbers) view and read them before you trust the rate.


# How to win one answer
Source: https://docs.attensira.com/measure/win-this-answer

The win plan for a single prompt: per model, what decided the answer, which rung you are on, the one next action, and when we read it again.

[Who decides an answer](/measure/deciding-sources) tells you which pages the models leaned on. The **win plan** turns that into one thing to do — per model, with the evidence under it and the date we will find out whether it worked.

## One action per model, never a list

A plan with five actions per model is a plan nobody works. So each model gets:

* **Up to five deciding sources**, each with its share, its **n**, and whether you are on it already.
* **A rung**: `proven` (you are cited among the pages that decide this answer), `better` (you are cited and a competitor sits above you), or `new` (nothing of yours is there).
* **One next action**: fix a page, get listed, pitch a listing, answer a thread, or write something cite-able that does not exist yet — with the page or domain to work on and the citations it rests on.
* **A credits estimate** for that action. It is an estimate; the meter on the run is what you are actually charged.

The models are never averaged. The move that wins a prompt on ChatGPT is not the move that wins the same prompt on Perplexity, and one blended plan would fit neither.

Where a model was read on two surfaces — a consumer answer and an API answer — the plan takes the surface with the most answers behind it and says which one it took, rather than averaging two different products.

## Where the rung comes from

The same rule the [deciding-sources map](/measure/deciding-sources) follows: the static per-engine table until your own workspace has read at least 20 successful answers on that source, and your own history after that. Every rung says which of the two it came from.

## What the plan will not say

* **No forecast.** No predicted rank, no predicted lift, no percentage that has not been measured.
* **No movement language.** Nothing "improved" or "climbed": until a re-read comes back **moved**, there is no proven change, and the plan ends on that sentence in so many words. Once the workspace has a moved verdict, the footer may use movement wording.
* **No number without its n.** The plan states the evidence rung it is entitled to — `measured` at 7 or more answers, `published` between 2 and 6, `hypothesis` at one.

## When we read it again

Every plan ends with when the answer gets re-read. The date is the day the next booked [re-read](/measure/before-and-after) for that prompt is actually read, a few days after its +7, +14 or +30 day mark. If nothing has booked one yet, it says **"after the first action lands"** rather than naming a date nothing is going to keep.

## Where to find it

* On the **prompt page**, as the first section, **How to win this answer**. See [Prompts](/tracking/prompts#the-prompt-page).
* The [Daily win plan](/agent/autopilot) works from the same plan every morning.
* In **chat**, whenever you ask how to win a prompt — the agent follows the same procedure and shows you the sections per model.
* As the [MCP tool](/mcp/tools) **`win_plan`**. It takes a required `prompt` — a prompt id or the prompt's exact text — and an optional `days` window. Ask for a prompt you are losing; [`deciding_sources`](/measure/deciding-sources) says which those are.
* Over REST it is [`GET /v1/win-plan`](/api/endpoints#get-v1win-plan), with the same payload as `win_plan`.

<Note>
  The plan is composed over the deciding-sources map rather than recomputed from the readings, so the map, the page and the tool can never disagree about which page decided an answer.
</Note>


# Limitations
Source: https://docs.attensira.com/reference/limitations

What Attensira does not measure or promise, where its methods are approximate, and how to read numbers accordingly.

Every measurement system has edges, and so does every agency. This page is the honest account of ours: what we promise and what we don't, how detection actually works, where noise lives, and what we cannot tell you. Read it before you make a decision on an Attensira number, and read it again before you present one to someone else.

## We promise the work, not the outcome

Your budget commits us to a volume of work each month, and work we don't deliver isn't charged. That is the whole promise. We do not promise a ranking, a citation, a mention, traffic or revenue: no one controls what an assistant or a search engine says, and a change that is never published cannot work. See [Plans](/account/plans#what-a-budget-does-not-buy).

The commitment has edges of its own:

* **Only some work is counted.** Articles, page fixes and outreach pitches are committed and counted. Listing fixes are not counted yet, so nothing is committed for them, and monitoring is not a deliverable. On [Costs](/account/costs#by-kind-of-work) these read **not tracked yet**, which never means zero delivered.
* **Work waiting on you counts as delivered.** A pull request you have not merged, or an article you have not pasted in, is finished on our side. It is never a shortfall, and it cannot move a number until it is live.
* **Some work only you can do.** No person on our side does client work, so where an action needs you (merging a pull request, sending the first pitches from your own mailbox, connecting an integration), it waits in the [Inbox](/agent/inbox) until you do it.

## Not every surface is measured yet

Signup offers every place you might want to be found: the assistants, Google, YouTube, Reddit and review sites. The agents work on all of the ones you choose, but not all of them can be measured for every workspace. A surface with no reader, or one your plan does not include, says **not measured yet** rather than showing a number. See [Models with no reader](#models-with-no-reader-are-unmeasured-not-zero) below.

## How a mention is actually detected

A mention is a **case-insensitive whole-word match** of your brand name, or a **host-aware match** of your domain, against the text of a model's answer. That is the whole method — there is no entity resolution and no disambiguation against a knowledge graph.

For your **name**, boundaries are any non-letter, non-digit character, so possessives ("Acme's"), hyphenation ("Acme-shaped") and punctuation all count as named.

For your **domain**, the rule is tighter, because ordinary word boundaries would hand other people's hostnames to you. A label cannot be extended on either side: `fake-acme.com`, `acme.community` and `acme.com.evil.test` are three different owners and none of them counts as a mention of `acme.com`. Your own subdomains do count — `www.acme.com` and `blog.acme.com` are yours — and so does a domain inside a URL (`acme.com/pricing`) or at the end of a sentence ("we recommend acme.com.").

The boundary rules remove one class of error. A brand called Arc is no longer matched by "architecture", "march" or "search"; a brand called Notion is no longer matched by "notional".

It does not remove the harder one. A brand whose name is also an ordinary word is still counted when that word is used in its ordinary sense: for a brand called Linear, an answer explaining that something "scales in linear time" reads as a mention. Separating those needs a judgement about what the answer is discussing, not a longer search string. **The error runs one way — such a brand reads high, never low.**

Everything built on mentions inherits this. Your share of voice inherits it. Every competitor rate you compare against inherits it. If a competitor's name is a common word and yours is not, the comparison is unfair in their favour, and vice versa.

We store the **verbatim answer text** for every successful run precisely because of this. The stored answer is the evidence: you can read what the model actually said, and detection can be re-run over the same history when the method improves, without losing the past.

<Tip>
  If your brand name is a common English word, check a handful of stored answers before trusting your rate. Two minutes of reading tells you whether your number is measuring your brand or the dictionary.
</Tip>

## Models are not deterministic

Ask the same model the same prompt twice and you can get two different answers. Ask it an hour later and you can get a third. This is a property of the models, not a fault in our collection.

Sampling is the response. We put a prompt to a model more than once per reading where your plan allows it — n=1 on Starter, n=3 on Growth, Business and Agency — so that a rate is computed from several draws rather than one. Gating deltas is the second response: a change has to clear a statistical bar before we will call it a change at all.

## Why a small sample shows "no proven change"

Every delta is tested with a two-proportion z-test at 95% significance. If the movement does not clear that bar, you get `{real: false, value: null}` — which means **we cannot prove a change**, and never means the change was zero.

At n=1, this will be the common outcome. One draw per prompt per model per day is a thin sample, so most week-to-week wobble sits under the noise floor and is reported as no proven change. That is the system working: it is refusing to sell you a story that the data does not support.

If you need movements confirmed faster, sampling depth is the lever — n=3 gives three draws per reading. See [Plans](/account/plans).

## null is not zero, and small n is not a rate

Two shapes carry the numbers. A rate is `{value, n}`. A delta is `{value, real}`.

* `value: null` means **not measured**. Nothing was observed, so nothing can be reported.
* `value: 0` with `n > 0` means **measured zero**. Runs happened; none named you.

These are different facts and they must not be collapsed into one chart line. A dashboard that draws null as zero is telling you a model rejected you when in truth it was never asked.

Small `n` deserves the same care. A rate of 33% at n=3 is one hit out of three draws; a single different answer moves it to 0% or 67%. Look at `n` before you quote the percentage, and prefer wider windows over single days.

## Models with no reader are unmeasured, not zero

Per-model metrics carry `tracked` and `readable`.

* `tracked: false` — this model is not configured for your workspace, so it was **never queried**. Rendering that as 0% invents a failure that did not happen.
* `readable: false` — no reader exists for that surface, so we cannot collect it at all.

Not every model surface has an API reader. `google_ai`, in particular, has no API reader — it is read through the consumer surface instead, so answers do arrive, thinner and less regularly than on the API-backed channels. Two other things narrow what you see. A new workspace starts with four model surfaces turned on, not five: Gemini is switched on by hand, so an empty Gemini was most likely never switched on. And `gemini` is gated to Growth, Business and Agency: on Starter it is never queried, whatever the setting says. See [Models](/tracking/models) and [Plans](/account/plans).

## Competitor rates do not sum to 100%

Competitor rates use the **same denominator as yours**: successful runs where that competitor was named, over all successful runs in the window. They are independent rates, not slices of a pie.

One answer can name you and three competitors, so four rates each rise from the same run. Adding them up produces a number that can exceed 100% and means nothing. There is no share-of-voice metric in Attensira that divides a fixed total between brands.

<Note>
  The `share_of_voice` field carries the rate defined above — runs naming the brand over all successful runs. Most tools in this category use that name for the pie version instead, so a number under the same name from another tool is not comparable to this one.
</Note>

## Citation position is not a rank of your brand

`AvgPosition` is the mean index of a URL within the model's own citation list, computed **per source domain**. It answers "when this domain is cited, how far down the list does it sit". It is not a leaderboard position, not a ranking of your brand against competitors, and not comparable to a search engine result position.

There is also no visibility score in Attensira. "Visibility" appears as a label on a section of the product and as a value in the `get_analytics` area enum; it is not a computed metric.

Your brand's own position is a different number: where you first appear in an answer, counted only among the names you track, and shown with that base ("#2 of 5 tracked names"). Track one more competitor and it can move without the answer changing. See [Share of voice](/measure/share-of-voice#where-did-you-come-in-the-answer).

## Sentiment rests on fewer answers than were read

[Sentiment](/measure/sentiment) is measured only where an answer takes a position on your brand. Most answers that name a brand judge nothing, so the score usually rests on far fewer answers than were read; the page shows both counts. Competitors are not scored on the Sentiment page. It is a separate reading: share of voice counts a mention the same whatever the answer said about you.

## A country is not always a local search

Only the consumer surfaces (ChatGPT, Perplexity, Google AI and Gemini) are read from a browser in the country you track, and only in the [countries that can be read locally](/tracking/markets#which-countries-can-be-read-locally). **Claude is never read from a location.** It has no consumer reader, so its answers come from the API with the country stated in the question (`User location: DE`). The same holds for any run that fell back to an API. Such a rate says how the model answers someone who says where they are, not what a search from there returns.

In a [secondary market](/tracking/markets), these runs are labelled "location stated in the question" and never compared with a local reading, and a consumer surface that cannot be read from the country is shown as not measured rather than as zero.

## What we cannot attribute

We measure two things: what models answer, and which pages AI crawlers fetch. We do not measure what happened next.

Attensira cannot tell you that a mention produced a visit, that a citation produced a signup, or that any of it produced revenue. There is no identity join between an answer a model gave someone and a person who later arrived at your site. Anyone claiming that link for this class of product is inferring it, not observing it.

Treat these numbers as a measure of presence in AI answers. Connect them to commercial outcomes with your own analytics and your own judgement, and say out loud that you are doing so.

## Freshness: today is always partial

Work is enqueued on an hourly ticker, but each prompt × model × country combination runs **at most once per workspace-local calendar day**. Today's numbers are therefore incomplete until the day closes, and the shape of that incompleteness is not uniform — some prompts will have run, others not.

Failed runs are stored but excluded from `n`, so a day with collection problems produces a smaller sample rather than a distorted rate. Compare whole days against whole days. A "drop" measured at eleven in the morning is usually the morning.

For how to read a number once you have it, see [Reading the numbers](/measure/reading-the-numbers). For specific symptoms, see [Troubleshooting](/reference/troubleshooting).


# Troubleshooting
Source: https://docs.attensira.com/reference/troubleshooting

Answers to the questions people ask when an Attensira number looks wrong or missing.

Most surprising numbers in Attensira have an ordinary explanation. This page covers the five that come up most, in the order people usually hit them.

## Why is my share of voice zero?

Check first whether it is a measured zero or an unmeasured null. A measured zero has `n > 0`: runs completed and none named your brand. A null has no runs behind it and means nothing was collected. They look similar on a chart and mean opposite things.

If it is a measured zero, the models genuinely are not naming you for those prompts. That is a real finding, and usually the most useful one you will get in your first weeks. Look at which prompts produced it: broad category prompts ("best project management tool") behave very differently from prompts that already contain your name.

If it is a genuine zero but you expect otherwise, read a few stored answers. The verbatim answer text is kept for every successful run, and it will show you whether the model named competitors instead, refused the question, or answered something adjacent to what you meant to ask.

Also confirm your brand name and domain are entered as models would write them. Detection is a whole-word match, so a brand recorded with punctuation or a trailing suffix that models never use will match nothing. [Limitations](/reference/limitations) explains exactly how the match works.

## Why did my number not change?

Because the change did not clear the significance gate. Every delta runs a two-proportion z-test at 95%. If the movement sits under the noise floor, you get `{real: false, value: null}` — "no change we can prove". That is not a bug and it is not a zero; it is the system declining to report noise as signal.

This is most common at n=1, the sampling depth on Starter. One draw per prompt per model per day is a thin sample, and models are not deterministic, so ordinary run-to-run variation swamps small real movements. Widening the comparison window gives the test more runs to work with and is the cheapest fix.

If you routinely need faster confirmation of small movements, sampling depth is the lever: Growth, Business and Agency sample at n=3. See [Plans](/account/plans) for what that changes.

Also check you are not comparing a partial day against a whole one. Readings run at most once per workspace-local day, so today is incomplete until it closes. [Reading the numbers](/measure/reading-the-numbers) covers window choice.

## Why does one model show no data at all?

Almost always because it was never queried. Per-model metrics carry two flags. `tracked: false` means that model is not configured for your workspace, so no request was ever made. `readable: false` means no reader exists for that surface, so we cannot collect it regardless of your settings.

A new workspace starts with four model surfaces tracked (ChatGPT, Perplexity, Claude and Google AI), not all five. Gemini, on Growth and above, is untracked until you switch it on. Turn on the surfaces you care about and data starts arriving on the next day's run.

The `readable: false` case is different and you cannot fix it from settings: `google_ai` has no API reader, for example. Neither flag means the model rejected you. Reporting an untracked model as 0% would invent a failure that never happened, so we report it as unmeasured instead. See [Limitations](/reference/limitations).

## Why don't I see data yet?

Give it a full workspace-local day. An hourly ticker enqueues work, but each prompt × model × country combination runs **at most once per calendar day** in your workspace's timezone. A workspace set up this afternoon has its first meaningful comparison tomorrow, not in an hour.

Before that first day closes, partial data is expected: some prompts will have run and others will not, so early rates are computed from whatever landed. Do not read trends into the first twenty-four hours.

Check the ordinary setup causes too. Prompts must exist and be tracked, at least one model surface must be turned on, and your brand and domain must be set — without them there is nothing to match answers against. If your credit balance is at zero, agent-driven actions such as adding prompts will have been refused rather than partially completed; see [Credits](/account/credits).

Failed runs are stored but excluded from `n`, so a collection problem shows up as a smaller sample rather than a wrong rate. A day with unexpectedly low `n` is worth a look.

## Why did a prompt stop being tracked?

The two usual causes are that someone removed it, or that your workspace is at its prompt slot limit. Slots are a fixed stock — 50 on Starter, 150 on Growth, 350 on Business, 750 on Agency — and adding a prompt when the stock is full will not succeed until a slot is freed.

Removal is immediate and stops future readings for that prompt. History already collected remains available inside your plan's retention window: 3 months on Starter, 12 on Growth, unlimited on Business. Once a prompt's data ages past that window, it is gone, and re-adding the prompt starts a fresh series rather than restoring the old one.

Prompts can also be removed over the API. `remove_prompt` is a destructive tool available to any read-and-write key, and it does not ask for confirmation. If a prompt disappeared without anyone in your team removing it in the app, audit your keys in Settings > Your assistant and revoke any you cannot account for. See [API keys](/account/api-keys).

Re-adding is straightforward once a slot is free, but expect a gap in the series where the prompt was untracked. That gap is unmeasured, not zero.


# Concepts and entity model
Source: https://docs.attensira.com/start/concepts

Every word Attensira uses, what it means, and the wire field an MCP user actually sees.

Attensira uses a small vocabulary, and each word means exactly one thing. It comes in two layers: the words for the work the agents do for you, and the words for what we measure. This page defines every term, then maps the customer-facing words onto the field names you will meet in the API and through MCP.

## The work

**Chief of staff** — the one agent you talk to. It answers you, proposes goals and projects, hands work to the right crew, makes sure it gets done, and reports back. It does not do the work itself. See [How we work](/start/how-we-work).

**Project** — an area of work inside a workspace, with a crew of agents that keeps working on it: your blog, your site's technical health, the comparison questions. It has a short name, a focus, a feed of what happened and why, and optionally a goal, a deadline and a monthly cap. See [Projects](/agent/projects) and [Project and workspace](#project-and-workspace) below.

**Crew** — the agents staffed on a project (Chief of staff, Researcher, Writer, Web engineer and others), each with what wakes it. You describe the work; the Chief of staff staffs it. You never define an agent.

**Deliverable** — a finished piece of work: an article, a page, a fix, a social post, a listing fix, product copy or an image. Deliverables land in your own systems and are listed on the **Deliverables** page, with where each one lives and what it did. Articles, page fixes and outreach pitches are what a [budget](/account/plans#your-monthly-budget) commits us to deliver.

**Goal** — an objectively measurable target: a metric, where it is read from, a baseline, a target and a date. Each goal is read against a pace line and is **ahead**, **on pace**, **behind**, or **unmeasured** when its source cannot be read. The first goal is usually to be seen at all: named in a number of your prompts, on the assistants you chose. A goal reports where a number stands; it never claims the number moved because of us. Goals are under **Analytics → Goals**.

**Inbox** — where the agents put what needs you. Every item that asks something of you is one of three kinds:

* **Approve**: a yes or no on something the agents made. A draft can be edited before you approve it, and a decline asks why.
* **Answer**: a fact only you have, given in the row.
* **Do**: something only you can do, such as connecting an integration or sending a drafted email from your mailbox, then marked done.

Work already done on its own, or approved and moving, is shown too but asks nothing. The Inbox button in the top bar carries a count of what is waiting on you, and Home lists the top three under **Needs you**; both show the same number. See [the Inbox](/agent/inbox).

**Budget** — the monthly ceiling on what the agents' work may be charged, shown as the work it buys ("≈ 4 articles · 12 page fixes · 20 outreach pitches · daily monitoring"). It is separate from the plan fee, and from the plan's monthly response budget. See [Plans](/account/plans#your-monthly-budget).

## The tracked objects

**Prompt** — one question, phrased the way a buyer would phrase it. Your plan sets how many you can track at once: 50, 150, 350 or 750 slots.

**Topic** — a named group of related prompts, so you can read a whole buying question as one line instead of twenty.

**Model** — a model surface Attensira asks, identified by a stable channel id: `chatgpt`, `perplexity`, `google_ai`, `claude`, `gemini`. The channel is deliberately stable, so when the vendor swaps the underlying model behind that surface, your history stays continuous.

**Workspace** — the container for prompts, competitors, models, automations, and data. In the dashboard a workspace is called a **brand**: the brand switcher at the top of the sidebar picks which one every screen is about, and **Add a brand** creates another. A credential names your organisation and one workspace as its default; every MCP tool and REST endpoint takes an optional `project` to name another one in the same organisation.

**Answer** — one stored response from one Model to one Prompt on one day. Answers are the raw material every metric is folded from.

## What we read out of an answer

**Mention** — the answer named your brand in its text, with no link attached.

**Citation** — the model attached one URL, and that URL points at your own domain.

**Source** — a domain bucket across citations. Sources cover every domain the models cite for your prompts, not only yours, so you can see which sites the answers lean on.

Mentions and citations are counted independently. An answer can do either, both, or neither.

## The metrics

<Note>
  Every rate is `{value, n}`. `n` is the number of **successful** runs behind it; failed runs are stored but excluded. `value: null` means not measured. `value: 0` with `n > 0` is a real, measured zero.
</Note>

**Share of voice** — successful runs naming your brand, over all successful runs.

**Citation rate** — successful runs citing your own domain, over all successful runs.

**Competitor rates** — the same calculation for a competitor, over the same denominator. They do not sum to 1, and they were never meant to.

**Source share** — citations to a given domain over all citations in the window.

**Average position** — the mean index of a URL inside the model's citation list, calculated **per source domain**. It describes where a domain tends to sit in a citation list. It is not a rank of your brand against competitors.

**Position** — where your brand first appears in an answer, counted among the names you track, always with its base: "#2 of 5 tracked names". See [Share of voice](/measure/share-of-voice#where-did-you-come-in-the-answer).

**Sentiment** — the stance of the answers that take a position on your brand, over those answers only. An answer that names you without judging you is left out, never counted as neutral. See [Sentiment](/measure/sentiment).

**State** — one word per prompt per engine, and one per prompt, for who had the answer: won, contested, lost or absent. See [Prompt state](/measure/prompt-state).

Deltas are `{value, real}` and pass a two-proportion z-test at 95%. Below the noise floor you get `{real: false, value: null}`: no change we can prove, which is not the same as no change.

Per-model figures carry `tracked` and `readable`. `tracked: false` means the model is not configured for this workspace and was never asked — reporting it as 0% invents a failure. `readable: false` means no reader exists for that surface.

<Warning>
  There is no visibility metric and no ranking of brands. "Visibility" is a label in the interface, not a number. Position counts where you came among the names you track in one answer, and sentiment scores only answers that take a position; neither orders brands against each other. Share of voice here is a rate — runs naming you over all runs — not a slice of a total, and it does not sum to 1 across brands. The `share_of_voice` field carries exactly that rate. Sentiment never changes share of voice, where a mention counts the same whatever it says.
</Warning>

## The machinery underneath

**Credit** — the unit work is metered in: one credit is one cent at list price. Adding prompts, running an automation, and asking the agent all spend credits. Reading numbers you already have does not. See [Credits](/account/credits).

**Automation** — a saved piece of recurring work an agent performs on its own. A project's crew works through automations: one for each thing that wakes a crew member.

**Chat** — a one-off conversation with the Chief of staff about your workspace. Ongoing work belongs in a project.

**Agent model** — the LLM your chat runs on. Attensira picks it for each turn; there is no model picker. This is a different thing from a **Model**: an agent model answers you, a Model is a surface Attensira measures.

<Warning>
  Credits and the response budget are currently inconsistent in the product. Treat credit arithmetic as approximate until that is fixed, and check your account before planning around a specific balance.
</Warning>

## Project and workspace

The two words are easy to mix up, and the wire makes it worse, so here is the rule.

A **workspace** is everything Attensira knows and does for one site: its prompts, competitors, models, connections, data, chats and automations, and its credit limits. Most organisations have one; an agency has one per client.

A **project** lives inside one workspace and is one piece of ongoing work in it, such as "win the comparison questions" or "keep the docs cited". It reads the workspace's data and works under the workspace's limits; it adds a crew, a feed, a focus and, optionally, a goal, a deadline, a monthly cap and an approval mode that can be stricter than the workspace's. A workspace can have many projects, or none.

On the wire, `project` has always meant the **workspace**: it is the `project` argument every MCP tool and REST endpoint takes, and `project_id` in identifiers. A project in the sense above is a `work_area_id`.

## Words to wire fields

The wire format kept its original names, and this is deliberate: renaming fields would break every existing integration, so the product vocabulary changed and the contract did not. When the docs and the JSON disagree on a name, both are correct.

| Customer word | Wire field | Appears in |
| - | - | - |
| Prompt | `keyword_id` | `get_prompt`, analytics rows, automation payloads |
| Topic | `group_id` | analytics rows, `get_analytics` with `area: "topic"` |
| Platform | `platform` | analytics rows, per-platform breakdowns |
| Workspace | `project_id` | internal identifiers; never a tool argument |
| Project | `work_area_id` | any payload that refers to a project |
| Inbox kind | `ask_kind` | Inbox rows, `list_inbox`: `approve`, `answer`, `do`, or `info` for a row that asks nothing |

The same holds for enum values. The `get_analytics` `area` enum accepts `visibility`, `prompt`, `page`, `source`, `competitor`, `topic`, `platform`, and `bot`. Those strings are the contract; pass them exactly as written. The older words `group` and `engine` were retired along with `channel` and are no longer accepted.

A **platform** is a stable identifier for an AI answer surface — ChatGPT, Perplexity, Google AI Overviews, Claude, Gemini. It is deliberately not the name of a model: the key survives the underlying model being upgraded, replaced or renamed, so two windows a year apart stay comparable.

## Crawlers are not models

An AI crawler fetches your pages. A model answers prompts. Attensira reports both, and they never mix: a crawler visit is not a reading, and a mention is not a pageview. See [AI traffic](/traffic/ai-traffic) for what the crawler side does and does not tell you.


# Your first fifteen minutes
Source: https://docs.attensira.com/start/first-fifteen-minutes

Give your domain, correct what the agents found, see what a free scan found, then choose a plan and a budget. Real work lands within fifteen minutes of paying, or you are told why.

You give one thing: your website. The agents read it and propose the rest, and you confirm or correct what they found. Nothing is charged before checkout, and you see what a free scan found before you are asked for a card. Everything is set up before you pay: your business, competitors, platforms, goals and the scan's first verdict are all on screen before the plan step asks for a card, and each step can be corrected in place. If you arrived from the [free audit](/start/free-audit), the questions it asked are kept word for word.

## Before you pay

<Steps>
  <Step title="Your domain">
    The domain is asked once. If you came from the landing page or the [free audit](/start/free-audit), it is already filled in; otherwise the sign-up page asks for it. Google sign-in is the main way in, and email works too.

    Creating the workspace starts the set-up on our side, in this order: the agents read your site into a drafted business profile, choose your starter prompts, and start the scan. You do not start any of it yourself. "Change domain" takes you back to the field; the old workspace is removed only once the new one has been created, so if creating it fails, your old workspace and its set-up are left as they were.
  </Step>

  <Step title="Your business">
    Each fact the agents found on your site arrives with the page it came from, such as "from /pricing". Edit any fact in place, and what you type is saved as confirmed. Anything your site did not say is asked as a question instead of being guessed. If your site cannot be read, the step says why and offers **Retry**.
  </Step>

  <Step title="Who you compete with">
    Suggestions come from who appears in the scan's answers, such as "Named in 11 of 150 answers", then from domains cited beside yours, then from rivals named on your site. While the scan is still reading, the counts say "so far" and the step says how many answers are still to come.

    Every suggestion starts ticked. Untick the ones you do not lose deals to and add missing ones by their website. When the agents are unsure whether a rival is in your market, they ask you about that one, and never about more than one. See [Competitors](/tracking/competitors#how-are-competitors-suggested-at-signup).
  </Step>

  <Step title="Where you want to be found">
    ChatGPT, Perplexity, Google AI Overviews and AI Mode, Gemini, Claude, Copilot, Google Search, YouTube, Reddit and review sites. The ones that make sense for you are already on, each with one line on why, worked out from what the scan's answers named and cited where it can be.

    A surface we cannot measure for this workspace yet is still offered and still worked on. It says "Not measured yet" and never shows a number. YouTube, Reddit and review sites are measured through the citations in AI answers. At least one surface must stay on. Your choice is saved now and applied when your plan starts.
  </Step>

  <Step title="Goals and prompts">
    The first goal is always to become visible: named in more of the answers on each assistant you measure, with a target, a deadline 90 days out and a pace line. A target is never set below where the scan found you. Other goals follow when they apply: being recommended against the rivals you kept, ranking in Google once Search Console is connected, and a business number once Google Analytics is connected. The business number is shown beside the work and is never credited to it.

    The prompts behind those goals are your starter set: about fifty, grouped into four to eight topics and collapsed. Open them to read or change them, or change them later from chat or MCP. See [Topics](/tracking/topics#the-starter-set). Goals are created when your plan starts.
  </Step>

  <Step title="What your scan found">
    The verdict, before the plan. See [the scan](#the-scan) below.
  </Step>

  <Step title="Your plan and budget">
    Starter, plus a monthly work budget shown as the work it buys. The step says the top-up rule in one sentence and you agree to that sentence before checkout. See [Plans](/account/plans#your-monthly-budget).
  </Step>
</Steps>

## The scan

The scan is free and runs before you hold a plan. It asks every starter prompt once (`n = 1`) on ChatGPT, Perplexity and Claude, in your primary market: at most 150 answers.

You see one row per assistant, each with its count, such as "12 of 50 read". Once half the answers are in, or twenty minutes have passed, the step writes the verdict: how many answers named you, out of how many, per assistant, such as "Named in 3 of 48". It says "so far" until the scan finishes. There is no comparison with anything; it is day one.

* A read that failed after retrying says "could not be read". It is never counted as a zero.
* An assistant you chose that the scan does not read (Google AI Overviews, Gemini or Copilot) says "Not measured yet" and has no number. Google AI Overviews is outside the free scan only: once your plan starts, Starter reads it with the others.
* You can go on to the plan at any time. The scan keeps reading on our side.

The free scan is limited per site and per account. If your site or account had one recently, the step says "Your scan starts when you pick a plan" and shows no number. When many scans are running at once, yours waits in line and starts on its own.

## After you pay

The screen reads back what is being watched (your domain, how many prompts and competitors) and the budget rule as it now stands, with the same numbers **Settings → Billing** shows. If the payment provider's confirmation takes a moment to reach us, the screen says it is waiting rather than sending you back to set-up.

### Watching it work

The moment your subscription is confirmed, every prompt is read on every engine your plan includes, and you watch it happen: one row per engine, updated every three seconds, each saying how many of its reads are in. If you pay on the same day (UTC) as your scan, the scan's answers count toward these readings and are not asked again; only what the scan did not cover is read. A read that failed after retrying says "could not be read yet". It is never counted as a zero.

Once at least half the reads are in (or twenty minutes have passed), you get the verdict, per engine, with the sample size in the sentence. See [Your first reading](/start/first-reading).

### The first thing to do

The [set-up run](/start/set-up-run) starts on its own. If your scan reached its floor in the last 48 hours, it starts the moment you pay; otherwise it starts when your first readings reach theirs.

When it files its first work, "First fix is ready" appears on the first item and you get an email. The first thing to do is a draft pull request if a repository is connected, or a before-and-after [rewrite](/agent/inbox#rewrites) with a shareable link and a one-click "request access from my developer" if not. GitHub is offered only when a fix is ready to become a pull request. Nothing is merged for you: pull requests open as drafts, and auto-merge is off unless you turn it on.

### Fifteen minutes, or the reason

Fifteen minutes after you pay, we check what the set-up run has filed. Real work in your [Inbox](/agent/inbox) is the goal. When it is not there yet, the screen says why, and the Inbox gets a row only when you have to do something:

| What we find | What you see |
| - | - |
| Work is filed | It is in your Inbox. |
| The run is still working | A line on the screen says what it is on. No Inbox row. |
| The run is waiting for your answer | A line on the screen points you to its question in your Inbox. |
| Your first readings are still coming in | A line on the screen says the run starts as soon as enough have landed. |
| The run needs credits, or your site could not be read | One **blocked** row in your Inbox saying what to do. |
| The run stopped without filing anything | One row in your Inbox that lets you start it again. We are alerted too. |

### Your team

Under the work is **Your team**: the [projects](/agent/projects) the Chief of staff proposes from what the set-up run finds on your site, each with the crew on it. Each one is yours to keep, pause or remove; the Chief of staff runs the rest. Projects appear here, and in the sidebar, as they start. Until then the section says **No projects yet**.

The kickoff call is offered here on every plan, whatever the team looks like. Book it when it suits you: nothing waits on it.

The last line is the only promise the screen makes: tomorrow at 07:00 you get your first [brief](/agent/morning-brief), if there is something to show.

## After that: Home

From then on, signing in opens [Home](/start/today#home). It asks "What should we work on?" above the box you hand work to, and under it, **Needs you** lists the three things that most need your answer, approvable in place. How many are waiting is the same number as the badge on the Inbox button in the top bar.


# Your first reading
Source: https://docs.attensira.com/start/first-reading

What happens the moment your subscription goes active: a dedicated pass reads every tracked prompt right away, and files what it finds in your Inbox.

Paying for Attensira starts a read, not a wait. The instant your subscription becomes active, every prompt you are tracking is read on every model your plan includes — on a dedicated fast lane that runs once, separately from the daily schedule that takes over afterward.

## What you see while it runs

The onboarding screen shows this pass live: one row per prompt on each model, resolving from waiting to read as the results come back. Nothing here is a placeholder — every row is a real, individually queued read, and the screen is watching the same record the API does.

Not every read lands cleanly. A model can time out, refuse a request, or return something we cannot parse. Reads like this are retried, and a row that could not be read after retrying **says so** rather than sitting there forever or quietly counting as a zero. "3 could not be read yet" is a fact about that row, not an estimate.

## When the setup pass reports in

You do not wait for every last row. The setup pass reaches a floor and reports what it has as soon as **at least half the reads are in**, or after **twenty minutes**, whichever comes first — a customer who paid a minute ago is owed an answer, even a partial one. What it finds is filed in your [Inbox](/agent/inbox), so the first thing this subscription does is hand you something to look at, not a spinner.

A full setup pass like this — reading every tracked prompt across the whole plan at once — runs **at most once a day per workspace**. Re-activating a subscription you had just turned on again the same day does not queue a second one.

## After that

Once the setup pass has run its course, your prompts move onto the same daily schedule as every other workspace: each prompt × model × country combination is read once per workspace-local day, at the sampling depth your plan sets. See [How we collect data](/measure/how-we-collect-data) for the full mechanics of that ongoing cadence.


# The free audit
Source: https://docs.attensira.com/start/free-audit

What attensira.com/audit reads, what it shows everyone, and what one email address unlocks.

The free audit at [attensira.com/audit](https://attensira.com/audit) is the trial. Enter a domain and it asks the same four engines a Starter workspace tracks (ChatGPT, Perplexity, Claude and Google AI Overview) eight buying questions about your category, and tells you who got named.

## What it reads

Eight questions are proposed from your site. At least three are implementation-shaped ("how do I…", "X vs Y") because those are the questions buyers ask when they are close to choosing. You can edit, delete or add questions before the run, up to eight. Each engine answers each question once, so every number on the report carries its n.

## What everyone sees

The verdict is public: per engine, how many of the eight questions named you and how many named a competitor instead. When you are named in most, the report leads with that. Competitor statements appear only as the model produced them; anything we cannot show safely is withheld and says so, and every report carries a takedown link.

Reports are not listed anywhere and carry `noindex`. There is no directory of audited domains.

## What one email unlocks

One email field opens two things:

* **The deciding-sources map.** For each losing question and each engine, the pages and domains that decided the answer, whether you are on them, and the next move.
* **One rewrite of one of your own pages.** We take the question you lost most and the page that should have answered it (one of your pages the assistants already cited, or your homepage), read it at that moment, and write one passage of it beside a replacement. Two checks run in code, the same two the paid product runs on every change: the passage must be on the page exactly as we read it, or the rewrite is refused and the report says so; and the change is classified **safe** (touches nothing you could be held to) or **claim** (touches a number, price, promise, product or competitor name), labelled that way so you read it before publishing. Code samples are never rewritten.

Nothing is changed on your site. You copy the text or share the report.

If you give an email address with the domain, we send one email when the report is ready, with its link. That is the only email the audit sends, and asking for a report does not sign you up for a newsletter.

## Limits

One rewrite per audit. A finished report stands for 30 days; opened after that, it is shown with its date while a new run replaces it at the same link, so a shared link never breaks. On a busy day the report can be generated without the map or the rewrite; it says so rather than showing something older or invented. Runs are capped per address and per day.

If a run fails, or stops making progress for 90 minutes, entering the same domain again starts a new run at the same link. A retry counts against the same per-address and daily caps as a first run.

## From the audit to a workspace

"\$129/mo. It does this every morning and starts working the map." The button carries your domain and your eight questions into the [first fifteen minutes](/start/first-fifteen-minutes) of a workspace, so nothing is typed twice.


# How we work
Source: https://docs.attensira.com/start/how-we-work

An SEO agency run by AI agents: who does what, what reaches you, what needs your yes, and what we promise and what we don't.

Attensira is the AI-native SEO agency. SEO here means search everywhere: Google, the AI assistants (ChatGPT, Claude, Perplexity, Gemini and Google's AI), and the sites and communities those assistants read. The agents do the work. You set the direction, answer the few questions only you can answer, and approve what goes live.

No person on our side does client work. Research, writing, page fixes, outreach drafts, follow-ups and the weekly report are done by agents. A kickoff call is part of onboarding on every plan, the way a good agency starts, but nothing waits on it: checkout, the first readings and the first piece of work start without it.

## Who does what

| Who | What they do |
| - | - |
| **You** | Set goals, steer, approve or decline what the agents propose, answer their questions, and do the few things only you can do, such as merging a pull request or sending an email from your own mailbox |
| **Chief of staff** | The one agent you talk to. It answers, proposes goals and projects, hands work to the right crew, makes sure it gets done, and reports back. It does not do the work itself |
| **A project's crew** | The agents that do the work for one [project](/agent/projects): a Researcher, a Writer, a Web engineer and the others the Chief of staff staffs it with. Each one wakes when something it is responsible for changes |
| **Your own assistant** | Optional. Connected over [MCP](/mcp/overview), it can set up a workspace, read the numbers and act, with the same data and the same approvals as the dashboard |

You never define an agent, write a schedule or pick a team. You describe what you want in your own words and the Chief of staff proposes how it will be done, as a card you accept or change.

## Where you talk to it

* **Home.** Type what you want. Leave it to the Chief of staff to route, or pick the project it belongs to.
* **A project's feed.** **Message** asks the crew something or hands it work. **Steer** sets direction ("focus on enterprise, stop the SMB posts"), and the steer is kept word for word as the project's direction. See [Projects](/agent/projects).
* **The Inbox.** Everything that needs you.

## What reaches you

The agents stop only when they need you, and when they do, it lands in the [Inbox](/agent/inbox) as one of three kinds of item:

| Kind | What it asks | What you do |
| - | - | - |
| **Approve** | A yes or no on something the agents made: a change to your site, an article, an action that cannot be undone | Approve it, edit a draft and then approve it, or decline it and say why |
| **Answer** | A fact only you have: a question an agent asked, or a gap in your business profile | Answer in the row |
| **Do** | Something only you can do: connect an integration, send a drafted email from your mailbox, paste an article where it goes | Do it, then mark it done |

Work the agents did on their own, and work already approved and moving, is shown as well, so you can see it, but it asks nothing of you.

The agents also report without being asked: a short [brief](/agent/morning-brief) on mornings when there is something to show, and, every week, each project's Chief of staff posts what its crew did and what moved.

## Nothing irreversible without your yes

Every action an agent can take is classified by whether it can be undone. Reversible work, such as opening a draft pull request or drafting an article, can happen on its own. Anything that cannot be taken back stops for your yes. How much happens before that stop is your workspace's [approval mode](/agent/approval): ask, draft or act. A project can be set stricter than its workspace, never looser.

Two kinds of action stay yours unless you hand them over:

* **Merging.** We open pull requests; you merge or close them. We merge only if you switch on [auto-merge for safe changes](/agent/approval#auto-merge-for-safe-changes), and then only safe changes such as structured data, meta tags, alt text and internal links.
* **Sending.** Outreach goes from your own mailbox, never ours. A pitch is drafted into your Drafts for you to send. The agent can send pitches for you only in act mode, and only after the first ten have gone out by hand. See [Off-site work](/agent/off-site).

## Where the work lands

Work lands in your own systems, where you already review things: a pull request on your repository, a draft in your CMS or Notion, a pitch in your mailbox, or text to paste where there is no connection. Everything that was made is listed on **Deliverables**, with where it lives and what it did.

Every change is read before and after it ships, on the same questions and the same assistants. See [Before and after](/measure/before-and-after).

## What we promise, and what we don't

**We promise the work.** Your plan and your monthly budget buy a volume of work: articles, page fixes and outreach pitches. If we deliver less than the budget committed, the work we did not deliver is not charged. Work that is finished on our side and waiting on you, such as a pull request to merge or an article to publish, counts as delivered: it is never a shortfall. See [Plans](/account/plans#if-we-deliver-less-than-the-budget-committed).

**We don't promise outcomes.** No one can promise what an assistant or a search engine will say, and whether a change works also depends on it being published. We do not guarantee rankings, citations, mentions or traffic. We measure every change and tell you what it did, including "nothing moved that we can prove" when that is the honest answer. See [Limitations](/reference/limitations).

## What you pay for

A plan fee for the platform, and a monthly budget for the work, shown as the work it buys: "≈ 4 articles · 12 page fixes · 20 outreach pitches · daily monitoring" for a \$1,500 budget. The budget is a ceiling, not a prepayment: work is charged as it is done, new work stops when the month reaches the budget, and **Costs** shows where every dollar went. See [Plans](/account/plans) and [Costs](/account/costs).


# Quickstart
Source: https://docs.attensira.com/start/quickstart

From your domain to a team at work: the set-up steps, the plan and budget, the first readings, and how to do it from your own assistant.

Signing up asks for one thing, your website. From the domain, the agents read your site, choose the questions to track, find your competitors and run a first scan, and each set-up step shows what they found with the answer already filled in. Most of set-up is reading it and pressing **Continue**. Then you choose a plan and a monthly budget, and the team starts work.

## Set up in the dashboard

<Steps>
  <Step title="Give us your domain">
    Enter it on the sign-up page, or arrive from the [free audit](/start/free-audit) with it already filled in. Submitting it creates your workspace and starts set-up on our side: reading the site, picking the starter prompts and running a preview scan. If you came from the audit, the questions it asked are kept word for word.
  </Step>

  <Step title="Your business">
    What we read from your site, each fact with the page it came from. Correct any of them in place. What the site did not say is asked as a question rather than guessed. An unanswered question never blocks you: it is asked again later.
  </Step>

  <Step title="Who you compete with">
    The rivals that actually show up in the answers, filling in as the scan reads. Every suggestion starts ticked: untick the ones you do not lose deals to, and add any that are missing. Only the ticked ones are tracked. See [Competitors](/tracking/competitors#how-are-competitors-suggested-at-signup).
  </Step>

  <Step title="Where you want to be found">
    The assistants, Google, YouTube, Reddit and review sites, with the sensible ones already on and a line on why each one matters for you. A surface we cannot measure for your workspace yet is still offered and still worked on, and says **not measured yet** rather than showing a number.
  </Step>

  <Step title="Your goals">
    The first goal is about being seen at all: named in a number of the prompts that matter, per assistant, with the baseline the scan read and a date. Other proposed goals are switches you turn on or off. The prompts behind the goals are your starter set: about fifty, grouped into four to eight [topics](/tracking/topics#the-starter-set). Read them and remove or reword the ones that are off, now or later from the prompts screen, chat or MCP.
  </Step>

  <Step title="What your scan found">
    The [free scan](/start/first-fifteen-minutes#the-scan) asks each starter prompt once on ChatGPT, Perplexity and Claude. One row per assistant, with how many of its answers are in, and once enough are, how often they named you. An assistant we cannot read yet says so; it is never shown as a zero. You can continue at any time: the scan keeps running.
  </Step>

  <Step title="Your plan and budget">
    The plan runs the platform. The budget buys the work, and is shown as the work it buys, for example "≈ 4 articles · 12 page fixes · 20 outreach pitches · daily monitoring". Beside it: work we don't deliver isn't charged, and items waiting on you to publish count as delivered.

    At the budget, new work pauses and you are asked first, or you can let it run up to 10% over. The button agrees to one sentence with three numbers in it, such as "We'll top up $50 when your balance falls below $10, never more than \$1,500 a month." Nothing is charged by agreeing: payment is taken at checkout, and the budget rule starts when your plan does. You can change the budget or top up any time in **Settings → Billing**. See [Plans](/account/plans).
  </Step>

  <Step title="Watch the team start">
    After checkout, the first readings start straight away, the first piece of work follows, and the projects the Chief of staff proposes for you appear under **Your team**, each one yours to keep, pause or remove. You can also book the kickoff call there. See [Your first fifteen minutes](/start/first-fifteen-minutes).
  </Step>
</Steps>

## Set up from your own assistant

Your own assistant can do the same set-up over the [MCP server](/mcp/overview). Add the server to your assistant and sign in through the browser when it asks (see [OAuth](/mcp/oauth)); you need no workspace first, because the first call makes your account's organisation. Then ask it to set up your business. Once you are set up, **Settings → Your assistant** connects more assistants.

1. `start_workspace` takes your domain and creates the workspace, or resumes it. It returns the facts we read, the questions we have, the suggested competitors and platforms, the proposed goals and the plans. Calling it again with the same domain is how the assistant checks on progress; it never creates a second workspace.
2. `confirm_setup` saves your choices (plan, monthly budget, competitors, platforms, goals and profile corrections) and returns a checkout link. The plans it can take are the ones `start_workspace` listed: every plan checkout sells, not only Starter.
3. You open the link and pay in your browser. **An assistant cannot charge you**: every payment finishes in a browser, with you looking at the price. If you already have a card on file, the link opens your billing page instead, so the charge is still one you make yourself.
4. Once you have paid, `start_workspace` reports the workspace as active, and then when its first readings are ready.

Until an account has paid, it has one workspace: `start_workspace` with a second domain is refused until the first is paid for, so an assistant cannot leave a trail of half-set-up workspaces.

Items your assistant files in the Inbox are marked with where they came from, such as **via Claude**, so you can tell them from the ones the team raised. The same approvals apply to everything it does.

## Reading the first numbers

The moment your plan starts, your [first reading](/start/first-reading) reads every prompt on every engine your plan includes, on a fast lane of its own, and its first answers land within minutes. A prompt you add later is queued for its first reading when you add it. After that, readings run on a daily cadence: each prompt × model × country combination is read **at most once per workspace-local calendar day**.

Open **Analytics** in the sidebar, then its **Overview** tab. Two numbers matter first. **Share of voice** is the share of successful runs that named your brand. **Citation rate** is the share of successful runs that attached a link to your own domain. They answer different questions: whether the model knows you, and whether it sends readers to you.

Every rate arrives as a value plus `n`, the number of successful runs behind it. A value of `0` with `n > 0` is a real measured zero. A **null** value means not measured: no successful run exists for that cell, and it is not a zero. Per-model numbers also carry `tracked` and `readable`; a model with `tracked: false` was never asked, so it is not a 0%.

Sampling depth is the number of times each prompt × model is asked per reading: `n = 1` on Starter, `n = 3` on Growth, Business and Agency. Changes over time pass a two-proportion z-test at 95%. Below that noise floor you get `{real: false, value: null}`, which means "no change we can prove", never zero.

<Warning>
  Do not read the first day as a trend. With `n = 1` a single day is one draw per prompt; rates only mean something once several days have accumulated.
</Warning>

## What next?

Ask the agent why a number looks the way it does, then hand the recurring parts to an Automation. Both start from the box on [Home](/start/today#home), under "What should we work on?"

When the agent needs you — an approval, a decision, a connection only you can make — it says so in two places: the **Inbox** button in the top bar, whose badge counts what is waiting, and **Needs you** on Home, which lists the top three. Both show the same number. See [Home and the dashboard](/start/today) for where everything else lives.

<Tip>
  Word-to-field mapping, and why the wire format still says `keyword_id`, `group_id` and `work_area_id`, is on [Concepts](/start/concepts).
</Tip>


# The set-up run
Source: https://docs.attensira.com/start/set-up-run

The one session that runs itself when your plan starts: it reads your site, groups what is wrong by the source that decided it, and files a starting plan.

## What it is

One session starts on its own, without you asking for it. It is the first thing the product does with your money, and the only run allowed to look at a workspace it has never seen before.

It runs **once, ever**, per workspace.

## When it starts

* **At payment, when your scan already has enough.** If the free [scan](/start/first-fifteen-minutes#the-scan) you saw before paying reached its floor in the last 48 hours, the set-up run starts the moment your plan does. Its floor here is at least half the scan's answers read, or every read finished with at least one answer. A scan that read nothing never starts it.
* **Otherwise, at your first reading's floor.** When there was no scan, it was older than 48 hours, or it did not read enough, the run waits for your [first reading](/start/first-reading) to reach its floor: **at least half the reads back**, or twenty minutes after the reading started.

Either way it is one session. The first-reading floor arriving later does not start a second one.

Fifteen minutes after you pay, we check what the run has filed. If nothing is filed yet, you are told why: on the onboarding screen while it is still working, or with one row in your Inbox when you have to do something. See [Fifteen minutes, or the reason](/start/first-fifteen-minutes#fifteen-minutes-or-the-reason).

## What it does

1. **Reads your site.** The home page and whatever pages answer a buying question — never a claim about a page it did not actually fetch.
2. **Reads the losing prompts** from the readings so far (your scan's or your first reading's): where you were not named, on which engine, against which rival.
3. **Groups what it finds by deciding source** — the same grouping [Who decides an answer](/measure/deciding-sources) uses — so a domain behind four losses is one piece of work, not four.
4. **Files at most three rows** in your [Inbox](/agent/inbox), each naming one source and the decision that is yours to make, backed by a verbatim excerpt from the answer.
5. **Proposes one fix as a pull request**, when a repository is [connected](/agent/github) — the single best on-site change among the sources it found. Never more than one; a first session that opens four pull requests is a first session nobody merges.

## If it finds nothing wrong

A workspace with nothing to fix does not end with an empty run. Instead, the session takes your **worst prompt** — lowest rate, largest sample — and files one plan for winning it: the page that should answer it, the claim that page is missing, and the evidence behind each part of that plan.

A set-up run never ends with nothing filed.

## If credits cannot cover it

The balance is checked before anything is read or dispatched. If your workspace cannot afford the run, nothing is read and nothing is charged — but the refusal is never silent. It is written down three ways: a **blocked** row in your Inbox, an email, and a session you can open that says exactly why it did not start. Top up, and the set-up run can be started again.

## What it never does

* It never reports a trend. There is one day of readings and nothing to compare it to yet.
* It never lists every page it read or every prompt you are named on — only what needs a decision.
* It never files more than three Inbox rows, and never fewer than one.

<Note>
  This is a once-ever run. The standing job that keeps watching after it is the hourly pass described in [What the agent notices on its own](/agent/findings).
</Note>


# Home and the dashboard
Source: https://docs.attensira.com/start/today

The signed-in home: what to work on next and what needs you. Plus where everything else lives, in the sidebar and the top bar.

## The sidebar

The dashboard has one sidebar. At the top is the **brand switcher**: the brand (workspace) every screen is about, shown by its domain. Its menu switches brand, adds one (**Add a brand**), and opens **All workspaces**, the overview of every workspace in your organisation (see [Clients](/agencies/clients)). When you belong to an organisation, the same menu switches, manages and creates organisations.

Under it, five rows:

* **New**: [Home](#home), at `/`. Starting something is what you open it for, so the row is named for that.
* **Search**: opens the command menu. Cmd-K (Ctrl-K) does the same from anywhere.
* **Analytics**: everything tracked, as one row of tabs: **Goals**, **Overview**, **Prompts**, **Pages**, **Sources**, **Competitors**, **Boards** and **Site health**.
* **Deliverables**: every article, page change and post the agents made, where it lives and what it did.
* **Customize**: what the agents work with, as tabs: **Connectors**, **Skills**, **Guardrails**, **Agents** and **Company**.

Then your chats: pinned ones first, then the most recent, and **All chats** for the rest (`/s`). Cmd-N (Ctrl-N) starts a new one from anywhere.

**Bot traffic** is not in the sidebar. Open it from the command menu ("Bot traffic") or at `/analytics/ai-traffic`. See [AI traffic](/traffic/ai-traffic).

<Note>
  Older addresses still work. `/knowledge` opens Customize on **Company**, and `/plugins` opens it on **Connectors**.
</Note>

## The top bar

The bar above every screen stays put while the page scrolls under it. Left to right:

* **Search**, the same command menu as the sidebar row.
* **Inbox**, with a badge counting what is waiting on you. It counts decisions, not rows: seven runs stuck on one missing connection are one thing to do, and count as one. Chats paused on a question for you count too. The badge turns amber once the oldest item has waited more than three days, and hovering it says how long, for example "4 things waiting on you, oldest waiting 5 days". See [Inbox](/agent/inbox).
* **Usage**: your credit balance, and on wider screens the prompts you track and the responses read this month. Open it for those against your plan's limits, **Buy credits**, and **Upgrade**. It turns amber when the balance is low or a plan limit is used up. See [Credits](/account/credits) and [Plans](/account/plans).
* **Settings**, the gear.
* **Your account**: Settings, Billing, Help & support, Docs, and Sign out.

On a phone, the sidebar opens from the menu button at the left of the bar, Search shrinks to its icon, and Settings is in the account menu.

## Home

Home asks **What should we work on?** and gives you the box to answer in. Everything on it is one of two things: pointing the agents at work, or answering what only you can answer.

<Steps>
  <Step title="A status line, only when something is broken">
    When the [loop](/agent/loop-health) is blocked, one line says so and why, for example "Attensira is paused: GitHub is not connected", with **Fix it**. When everything runs, there is no line at all.
  </Step>

  <Step title="The box">
    Ask a question, or ask for work. `/` picks a mode (Plan, Ask or Report), and the microphone dictates. See [Chats](/agent/chats#the-box) for everything the box does.
  </Step>

  <Step title="Try">
    Under the box, a few questions drawn from your own numbers. Picking one puts it in the box for you to read and edit. It is never sent for you. They step aside as soon as you type.
  </Step>

  <Step title="Needs you">
    The three things that most need you, with how many are waiting beside the heading. That number is the same one the Inbox badge shows. See [Needs you](#needs-you) below.
  </Step>
</Steps>

### Needs you

The list leads with the same three decisions the [Inbox](/agent/inbox) leads with, in the same order, most consequential first. A chat paused on a question for you is listed after them. Each row works in place: approve or decline a change, read a written piece, or connect the integration a group of rows is waiting on, without opening the Inbox.

Under the three:

* **N more waiting · Open the Inbox**, naming what the rest are waiting on when that is a connection, for example "including Connect GitHub".
* **N in progress, nothing needed from you**, when chats are running.

When nothing is waiting, it says **Nothing needs you**. If what is waiting could not be read, it says **Couldn't load what's waiting** with **Retry**, and keeps the last count it read. It never shows an empty list in place of a failed read, because "nothing needs you" is the one wrong answer that list could give.

Act on a row here and the next one moves up; the count beside the heading and the Inbox badge change together.

## After checkout

Returning from checkout lands on one page: **Checkout cancelled** or **Payment received**, with a button back to Home.


# What Attensira does
Source: https://docs.attensira.com/start/what-it-does

Agents do the SEO work toward goals you set, and every change is measured against what real assistants answer.

## What problem does it solve?

Buyers now ask an assistant before they open a search engine, and the assistant offers two or three options. If you are not one of them, you are not considered. Three things stop most teams acting on that:

1. **The numbers are opaque.** Share of voice, citation rate, sentiment: it is hard to tell which one should change what you do this week.
2. **The tools need an operator.** Dashboards say what is wrong and leave the fixing to you.
3. **Nobody has the time.** Even a correct recommendation sits untouched, because acting on it is a week of work nobody scheduled.

Attensira takes the job. Agents do the work, show every piece of it, and measure what it did. You set the direction and approve what goes live. See [How we work](/start/how-we-work) for who does what.

## How the loop works

<Steps>
  <Step title="You set goals">
    At signup the agents read your site and propose goals. The first is usually to be seen at all: named in a number of the questions that matter to you, on the assistants you chose, by a date. Each goal has a metric, a baseline, a target, a date and a pace line it is read against.
  </Step>

  <Step title="The Chief of staff plans and staffs it">
    The Chief of staff proposes [projects](/agent/projects), each a crew of agents on one area of work, such as your blog or the comparison questions. You accept, change or remove them. You never define an agent or write a schedule.
  </Step>

  <Step title="The crews do the work">
    Pull requests to your site, articles, page rewrites, directory listings, community replies and outreach drafts. Every morning the [Daily win plan](/agent/autopilot) also does up to three pieces of work toward the questions you want to win.
  </Step>

  <Step title="You approve what goes live">
    Anything that cannot be undone waits for your yes in the [Inbox](/agent/inbox), along with the questions only you can answer and the few things only you can do. Work lands in your own systems: your repository, your CMS, Notion, your mailbox.
  </Step>

  <Step title="We measure, every day">
    Each day we put your prompts to the assistants you track and store the answers. Every change is read before and after it ships, and each goal is read against its pace line. A goal that falls behind is re-planned.
  </Step>
</Steps>

## What we measure

A **Prompt** is one question, phrased the way a buyer would phrase it. Each prompt runs against each **Model** you track, and every **Answer** is stored and read for two separate things, for you and for each competitor you track: whether the brand was named, and whether the model attached a link to the brand's domain. These are real answers from live model surfaces, not predictions and not a simulation.

### Mention versus citation

A **Mention** means the answer named your brand in its text, with no link. A **Citation** means the model attached one URL pointing at your own domain. They are counted independently: an answer can mention you without citing you, cite you without naming you in prose, do both, or do neither.

That distinction matters. A mention means the model knows who you are. A citation means the model sent the reader to your page. The two move for different reasons and are fixed by different work, so Attensira never collapses them into a single score.

Share of voice is the share of successful runs that named your brand. Citation rate is the share of successful runs that cited your own domain. Competitor rates use the same denominator, so they will not add up to 100%.

<Note>
  A **Source** is a domain bucket across all citations in a window, including domains that are not yours. It tells you which sites the models lean on when answering your prompts.
</Note>

## What it does not do

* **It does not guarantee outcomes.** We promise a volume of work for your budget and do not charge for work we don't deliver. No one can promise what an assistant or a search engine will say. See [Plans](/account/plans#what-a-budget-does-not-buy).
* **It does not claim revenue.** We show what changed in the answers and in your traffic beside the work we shipped. We never claim a sale was caused by it.
* **No person on our side does client work.** The agents do it, and a person is asked only for what a person has to decide or do.
* **It does not rank brands against each other.** Your position counts where you came among the names you track in one answer, and there is no share-of-voice figure that sums to one.
* **It does not count an answer that names you without judging you as neutral sentiment.** Sentiment reads only the answers that take a position on you.
* **Crawler traffic is a separate measurement.** A crawler fetches pages and a model answers prompts; those are separate systems, read separately.


# What to expect in your first eight weeks
Source: https://docs.attensira.com/start/what-to-expect

What you will see in weeks 1, 2, 4 and 8, in the order the signals can honestly appear: shipped work, crawler fetches, Search Console impressions, then citations and goals.

Before you pay, the plan step at signup shows four steps under the plan and budget: what you will see in weeks 1, 2, 4 and 8. The same four steps come back in your Monday note while the workspace is new, so the expectation you were given at signup is the one you are measured against afterwards.

The steps describe what we measure and report. They are not results we promise: what you pay for is the work, and work we don't deliver isn't charged. What the engines then do with it is something we measure, not something we control.

## The four steps

<Steps>
  <Step title="Week 1 · Shipped work">
    The agents ship the first changes, and the Monday note lists each one with a link.
  </Step>

  <Step title="Week 2 · Crawler fetches">
    We count how often AI crawlers fetch each changed page after it goes live.
  </Step>

  <Step title="Week 4 · Impressions">
    Search Console starts to report impressions for the changed pages, about two days behind.
  </Step>

  <Step title="Week 8 · Citations and goals">
    We report whether answers name or link you for the first time, and how each goal is pacing.
  </Step>
</Steps>

## Why it runs in this order

Each step is the earliest point at which that signal can honestly appear.

* **Work comes first** because it is the one thing fully in our hands. A pull request opened, a page rewritten, a pitch drafted: each is listed with a link the week it ships, and the same acts are on your [receipt](/account/receipt).
* **Crawler fetches come next** because a crawler has to fetch a changed page before an engine that searches the web can quote the new version. These are counts of fetches, not of answers. See [AI traffic](/traffic/ai-traffic) for why a fetch and a citation are different things.
* **Impressions follow** because Search Console reports with a delay of about two days, and a new or changed page takes time to be shown at all.
* **Citations and goal pace come last.** Answers change slowly, and some come from what a model learned months ago, so a citation or a goal moving is reported as what it is and never presented as the result of the work.

## Where you see it

**At signup.** On the plan step, under the plan and the budget, before you go to checkout, headed *What you'll see in your Monday note*. No week is marked yet.

**In the Monday note.** The Monday note carries the same four steps under *What to expect in your first eight weeks*, with the line "You are in week N". In the app, the step you have reached is also marked; the email lists the steps in order. The weeks count from the day the workspace was created, not from payment: week 1 is its first seven days, and the timeline stops showing after day 56. The first Monday note a workspace receives always carries the timeline. If that note comes after the eighth week, it shows the steps without a week number.

The signup page and the note use one shared set of steps, so they cannot describe different timelines. The note is the Monday edition of the [morning brief](/agent/morning-brief).

## When a signal cannot be measured

Two of the steps depend on something being connected. When it is not, the note says why in words, never with a zero that could mean "nothing happened".

| Step | Needs | Without it, the note says |
| - | - | - |
| Crawler fetches | The [AI traffic](/traffic/ai-traffic) crawler snippet installed on your site | We cannot count AI crawler fetches yet, because the crawler snippet is not installed on your site. |
| Impressions | Search Console connected to the workspace | Search Console is not connected, so impressions are not counted. |

If a change went live too recently for Search Console's reporting delay, the note says the change is still inside that delay. It does not print a zero.

Shipped work and citations need nothing extra. Citations come from the answers we already read for your [prompts](/tracking/prompts). Goal pace is shown for the goals your workspace has.

## Related

* [Your first fifteen minutes](/start/first-fifteen-minutes): the scan, the verdict and the first fix.
* [Before and after](/measure/before-and-after): how a change is measured once it ships.
* [Reading the numbers](/measure/reading-the-numbers): sample sizes, unmeasured cells, and when a change is real.


# Brand family
Source: https://docs.attensira.com/tracking/brand-family

The other domains, products and sub-brands you own count as you once you confirm them. Agents propose them with evidence, and prefer publishing on your own domain.

## What counts as you?

Your workspace's domain counts as you, and so does **every subdomain of it**. An answer that cites `blog.example.com` has always counted as citing `example.com`. Nothing needs setting up for that.

The **brand family** is everything else you own: a second domain, a product with its own name, a sub-brand. Once you confirm one, an answer that names it, or cites a page on its domain, counts as you. That applies to every headline number: share of voice, citation rate and position. A confirmed property is never listed as a third-party source, a deciding source, an outreach target or a new competitor.

| Kind | What it is | How answers are matched |
| - | - | - |
| `subdomain` | A host under your own domain, such as `blog.example.com` | Already counted as you. Confirming one only names it, so the [split](#how-much-of-you-each-part-is) can show how much of "you" it carries |
| `domain` | Another domain you own, such as `hello.com` | Citations of the domain and of any subdomain under it (`blog.hello.com`). Not the other way round: owning `shop.hello.com` does not make the rest of `hello.com` yours |
| `product` | A product name, such as "Acme Analytics", with an optional domain of its own | The name in the answer text, matched the way your brand name is, and citations of its domain when it has one |
| `sub_brand` | A sub-brand name, with an optional domain | As for a product |

### What a property cannot be

* **Your own domain, or anything under it, as a `domain`.** It already counts as you. A host under your domain is added as a `subdomain`.
* **A tracked competitor's domain.** One domain cannot be both you and a rival, so it is refused with `property_is_competitor`. If it really is yours, remove it from [Competitors](/tracking/competitors) first.
* **A name shorter than three characters, a placeholder, or a common word.** "Analytics", "Cloud" or "Studio" would make every answer about the subject an answer that named you. Name a product the way the answers would, such as "Acme Analytics".
* **More than 50 properties** in one workspace, counting proposals and rejections.

A product name that is also an everyday phrase can still over-count, as a brand name can. A confirmed name is matched the way your brand name is, so confirm only names the answers use to mean you.

## Only confirmed properties count

A property is **proposed**, **confirmed** or **rejected**. Only a confirmed one changes a number. A proposed one is a question an agent is asking you, and a rejected one is kept so the question is not asked again.

Every confirmed property records the person who confirmed it. Nothing is confirmed automatically, and an agent cannot confirm one: an agent may do the finding, but deciding what your numbers mean is yours.

You manage the list in **Settings → Workspace → Brand family**:

* Proposals sit at the top, marked **Proposed by an agent**, with the evidence and a confirm or reject control for each.
* Confirmed properties are listed below them. With none, the group shows your domain and its subdomains, which already count.
* **Add a property** takes a domain, or a name with its kind.

Proposals are also announced in the [Inbox](/agent/inbox) as one row, "Are these yours?", listing each with its evidence. You confirm or reject them here, in Settings. Settings has no control to remove a confirmed property.

## Confirming a property changes history

An answer that named "Acme Analytics" last month named you, once you confirm that Acme Analytics is yours. So confirming a property re-reads your stored answers against the new family. It is the same re-extraction a competitor change goes through. A queued job does it within minutes, and the nightly reconcile catches anything left over. Rejecting a proposal, or adding a new one, changes nothing that was measured, so it re-reads nothing.

Until an answer has been re-read, it is counted as stored in the rolled-up numbers and left out of the per-property split. The split says how many answers are waiting, and why.

## Agents propose properties

Once a week, alongside the [markets proposals](/tracking/markets#agents-propose-markets), an agent looks for what else is yours:

* **Subdomains of your domain** seen in citations over the last 90 days, or in the crawl of your site. They already count; proposing one is asking you to name it.
* **Cited domains whose name carries your brand**, such as `examplehq.com` for `example.com`.
* **Domains your home page links to** in its structured data (`sameAs`) or its footer. Profiles on platforms such as LinkedIn, GitHub or G2 are left out.
* **Products** your Knowledge profile lists under what you sell, when they read as names rather than descriptions.

A domain candidate is also asked a "same organisation?" question before it is proposed. That check can only stop a proposal; it can never confirm one. A tracked competitor's domain is never proposed, and neither is anything you have already confirmed, rejected or listed. One pass proposes at most ten properties.

## How much of you each part is

Your headline numbers already include every confirmed property. The **Brand family** panel on **Sources** shows how much each part carries. It appears once a workspace has at least one confirmed property.

* **Rolled up**: how often answers named your brand or any property, and how often they cited a page of any of them, over every answer read, with the most-cited pages.
* **Per part**: your main brand first, then each confirmed property. Each has a **hit rate**, the answers in which it counted as you, named or cited. It is one rate because the stored answer records that the part counted, not which of the two it was. Each also has a **cited rate**, the answers citing a page on its domain (unmeasured for a part with no domain of its own), and its `n` and most-cited pages.

**The rows overlap.** One answer can name your main brand and cite your blog, so it counts on both rows. The per-part rows do not add up to the rolled-up line, and are not meant to.

`query_evidence` splits share of voice by `property`, and narrows it with `property_ids`. Only share of voice can be split this way, for the same reason as the hit rate.

## Where agents publish

The rule: if your main brand is `example.com`, then `blog.example.com` is much better than `blog.hello.com`. Agents rank every place a page could go in this order, and say why:

1. **A path on your domain**, such as `example.com/blog/`. It inherits everything the domain has earned, and every citation of it counts as you.
2. **A subdomain of your domain**, such as `blog.example.com`. It carries your brand and counts as you, but search engines treat a subdomain as a partly separate site, so it starts behind a path.
3. **Another domain you have confirmed**, such as `hello.com`. It counts as you, but it builds its authority alone and splits your brand across two sites.
4. **A new domain. Agents never propose one.** It starts with no authority and splits your brand, and starting a new site is your decision.

Agents have this ranking twice: in the knowledge brief they start from, and in a `brand_family` tool they ask before choosing where a draft, a new page or a tool will live. Given the place a draft is meant for, the tool also checks it: anywhere but a path on your domain is flagged unless the draft states why the better option does not fit, and a domain you have not confirmed is flagged whatever reason it gives. A proposed property is listed to the agent as proposed, so it is never treated as yours before you confirm it.

## Over MCP and the API

[`get_brand_family`](/mcp/tools#get_brand_family) returns every property with its status and evidence, then the window rolled up and split by part. It is MCP-only for now. Confirming, rejecting and adding properties is done by a person, signed in to the dashboard. An API key or an assistant connection cannot change the list.

Client reports carry a **Brand family** section once the workspace has a confirmed property. See [Client reports](/agencies/client-reports).

## Related

* [Markets](/tracking/markets): reading your prompts in more than one country.
* [Citations and sources](/measure/citations-and-sources): what counts as a citation of you.
* [Competitors](/tracking/competitors): the brands you are measured against.


# Competitors
Source: https://docs.attensira.com/tracking/competitors

Track rival domains alongside your own, and read competitor rates correctly — they do not sum to 100%.

## What does adding a competitor do?

Adding a competitor tells Attensira to look for that brand in the answers it is already collecting. No extra runs are issued and no extra prompts are consumed. Every answer from every tracked prompt is re-read for the competitor's name and domain, and the result becomes a competitor share of voice and citation rate next to your own.

Because the analysis runs over answers you already have, a competitor added today is measured against answers collected from that point forward, on the same daily cadence as everything else. Competitors do not consume prompt slots.

Add a competitor by domain — `example.com` — rather than by display name. The domain is what makes a citation attributable; the brand name is what makes a mention detectable. Attensira uses both.

You can add one programmatically with the `add_competitor` MCP tool. See [MCP overview](/mcp/overview).

## How are competitors suggested at signup?

At signup you choose your competitors from suggestions instead of typing a list. While the agents set up your workspace, they store up to ten rivals named on your site and in its analysis as **candidates**. A candidate is not a competitor yet: no share of voice, rival table, rollup or agent tool includes one. The only thing that reads candidates is the reading of answers, so the step can say how often each one was named before you choose.

The suggestions on the competitors step come from three places, in this order:

1. **Brands the free scan's answers named**, with the count: "Named in 11 of 150 answers".
2. **Domains those answers cited beside yours.**
3. **Candidates no answer has named yet**, from your site, ranked last.

The scan is usually still reading while you look at this step. Counts say "so far", and the step says how many answers are still to come, so an early count is not read as final.

Every suggestion starts ticked. Untick the ones you do not lose deals to, and add any that are missing by their website.

### The one question

The agents check each candidate against your business profile: is it in the same market as you? When they are confident, nothing is asked. When they are unsure, they ask you about exactly one candidate, inline: track it, or leave it out. That candidate starts unticked, because ticking it would be answering for you. The check never tracks a rival on its own. Only your choice does.

### When you continue

* The ticked suggestions, and the ones you added, become tracked competitors.
* The rest are removed, and never reach a share figure.
* The scan's answers are read again against the competitors you chose, so your first numbers use your list.
* If the question was still unanswered, it goes to your [Inbox](/agent/inbox) as a decision, once per domain, and that candidate is kept until you answer.

## What gets measured for a competitor?

The same two rates you get for yourself, over the same runs:

* **Share of voice** — successful runs that name the competitor, divided by all successful runs.
* **Citation rate** — successful runs that attach a URL on the competitor's domain, divided by all successful runs.

A **mention** is the brand appearing in the answer text with no link attached. A **citation** is one URL the model attached to the answer. These are different events and a strong brand often has one without the other: models name well-known companies from memory and cite whichever page they happened to retrieve.

Alongside these, **source share** tells you what fraction of all citations in the window went to a given domain — a competitor's domain included. And **AvgPosition** is the mean index of a URL within the model's citation list, computed **per source domain**. It is not a brand rank and it is not a leaderboard position; a low AvgPosition means that domain's links tend to appear early in the list of links, nothing more.

<Warning>
  Attensira does not rank brands against each other. Your **position** counts where you came among the names you track in one answer, and **sentiment** scores how answers that take a position talk about **you**; the Sentiment page does not score competitors. Each answer's full [brand order](/measure/share-of-voice#the-full-brand-order) lists every brand it named, competitors included, as evidence for that one answer, not as a leaderboard.
</Warning>

## Why don't competitor rates add up to 100%?

Because a single answer can name several brands at once, and every rate is measured against the same denominator: **all successful runs in the window**. Each brand is scored independently — "was this brand named in this answer, yes or no" — so the rates overlap by design and routinely total well over 100%. They can also total under 100%, when many answers name nobody you track.

Every competitive product you have used implies the opposite. Share of voice, market share, ranking positions — these all divide a fixed pie and sum to one. Attensira does not, because an assistant answering "what are the best expense tools for a small agency?" does not pick a winner. It lists five.

### A worked example

Ten successful runs of one prompt. Here is what the answers contained:

```text theme={null}
Run  1: names You, Acme, Borealis
Run  2: names Acme
Run  3: names You, Acme
Run  4: names Borealis
Run  5: names You, Acme, Borealis
Run  6: names Acme, Borealis
Run  7: names nobody tracked
Run  8: names You
Run  9: names Acme, Borealis
Run 10: names You, Acme
```

The rates:

| Brand | Runs naming it | Share of voice | Denominator |
| - | - | - | - |
| You | 5 | 50% | 10 successful runs |
| Acme | 7 | 70% | 10 successful runs |
| Borealis | 5 | 50% | 10 successful runs |

That totals 170%. Nothing is wrong. Acme appears in seven of ten answers; you appear in five; they appear together five times. Run 7 named nobody, and it still sits in every denominator — an answer that ignored the whole category is real evidence, not a missing data point.

The practical consequence: **your share of voice going up does not require a competitor's to go down.** You can both rise, if models start naming more products per answer. Read each rate as "how often is this brand in the room," never as "what slice of the room does this brand own."

<Note>
  The `share_of_voice` field holds the rate defined above, on the denominator defined above. It is not a slice of a total and it does not sum to one across brands.
</Note>

## How do I compare a competitor fairly?

Compare like against like. Both rates come from the same runs, the same prompts, the same model surfaces and the same days, so a difference between two brands on one topic is a real difference. A difference between two topics is not, because the prompts differ.

Watch the counts. Rates arrive as `{value, n}`, and `n` is the number of successful runs behind the number. A competitor at 100% off three runs and one at 62% off eighty are not comparable claims. A `value` of `null` means not measured — no successful run — while `0` with a non-zero `n` is a real, measured absence.

Changes carry `{value, real}`. Attensira applies a two-proportion z-test at 95% before it reports a movement. Below that noise floor you get `{real: false, value: null}`, which means "no change we can prove." Do not read it as flat, and do not put it in a board deck as zero.

Finally, remember what a competitor rate is not: it is not traffic, not revenue, and not a ranking. It is the frequency with which a model, asked your buyers' questions, brings a brand up. See [Models](/tracking/models) for how the surfaces you track shape that number.


# Markets
Source: https://docs.attensira.com/tracking/markets

Read your prompts in more than one country, and in the language buyers there ask in. Each engine says how it was located there, an engine that cannot be is shown as not measured, and the cost is shown before you save.

## What is a market?

A **market** is a country, and optionally the language a prompt is asked in there. Every workspace has a **primary market**: the country it was set up with. With no other markets, nothing changes: each prompt × model is read once a day from that country, exactly as it always was.

Add a second market and every prompt is read again from that country, on every model. The unit of work becomes **prompt × model × market**, and each market has its own rates, its own `n` and its own trend. A workspace, a topic or a prompt can hold up to **five** markets.

Markets cost [AI responses](#what-markets-cost): a second market over every prompt doubles them. Settings prices every change before you save it, and agents propose a second market [scoped to the topics with demand there](#agents-propose-markets) rather than to everything.

## Where are markets set?

**Settings → Workspace → Markets and engines** holds the workspace's list:

* **Primary market** comes first. Every number defaults to it, and when the response allowance runs short, extra markets pause before it does. To change it, make another market in the list the primary.
* **Markets** is the ordered list. Each market can carry a language.
* **Engines** are the models read in every market above. At least one has to read.
* Under each market, one line says how each engine is read there: "Read locally on every engine", or the engines that are not, such as "Claude: location stated in the question only".

Nothing is saved until you save, and saving shows the [cost preview](#what-markets-cost) first.

### Topic and prompt overrides

A topic or a prompt can read in markets of its own:

* **A topic**: open the topic's menu and choose **Markets**.
* **A prompt**: open its move dialog and switch to **Prompt markets**.

Each offers **inherit** or a list of its own, and each change goes through the same cost preview. The most specific setting wins:

```
prompt markets → prompt country → topic markets → topic country → workspace markets → workspace country
```

An empty list goes back to inheriting, so clearing a list can never leave a prompt read nowhere. The first market of whichever list applies is that prompt's primary market.

### Asking in a language

A market with no language asks the prompt **as written**. A market with a language, such as Germany with `de`, asks it **in German**: a buyer in Germany asks ChatGPT in German, and the brands and pages that answer cites are not the answer to the English question.

* An agent translates each prompt once per language. Your brand, your domain and your tracked competitors' names are kept exactly as written. A prompt is translated again only when its text changes.
* If the translation cannot be made, that market's readings **wait** and are retried on the next pass. They are never asked in the original language under the German market's name, which would be a different question with the wrong label. Loop health records it as `translation_failed`.
* Languages come from a fixed list of BCP-47 tags. A tag outside it is refused when you save, rather than becoming a market that never reads.

<Accordion title="Supported market languages">
  `ar`, `bg`, `cs`, `da`, `de`, `de-AT`, `de-CH`, `el`, `en`, `en-AU`, `en-CA`, `en-GB`, `en-IE`, `en-IN`, `en-NZ`, `en-US`, `es`, `es-419`, `es-ES`, `es-MX`, `et`, `fi`, `fil`, `fr`, `fr-BE`, `fr-CA`, `fr-CH`, `fr-FR`, `he`, `hi`, `hr`, `hu`, `id`, `it`, `ja`, `ko`, `lt`, `lv`, `ms`, `nb`, `nl`, `nl-BE`, `pl`, `pt`, `pt-BR`, `pt-PT`, `ro`, `ru`, `sk`, `sl`, `sr`, `sv`, `th`, `tr`, `uk`, `vi`, `zh-CN`, `zh-Hans`, `zh-Hant`, `zh-TW`
</Accordion>

**One language per country, for now.** A prompt × model × country is read once a day, so a country listed twice in two languages (Switzerland in `de` and in `fr`) is read in the language listed first. The other is recorded as [not measured](#not-measured-is-not-zero), with the reason "this country is already read in another language for these prompts". The two are never pooled into one number.

## How is each model located in a market?

Not every model can be asked from a country. Attensira writes down how each one was, and never presents one kind as another.

| Model | Read through | In a market | Mode |
| - | - | - | - |
| ChatGPT, Perplexity, Google AI, Gemini | The consumer product, from a browser in that country | Read as a person there sees it, in the [countries listed below](#which-countries-can-be-read-locally) | `geo_proxy` |
| Claude | Its API | The location is **stated in the question**. It is not a local search | `hinted` |
| Any model read through its API fallback | Its API | The location is stated in the question | `hinted` |
| A consumer model in a country outside the list | — | **Not read.** No call is made and nothing is counted | `unsupported` |

<Warning>
  **Claude is never read from a location.** There is no consumer reader for Claude. Its answers come from the API, and the country is added to the end of the question as a line reading `User location: DE`. That has always been how Claude is asked in your primary market as well. A Claude rate in Germany tells you how Claude answers someone who **says** they are in Germany. It does not tell you what someone searching from Germany sees, so it is labelled "location stated in the question, not a local search" everywhere it appears.
</Warning>

Every reading in a secondary market, and every reading asked in a market language, records its language and its mode, so later comparisons can set like against like. Readings in the primary market asked as written are left exactly as they were, with nothing new recorded on them.

### Which countries can be read locally?

The consumer models can be read from a browser in these 42 countries:

United Arab Emirates (AE), Argentina (AR), Austria (AT), Australia (AU), Belgium (BE), Brazil (BR), Canada (CA), Switzerland (CH), Chile (CL), Colombia (CO), Czechia (CZ), Germany (DE), Denmark (DK), Spain (ES), Finland (FI), France (FR), United Kingdom (GB), Greece (GR), Hong Kong (HK), Ireland (IE), Israel (IL), India (IN), Italy (IT), Japan (JP), South Korea (KR), Mexico (MX), Malaysia (MY), Netherlands (NL), Norway (NO), New Zealand (NZ), Philippines (PH), Poland (PL), Portugal (PT), Romania (RO), Saudi Arabia (SA), Sweden (SE), Singapore (SG), Thailand (TH), Turkey (TR), Taiwan (TW), United States (US), South Africa (ZA).

The list is deliberately conservative. A country that cannot be confirmed is treated as unsupported, because the alternative is an answer read from somewhere else and labelled as that country's.

**A primary market outside the list** is still read on every model, exactly as before, but the consumer models' answers there may not be local. [`get_markets`](/mcp/tools#get_markets) says so in that cell's `note`: "Read as before, but ChatGPT cannot be read from KE, so its answers may not be local." Such a market is never compared with another on those models, because how it was located cannot be stated.

## Not measured is not zero

A secondary market that a model cannot be read from is a **gap**: no call is made, no reading is written, and the gap is recorded with its reason. It is shown as "Not measured in Kenya on ChatGPT", with the reason, never as 0% and never folded into another market's number.

| Reason | What it means |
| - | - |
| `market_unsupported` | That model cannot be read from that country. For the consumer models, the country is outside the [list above](#which-countries-can-be-read-locally) |
| `language_shares_country` | The country is already read in another language for these prompts. See [one language per country](#asking-in-a-language) |

Gaps appear on the Markets section of the analytics overview, in `get_markets`, in `query_evidence` answers about markets, and in [client reports](/agencies/client-reports).

A secondary market that was skipped because the month's responses ran short is not a gap. Its readings are recorded as unmeasured, and the [allowance section](#the-primary-market-is-read-first) explains what happens.

## Comparing markets

The **Markets** section of the analytics overview shows one row per market: its share of voice with `n`, its citation rate, how many prompts were read there, and its change against **the same market** in the window before. One market is a sentence, not a table. Two or more markets read under this setting add a split by model and surface, each with its mode, and a comparison of every market against every other on each model.

Attensira says one market leads or trails another, for example "DE trails US by 12 points (n = 84 vs 340)", only when all three of these hold:

1. **Both were read the same way** on that model and surface. A market read from a local browser is never compared with one whose location was stated in the question, because the comparison would be of the method, not the place. The row says "not compared: DE and US are not read the same way". Claude in two markets is stated-in-the-question on both sides, so it can be compared. Read that comparison as a difference in how Claude answers a stated location.
2. **Each side has at least 30 successful answers** on that model and surface in the window.
3. **The gap clears the same significance test** as every other delta: a two-proportion z-test at 95%. See [How we collect data](/measure/how-we-collect-data#what-is-the-significance-gate).

Below the floor the row says "no clear difference yet (n = 12 vs 340)". Over the floor but inside the noise it says "no clear difference (n = 45 vs 340)". Neither carries a number of points, because a number is how a screen says "this is a difference".

| Claim | What it needs | Below that |
| - | - | - |
| A market's rate | Nothing. It is always shown with its `n` | The rate and its `n` |
| A market's own change | The significance gate: at least 2 runs a side, z-test at 95% | "No proven change" |
| One market against another | Same mode, at least 30 answers a side, and the gate | "No clear difference yet", with both `n` |

On Starter, a market read on a few prompts collects only a few answers a week per model. The cost preview says when that is the case: "weekly comparisons need about 4 weeks here; monthly comparisons are available sooner". Widen the window rather than reading a week.

**Adding a market does not show as a drop.** A headline number pooled over markets would fall the day a weaker market starts being read. When the set of markets differs between the two windows, the change is computed over the markets read in both, and the chart carries a marker such as "Germany added".

To see everything for one market, cut the analytics to it: choose the market in the Markets section, and an **Asked from Germany** chip keeps the cut visible until you clear it. The prompts board can also be grouped **by market**, with one section per market. A prompt read in two markets appears in both.

## What markets cost

Readings are metered in **AI responses** against your plan's monthly response allowance, not in credits. See [Plans](/account/plans#the-monthly-response-budget-is-a-flow).

```
responses a month = prompts × models × markets × samples × 30
```

Allowances are sized for **one** market at full use. Starter is 50 prompts × 4 models × 1 sample × 30 days = 6,000 responses a month. A second market over every prompt makes that 12,000, which is 6,000 over the allowance. The same market over the 10 prompts that have demand there adds 1,200.

### The cost preview

Every change to markets or engines is priced before it is saved, at the workspace, topic and prompt level. The preview shows:

* **Responses a month, now and after**, across every workspace in the organisation, because the allowance is the organisation's.
* **What your plan includes**, and how far over it the change goes.
* **What happens past the limit**, in the words of your overage policy: readings in the extra markets pause, or they continue and the extra responses are billed as overage.
* **Answers a week**: when a market would collect fewer than 30 a week on a model, the note above, with the lowest count.
* **What will not be read**: the market × model cells that cannot be located, "never counted as zero".

A change that raises your responses past the allowance is saved only when you confirm it. Without that confirmation the save is refused with `409 over_allowance`, carrying the same preview, so an old tab or a stale approval cannot save past the allowance unseen. A change that lowers your responses is never refused.

### The primary market is read first

A second market can never stop your primary market's readings.

* The primary market's readings reserve their responses first, exactly as they did with one market.
* Each secondary market reserves its own responses separately. If that is refused, only that market is skipped.
* Under the **pause** policy, a secondary market may only spend the part of the allowance your primary markets are not projected to need for the rest of the month. Late in the month, that headroom runs out before the allowance does.
* Under the **bill** policy, secondary markets keep reading and the extra responses are billed as overage.

A skipped market is never silent. [Loop health](/agent/loop-health) records the day's skip as `market_allowance_reached`, and the [Inbox](/agent/inbox) gets one row per market per month: "Readings in DE are paused until the allowance resets". To read it sooner, track it on fewer topics, or move to a plan with more responses.

## Agents propose markets

You do not have to find your markets yourself. Each workspace is looked at once a week, and the morning after you connect Search Console. An agent gathers the evidence and needs at least one strong signal:

| Evidence | Strength |
| - | - |
| Search Console: the country's share of your Google impressions over 90 days, and the queries behind it | Strong from 5% and 100 impressions |
| Knowledge: the markets you say you sell into | Strong |
| Your site: an hreflang version for the country, or a country domain that names it | Strong |
| Your home page's declared language | Weak |
| Countries you already set on some prompts or topics | Weak |

The best candidates become proposals, at most three standing at a time. Each is **scoped to the topics with Search Console demand in that country**, at least 20 impressions on a topic's queries. Only when there is no per-topic demand does a proposal cover every topic, and it says why. A proposal asks in the country's main language when that language is on your Knowledge **Languages you publish in** list, and otherwise asks as written and says so.

Each proposal arrives in the Inbox as one row, "Track Germany too?", with the evidence, the topics it covers and the cost from the same preview Settings shows. To take it, set the market on those topics from each topic's menu (**Markets**), which shows the same cost preview before it saves. **Dismissing the row**, or marking it done without taking it, declines it: nothing changes, and that country is not proposed again for 90 days. A row you snooze stays open.

Agents never save markets themselves. A workspace with no country set gets no market proposals, because adding a market beside a primary that does not exist would change what every existing reading means.

## Over MCP and the API

* [`get_markets`](/mcp/tools#get_markets) returns the markets, engines and how each engine is read in each market, then the window by market with comparisons and gaps.
* [`preview_markets`](/mcp/tools#preview_markets) prices a change in responses and saves nothing.
* `query_evidence` groups by `country` and filters on `markets`, and answers with the gaps as gaps.

These three are MCP-only for now. Saving markets is done by a person, signed in to the dashboard. An API key or an assistant connection cannot change them.

## Related

* [Brand family](/tracking/brand-family): the other domains and names that count as you.
* [Models](/tracking/models): which surfaces have readers at all.
* [How we collect data](/measure/how-we-collect-data): the daily cadence, sampling depth and the significance gate.


# Models
Source: https://docs.attensira.com/tracking/models

Choose which assistant surfaces answer your prompts, and understand tracked versus readable.

## What is a model in Attensira?

A model is an assistant surface that answers your prompts — ChatGPT, Perplexity, Claude, Gemini, Google AI. In code and on the wire it is called a **channel**, and the channel id (`chatgpt`, `perplexity`, `google_ai`, `claude`, `gemini`) is deliberately stable. When a vendor swaps the underlying model behind their product, the channel id does not change, so your history stays continuous instead of splitting into a new series every release.

That stability is the point of the concept. You are tracking the surface a buyer actually uses, not a specific model version they will never see the name of.

<Note>
  This is not the same thing as an **agent model** — the LLM that a chat inside Attensira runs on, which Attensira picks for each turn. Agent models power the assistant you talk to. Model channels are the surfaces Attensira measures.
</Note>

## Which models does a new workspace track?

A new workspace starts on **four**: ChatGPT, Perplexity, Claude and Google AI. That is the default channel set, and it is a starting configuration rather than a limit. Gemini, on Growth and above, is switched on by hand.

Attensira does not offer five models to everyone. The readable set is defined in the engine, and it is not the same as the list of assistants that exist in the world:

| Channel | Wire id | API reader | Consumer reading |
| - | - | - | - |
| ChatGPT | `chatgpt` | Yes | Yes |
| Perplexity | `perplexity` | Yes | Yes |
| Claude | `claude` | Yes | No |
| Gemini | `gemini` | Yes | Yes |
| Google AI | `google_ai` | **No API reader** | Yes |

Google AI has no API reader. Whatever coverage exists for it comes from consumer reading only, and you should expect thinner, less regular data there than on the API-backed channels. Say this out loud in any report you build on it.

## Are models gated by my plan?

Partly. Starter reads four channels — `chatgpt`, `perplexity`, `claude` and `google_ai`. Growth, Business and Agency add `gemini`, which is the only plan-gated surface. Switching Gemini on in a Starter workspace changes the setting but not the readings: the scheduler queues only the engines the plan includes, so nothing is asked and the metric comes back untracked rather than as a zero. Your plan also governs [prompt slots](/tracking/prompts) and sampling depth. See [Plans](/account/plans).

Within your plan's channels, the allocation is yours.

The real constraint is the **response budget**. Asking more models multiplies the work: every model you add multiplies your prompt count by one more daily reading per prompt, per country, at your plan's sampling depth. Five channels across 50 prompts at n=3 is a very different volume of readings than two channels at n=1 — and the budget is spent proportionally. Each extra [market](/tracking/markets) multiplies it again, and the engines are chosen in the same Settings group as the markets, with the cost shown before you save.

You choose the allocation. Two common shapes:

* **Wide and shallow.** Track four or five channels across a smaller set of prompts. Good when you do not yet know where your buyers are and want to find out.
* **Narrow and deep.** Track two channels across many prompts. Good when you already know one surface drives your pipeline and you want statistically usable rates on a wide question set.

<Warning>
  Credits and the response budget are currently inconsistent in the product — a known open bug we are fixing. Because of that, this page does not publish a table of how many readings a plan's grant buys, and we make no promise that monitoring is free of credits. See [Credits](/account/credits) for what a credit is and what spends it.
</Warning>

## What do "tracked" and "readable" mean?

Every per-model metric Attensira returns carries two flags, `{tracked, readable}`, and reading them wrong is the most common way to misreport a result.

**`tracked: false`** means the channel is not configured for this workspace. It was never queried. There is no answer, because no question was asked. Rendering that as 0% invents a failure that did not happen — it says a model declined to name you, when in fact you never asked it.

**`readable: false`** means no reader exists for that surface at all. This is a capability of the platform, not a setting in your workspace, and no amount of configuration turns it on. `google_ai` over the API is the live example.

Both are distinct from a measured zero. Put together with the number shapes:

| What you see | What it means |
| - | - |
| `tracked: false` | Not configured here; never queried. Not a zero. |
| `readable: false` | No reader exists for this surface. Not a zero. |
| `{value: null, n: 0}` | Tracked, but no successful run in the window. Not measured. |
| `{value: 0, n: 12}` | Asked twelve times, named zero times. A real zero. |

This bites hardest in per-model breakdowns and in exports, where an untracked channel and a channel that never named you look identical unless you check the flags. If you are building a dashboard on `get_analytics`, branch on `tracked` and `readable` before you format a percentage. See [MCP overview](/mcp/overview).

## How do I change which models I track?

Change the channel set in your workspace settings. A newly added channel starts producing data from its next scheduled run and does not backfill, so its history begins the day you add it, exactly like a new prompt.

Each prompt × model × country combination runs at most once per workspace-local calendar day, at your plan's sampling depth — n=1 on Starter, n=3 on Growth, Business and Agency, issued concurrently. Failed runs are stored but excluded from `n`, so a channel having a bad day shows up as a smaller `n`, not as a drop in your rate.

Turning a channel off stops future runs. It does not delete the readings already collected, which remain subject to your plan's retention window. Turning it back on later resumes collection but leaves a gap in the series for the period it was off — and during that gap the metric is `tracked: false`, not zero.

<Tip>
  Before adding a channel across the board, add it and wait two weeks. Rates on a new surface are noisy until `n` builds, and the two-proportion z-test at 95% that guards deltas will correctly refuse to call a change until it has the evidence.
</Tip>

## A model is not a crawler

Models and AI crawlers are different systems and Attensira reports them separately. A model answers a prompt. A crawler fetches pages from your site. A high crawl rate does not produce mentions, and a model can name you having never crawled you.

The two also fail differently, and crawler traffic cannot be measured the way web analytics measures humans: AI crawlers do not execute JavaScript. A client-side beacon, however it is written, has never seen one and never will — by the time the script runs, the only visitors left are humans with browsers. See [AI traffic](/traffic/ai-traffic).


# Prompts
Source: https://docs.attensira.com/tracking/prompts

Add the questions people actually ask assistants, and manage the prompt slots your plan gives you.

## What is a prompt?

A prompt is a question a person actually types into an assistant, written the way they would type it. "What's the best expense tool for a 20-person agency?" is a prompt. "expense tool agency" is a keyword, and no one asks it. Attensira runs each prompt against the model surfaces you track and reads the answer that comes back.

The distinction matters because the answer is prose, not a ranked list. A model replies with a paragraph or two naming a handful of products, sometimes with links attached. What Attensira measures from that answer is whether your brand was named (a **mention**), whether the model attached a URL on your domain (a **citation**), and which domains it drew on (**sources**). Keyword-shaped input produces a thin, unnatural answer that tells you nothing about how buyers are actually being advised.

In code and in the API, a prompt is a `keyword_id`. The name is historical; the object is a prompt.

<Tip>
  Write prompts in the words your buyers use, not your own category language. If your customers say "invoice chasing" and your site says "accounts receivable automation," track both and compare.
</Tip>

## How do I add prompts?

You paste the questions; the agent files each one under a topic; you confirm or correct it before anything is tracked.

<Steps>
  <Step title="Open Add prompt on the Prompts screen">
    Every prompt belongs to one workspace. A workspace is one brand and one set of prompts, competitors, and model surfaces. The dialog opens with the paste box on one side and the [suggestions panel](#where-do-suggestions-come-from-in-the-add-dialog) on the other.
  </Step>

  <Step title="Paste one question per line">
    Phrase each as a complete question. Avoid your brand name in the prompt itself unless you specifically want to measure branded questions — an unbranded prompt is the honest test of whether a model reaches for you unprompted.

    A paste out of a spreadsheet, a document or a chat works as it is: list markers (`-`, `*`, `•`, `1.`, `2)`), wrapping quotes and trailing commas or semicolons come off, and a line repeated in the paste counts once. A line over 500 characters is flagged, and Review waits until it is shortened, because the whole add would otherwise be refused for it.
  </Step>

  <Step title="Press Review and read the proposal">
    Every line is listed with the topic the agent would file it under: one of your topics, or a new one it names. Each topic is a select you can change: another topic, **New topic…** to start one of your own, or **No topic**. New topics are named once, above the table, and a rename there reaches every line filed under that topic. A topic name is at most 60 characters.
  </Step>

  <Step title="Press Add">
    The button says how many prompts it adds. New topics are created first, then every prompt is added in one write, each under its topic. Each new prompt takes one slot.
  </Step>
</Steps>

No question in the paste disappears from the review. A line the agent did not place stays in the table with its reason, and you choose a topic for it or leave it without one. The reasons you will see most often:

| Reason shown | What it means |
| - | - |
| Not sure where this goes | The agent was not confident enough to pick a topic, so it does not guess. |
| Longer than 300 characters | Too long for the agent to file. You can still choose a topic for it yourself. |
| Already tracked | The workspace already tracks this question. It is shown as **Unchanged** and is not added again. |
| Topic suggestions are unavailable | The agent could not be reached. See below. |

A new topic the agent names that matches one of your topics, ignoring case, is filed under the topic you already have rather than creating a second one with the same name.

**The agent being unavailable never blocks the add.** When it cannot be reached, or your credit balance is at zero, the review opens with "Topic suggestions are unavailable right now. Choose a topic for each line, or add them without one." Every line starts as **Choose a topic**, and a line you leave unchosen is added without a topic.

**A topic that fails to create adds nothing.** If one of the new topics is not created, no prompt is added: the review names the topics that were created and the one that failed, and the button becomes **Retry**, which carries on from the topic that failed without creating the earlier ones twice.

Adding from a topic's own page (**Add prompt** in the topic's menu) skips the proposal: every line is filed under that topic.

You can also add prompts programmatically with the `add_prompts` MCP tool. That tool costs credits. See [MCP overview](/mcp/overview).

```json theme={null}
{
  "prompts": [
    "What is the best expense tool for a 20-person agency?",
    "How do small teams handle receipt capture without a finance hire?"
  ]
}
```

New prompts do not backfill. A prompt starts producing data from its first scheduled run onward, so the history you see for it begins the day you added it.

## When does a prompt actually run?

An hourly ticker picks up scheduled work, but each prompt × model × country combination runs **at most once per workspace-local calendar day**. Adding a prompt at 09:00 and again pressing save at 11:00 does not get you two readings. The daily ceiling is the unit of measurement.

How many readings that day produces depends on sampling depth: **n=1 on Starter**, **n=3 on Growth, Business and Agency**, issued concurrently. Deeper sampling is why the same prompt on Growth gives a steadier rate than on Starter — one reading can only ever be 0% or 100%.

Runs that fail are stored but excluded from `n`. A failed run is not a zero; it is an absence, and the metrics exclude it rather than counting it against you.

## How do prompt slots work?

A prompt slot is a **stock, not a flow**. A slot is occupied for as long as a prompt is tracked, and it returns to your pool the moment you remove that prompt. You are not buying a monthly allowance of prompts you can burn through — you are buying how many prompts can be under observation at once.

| Plan | Price / month | Prompt slots | Sampling depth | Retention |
| - | - | - | - | - |
| Starter | \$129 | 50 | n=1 | 3 months |
| Growth | \$349 | 150 | n=3 | 12 months |
| Business | \$699 | 350 | n=3 | Unlimited |
| Agency | \$2,000 | 750 | n=3 | Unlimited |

Yearly billing is charged at ten months' price. There is no Pro plan.

So a Starter workspace that adds 50 prompts, removes 10 in March and adds 10 different ones in April is still within its 50 slots. Nothing accumulates and nothing expires.

<Warning>
  Removing a prompt frees its slot immediately, but the readings already collected stay subject to your plan's retention window. Removing a prompt does not delete its history, and re-adding the same text later starts a fresh prompt rather than resuming the old one.
</Warning>

### The slots counter

The Prompts screen's header and the add dialog both show how many slots are in use, for example "142 of 150 prompts on your plan", and the two always agree: an add moves both at once.

A paste larger than the slots you have left is stopped before anything is sent, with how many would fit: "3 of these 10 fit: your plan has 3 prompt slots left. Remove 7 lines or upgrade." If the plan's limit is reached between the count and the add, nothing is added and the dialog says how many slots the plan has left. When the count cannot be read, the counter says "Prompt slots unknown" with a Retry, and nothing is refused on a guess; the add itself still checks the limit.

## Does Attensira find new prompts for me?

Once a week, it looks for buyer questions you are not tracking yet. Two signals: URLs your competitors get cited on inside your existing readings, and pages AI crawlers (GPTBot, ClaudeBot, PerplexityBot and others) fetched from your site in the last 14 days with no tracked prompt behind them. Page titles and headings are matched against a small set of buyer-question shapes ("best X for Y", "how do I Z", "X vs Y", "top N for Z"), and anything too close to a prompt you already track is dropped.

Discovery never adds a tracked prompt on its own. At most 3 candidates a week land as one Inbox row, "Track these questions?", each with its evidence (the competitor URL, or the crawler hit count and an example URL) and, where known, how many prompt slots your plan has left. Approving adds the prompt the same way you would by hand. Nothing is tracked until you say so.

### Where do suggestions come from in the add dialog?

The add dialog has a collapsible **Suggestions** panel beside the paste box, drawn from real Search Console queries, from the sub-questions assistants work through on prompts you are losing, and, on a new workspace, from a starting guess. Each row shows its volume (Search Console impressions, or "Volume —" when none has been measured, which is not a zero) and the reason it was suggested.

* **Track** adds one suggestion at once, under the topic it was suggested for if it has one.
* **Dismiss** (the ✕) removes it, and it is not suggested again.
* Tick several and press **Track these** to put them in the paste box. They then go through the same topic proposal as anything you typed, and a suggestion is marked tracked only once its prompt is actually added.

## What is each prompt filed under?

Every tracked prompt carries a few attributes beside its topic. Every one is optional, and a missing one is shown as —, never as a default: a prompt with no intent is not "informational".

Today you read them through `query_evidence` over [MCP](/mcp/tools), which can group or filter any metric by intent, stage and persona. The dashboard does not show or edit them yet, so for now the agent's filing stands and tags cannot be added.

| Attribute | Values | Who sets it |
| - | - | - |
| **Intent** | `informational`, `comparison`, `commercial`, `transactional` | The agent, until a person sets it |
| **Stage** | `awareness`, `consideration`, `decision`, `retention` | The agent, until a person sets it |
| **Persona** | One of the buyer roles you confirmed in your brand profile | The agent, until a person sets it |
| **Priority** | `high`, `medium`, `low`, or — | Computed when it is read, never stored |
| **Origin** | `user`, `search_console`, `fanout`, `competitor`, `discovery`, `seed`, `agent` | Set once, when the prompt is added |
| **Tags** | Your own labels | A person only |

### Intent and stage

**Intent** is what the person asking wants from the answer:

* `informational`: to understand something, with no product choice on the table ("how do I close the books at month end").
* `comparison`: to weigh named products against each other, or find alternatives to one ("Xero vs QuickBooks").
* `commercial`: to research what to buy, without a name in mind ("best invoicing tool for freelancers").
* `transactional`: to act on a named product: its price, its plans, a sign-up, a trial.

**Stage** is where in the buying journey it is asked: `awareness` (understanding the problem), `consideration` (looking at kinds of solution and lists of products), `decision` (choosing between named products) or `retention` (already a customer: using, connecting, fixing or leaving a product).

### Persona

A persona is one of the roles in the **Who buys** field of your [brand profile](/agent/knowledge-and-skills#company), one role per line or per semicolon. Only a field someone confirmed counts: a value Attensira drafted from your site is our guess, so no prompt is filed under it. With no confirmed roles, no prompt has a persona. Confirming or changing the roles files your prompts again on the next night, except the ones a person set.

### How the agent files them

The agent files a prompt by intent, stage and persona within a minute or two of it being tracked, and a nightly pass picks up anything it missed. Each attribute is one question with a fixed list of allowed answers. An answer the agent is not confident in counts as no answer, and the attribute stays — rather than guessed. If the agent cannot be asked at all, the prompt is left as it was and asked about again on the next pass.

**A person's value is final.** Once a person sets a prompt's intent, stage or persona, the agent never files that prompt again, even when its own rules change.

### Priority

Priority is how much a prompt is worth working on first: its monthly search volume, weighted by how close its intent is to buying. On a log scale, a transactional prompt counts its whole volume, a commercial one 0.8 of it, a comparison 0.7 and an informational one 0.4. In practice a transactional prompt is **high** from about 250 searches a month and **medium** from about 15; an informational prompt needs about 1,000 searches a month to reach medium.

Priority is computed every time it is read and never stored, because the search volume behind it is refreshed and a stored priority would outlive the number it came from. It is — whenever the volume or the intent is not known. A measured volume of zero is a real zero and gives **low**; an unknown volume gives —. Priority is not shown in the dashboard yet, and the prompts board does not carry search volume, so for now every prompt's priority is —.

### Origin

Where the prompt came from, set when it is added and never changed: `user` for a person adding it (in the dashboard, over the API or over MCP, including `add_prompts`), `search_console` for an accepted suggestion from your own Search Console queries, `fanout` for a sub-question under a tracked prompt, `competitor` for a question taken from competitors' answers, `discovery` for a prompt Attensira proposed and you accepted, `seed` for a new workspace's starting set, and `agent` for a prompt an agent added during a run.

### Tags

Tags are yours alone; nothing files them. A tag is stored trimmed and in lower case, so "Q4 Launch" and "q4  launch" are the same tag. A tag is at most 40 characters, and a prompt holds at most 20.

## Is a prompt won or lost?

Each prompt has a **state** across the engines it is read on (won, contested, lost, absent, or — when nothing was measured), and each engine has a cell (named and linked, named, lost, absent, or —). Both are read from the latest successful answers in the window, with fixed thresholds. [Prompt state](/measure/prompt-state) gives the rules.

## How do I remove a prompt?

Delete it from the prompts view, or call the `remove_prompt` MCP tool. The slot is returned as soon as the removal is accepted, so you can add a replacement in the same sitting.

`remove_prompt` is marked as a destructive tool in the MCP schema. An agent holding your key can remove tracked prompts, so treat the key as a workspace-level credential — see [Authentication](/account/api-keys).

Remove prompts when the question stopped mattering, when two prompts turned out to be the same question in different words, or when a prompt is producing answers that never mention anyone in your category. Rotate slots deliberately rather than hoarding them: a tracked prompt you never read is a slot doing no work.

### Moving or deleting many prompts at once

Tick prompts in the grid and a bar appears above it: "12 selected", with **Move…**, **Delete…** and **Clear**, and **Ask** when the selection gives the agent something to work from. One change takes up to 200 prompts; over that, the bar says so before anything is sent.

* **Move…** files the selection under one topic, or takes it out of every topic. The readings move with the prompts, and so do the engines each is read on. A prompt without a market of its own takes the market of the topic it lands in. Moving changes topic numbers for past days; see [Topics](/tracking/topics#merging-splitting-and-dragging).
* **Delete…** lists every selected prompt and asks you to type the count before it runs, so 20 is never deleted when you meant 2. Each deleted prompt frees its slot.
* **Ask** hands the selection to the agent as a brief, with the question "What would move the most of these?"

A bulk move or delete is **not all-or-nothing**. Each prompt is moved or deleted on its own, and the note on the Prompts screen says what happened in those terms: how many landed, how many did not and why, and where the rest still are ("still in their old group", or "still tracked, with every reading intact"). Retry only what did not land.

## Reading the numbers on a prompt

Every rate is reported as a value plus the count it came from, `{value, n}`. A `value` of `null` means **not measured** — no successful run exists for that window. A `value` of `0` with `n` above zero is a real, measured zero: the model was asked and did not name you.

Changes over time carry `{value, real}`. Attensira runs a two-proportion z-test at 95% confidence, and a change that does not clear the noise floor is returned as `{real: false, value: null}` — "nothing we can prove yet," which is not the same as no change. [Reading the numbers](/measure/reading-the-numbers) covers all three rules.

## Where do you stand on a prompt?

A rate says how often you were named. It does not say who had the answer when you were not. "Named 0% of the time" reads the same whether a rival took every answer or nobody was named at all, and those are different jobs. **Standing** is the missing word: one state per model, and one for the prompt across its models.

Standing is built from the same readings as the rates beside it. It is shown in the dashboard and in the prompts CSV export. The [REST API](/api/overview)'s prompt analytics and the MCP `get_analytics` tool return the rates, not the standing. The MCP `query_evidence` tool can group and filter prompts by standing (`state`: `won`, `contested`, `lost`, `absent` or `unmeasured`) under the same rule, but it does not return the per-model cells.

### One model: the cell

Each model a prompt is read on gets one cell. The cell is the state of the **latest successful reading** on that model in the window you are looking at. A reading is the answers from one day's run; its n is the answers that came back successfully. Failed answers have no text, so they have no state and are left out of n.

| Cell | Name | Over the n answers in that reading |
| - | - | - |
| ● | Named and linked | You are named in at least half of them, and your domain is cited in at least half of them. |
| ◐ | Named | You are named in at least half of them. |
| ✕ | Lost | You are not, and one tracked competitor is named in at least half of them. |
| ○ | Absent | Neither you nor any tracked competitor clears half. |
| — | Not measured | There is no successful reading on this model in the window. |

"At least half" rather than "at least once", because a cell is a label on a small sample, and a label one answer in three can flip would move inside the sampling noise. Ties go to you: named in 1 of 2 answers is **Named**.

A competitor counts only when the answer names it in its own words. A competitor whose site is cited as a source but who is not named in the text does not make the cell **Lost**.

The cell shows the latest reading's standing, while the rate beside it covers the whole window. The two answer different questions: where you stand now, and how often you were named over the period. Hover a cell to see its evidence:

* **Named and linked:** "ChatGPT: named and linked to your site in the latest reading (3 answers, last read Sep 29)."
* **Lost:** the competitor it was lost to and when it was last read, for example "ChatGPT: lost to rival.com, named instead of you in the latest reading (3 answers, last read Sep 29)."
* **Not measured:** the reason, for example "ChatGPT: not measured, not read in this window." The other reasons are that every read in the window failed (with the commonest failure reason when there is one), that no answers came back, or that this prompt is not read on that model. [Reading the numbers](/measure/reading-the-numbers#how-does-the-dashboard-show-a-zero-and-a-dash) lists them.

<Warning>
  **Absent is a measurement. Not measured is the lack of one.** ○ Absent means we read the answers and nobody you track was named. — Not measured means we have nothing to read. A prompt we never managed to read is never shown as absent.
</Warning>

### Across models: the prompt's standing

The **Standing** column folds the cells into one state for the prompt. Only measured cells count: a model we could not read neither helps nor hurts the prompt, because counting it would turn a gap in our coverage into a claim about your brand. Call the number of measured cells M.

| Standing | Rule, checked in this order | Hover text in the dashboard |
| - | - | - |
| **Won** | Named (● or ◐) on at least half of the M models, and ● Named and linked on at least one | Named on at least half of the engines read, and linked on at least one. |
| **Contested** | Named on at least one model | Named on some engines, but not enough of them to have won it. |
| **Lost** | Named on none, and ✕ Lost on at least one | A tracked rival is named and you are not, on at least one engine. |
| **Absent** | Every measured cell is ○ Absent | Nobody you track is named — not you, not a rival. |
| **Not measured** | M is 0 | No engine has a successful reading in this window. |

For example, a prompt read on four models with cells ●, ◐, ✕ and — has M = 3. It is named on two of the three, which is at least half, and linked on one, so it is **Won**, and the column adds "lost on 1 of 3".

A measured standing that is lost on any model shows "lost on X of M" under it. That count is what **Losses first** sorts by.

### The prompts grid

**Analytics → Prompts → By prompt** shows every tracked prompt against every model, one column of cells per model, beside the Standing column and the four core rates (Mentions, Share of voice, Avg position, Citations). The rows also show topic, intent, monthly search volume, a trend, the answers read (`n`) and a Last change column. A column with no value for a row shows a dash with its reason, for example "Intent is not classified yet." On this board the Last change column is a dash on every row, with the reason "No shipped change of ours is linked to this prompt on this board yet."

* **Losses first** is the default order: prompts lost on the most models first. Ties go to the prompt with more monthly searches, with unknown volume last, and then to the order the board was loaded in, so a reload never swaps two rows. Any column header sorts too. The first press on a model's column puts the worst first: ✕, ○, ◐, ●, with — last in either direction.
* **Group by** Topic, Intent, Standing or Market. Each group can collapse, and its header shows the number of prompts in it (`n = 12 prompts`), the count in each standing, worst first ("3 lost · 1 contested · 2 won"), and the group's pooled rates with the answers behind them. Grouped by standing, the groups run Lost, Absent, Contested, Won, then Not measured.
* **Filter** by Standing, Engine (the model), Topic, Intent, Asked from (the country) and Last read. Filters, the sort and the grouping are all in the page URL, so a link you share opens the same cut in the same order.
* **Pinning one model** recomputes every row from that model's cell alone, so the Standing column then answers for that model: ● is Won, ◐ is Contested, ✕ is Lost, ○ is Absent.
* **Wide grids** scroll sideways inside the grid, with the prompt column pinned. The page itself never scrolls sideways.

The prompts CSV export carries the cut it was taken under and the same standing. Each row has `state` and `lost_on`, and for each model `<model>_state` (`named_linked`, `named`, `lost` or `absent`), `<model>_state_n`, `<model>_lost_to` and `<model>_state_at`, beside that model's share of voice and its `_n`. An empty state means not measured, which is not absent.

## The prompt page

Opening a prompt leads with three sections, in this order:

1. **How to win this answer**: the [win plan](/measure/win-this-answer). The next step on the engine with the most answers read, the deciding sources for each engine with their citation share and whether you are on them, and when the answer will be read again ("after the first action lands" when nothing is scheduled). **Have Attensira draft it** starts a chat that drafts the work for your approval.
2. **Work on this prompt**: items in [Work](/agent/inbox) whose proposed change targets this prompt, marked when they need your OK.
3. **Before and after**: [re-read](/measure/before-and-after) results for this prompt only, each with its before and after samples, their date windows and an outcome (moved, no change, inside the noise, or not enough baseline).

Below them are two answers on the same engine, one from then and one from now. The older one is labelled **Before it shipped** only when it really predates the change; otherwise it is labelled **Then**. Each answer is labelled "read once on" and its date, and the page says so plainly: two single answers show changed wording, not movement.

The existing readings sit in tabs under those sections: **Answers and sources** (the default), **How it moved**, **Per engine** and **Competitors**.

With one model pinned, the prompts CSV export leaves `avg_position`, `failed_reads` and `last_read_at` empty, because they are not recorded per model, and the file says so in its header.

Next: group related prompts with [Topics](/tracking/topics), and choose which surfaces answer them in [Models](/tracking/models).


# Topics
Source: https://docs.attensira.com/tracking/topics

Group related prompts so reporting rolls up by theme instead of one question at a time.

## What is a topic?

A topic is a named group of prompts that belong to the same buying question. You assign a prompt to a topic, and Attensira rolls the per-prompt numbers up into a single set of rates for the group. Nothing about how a prompt runs changes — topics are a reporting structure, not a scheduling one.

The reason to use them is statistical as much as organizational. One prompt on Starter gives you one reading a day; ten prompts in a topic give you ten. A single prompt swinging from 0% to 100% is often noise. The same swing across a topic is a signal you can act on.

In the wire format a topic is `group_id`, but the `get_analytics` area enum value is `topic`, and `POST /v1/prompts` files a prompt under one with a `topic` field. The published vocabulary and the enum agree; only the identifier field kept its original name.

## What changes when I group prompts?

Reporting rolls up. Instead of reading forty prompt rows, you read six topic rows, each carrying a share of voice, a citation rate, and competitor rates computed across every successful run of every prompt in the group.

That rollup is a pooled rate, not an average of averages. A topic with two prompts — one that produced three successful runs and one that produced nine — weights the twelve runs together. This is what you want: the topic answers "across the questions in this theme, how often were we named?"

Individual prompt rows do not disappear. Grouping adds a level of reporting above them; you can still open any prompt and see its own rates, its citations, and the source domains the models drew on.

<Note>
  A prompt without a topic still runs and still reports. Topics are optional. Prompts left ungrouped appear only at the prompt level.
</Note>

## The starter set

A new workspace does not open on an empty grid. At signup the agents choose its first prompts and file them into named topics, so by the time you reach the competitors step it already tracks about fifty prompts in four to eight topics. On the goals step they are shown collapsed: one line with the count, opening to the topics and then to the prompts.

The prompts come from three sources, and the order is the precedence:

1. **The free audit's prompts**, word for word, if you came from the [free audit](/start/free-audit). Rephrasing them would make the product's first number disagree with the one that brought you here.
2. **Prompts suggested by the analysis of your site.**
3. **New prompts the agents write** from your business profile. These are phrased the way a buyer asks an assistant, 4 to 14 words, and never name a brand, yours or anyone else's: an unbranded question is the honest test of whether a model reaches for you unprompted. Topic names never name a brand either.

Duplicates are removed, and the set is capped at the prompt slots you have left: your plan's, or Starter's 50 before you hold a plan, less the prompts your other workspaces already track. When the cap bites, the audit's prompts are kept first and the agents' own are trimmed evenly across topics. A chosen prompt that fits none of the proposed topics goes into a topic called **More questions**.

When the agents cannot tell what your business does from your site and profile, they do not guess. The starter set is then the audit's and the site analysis's prompts only, in one topic called **Starter questions**, and the goals step says why. Fill in your business profile and they can propose more.

The starter set is created once. After that it is an ordinary set of prompts and topics: add, remove, reword or regroup any of them, from the prompts screen, chat or MCP.

## How should I structure topics?

Group by the question a buyer is asking, not by the feature you want to sell. Buyers arrive at assistants with a job to do, and the topic that predicts your outcome is the job, not your roadmap.

Three structures that hold up:

* **By stage.** Discovery prompts ("what tools exist for X"), evaluation prompts ("X vs Y for a team of 20"), and objection prompts ("is X hard to migrate to"). These behave very differently — discovery answers name many brands, evaluation answers name few.
* **By segment.** The same question asked for a solo operator, a 20-person team, and an enterprise. Models often recommend entirely different products across these, and a blended number hides it.
* **By use case.** One topic per problem your product solves. This is the structure that maps most cleanly onto content work, because a weak topic points at a page you need to write.

Keep topics large enough to be stable — five to fifteen prompts is a workable band — and resist a topic that holds one prompt, which reports exactly what the prompt already reported.

## How do I create and change topics?

<Steps>
  <Step title="Create the topic">
    Use **New topic** on the Prompts or Topics screen, or let the agent propose topics when you [paste prompts](/tracking/prompts#how-do-i-add-prompts). Name it in the words a buyer would recognize. The name appears on every rollup row, so make it readable without your internal shorthand.
  </Step>

  <Step title="Assign prompts">
    A prompt belongs to one topic at a time. Assigning it to a new topic moves it rather than copying it. File prompts as you add them, move one from its own page, move many with the [bulk bar](/tracking/prompts#moving-or-deleting-many-prompts-at-once), or drag them between groups (below).
  </Step>

  <Step title="Read the rollup">
    Topic rates appear once the prompts in the group have successful runs. A brand-new topic reports `value: null` until then.
  </Step>
</Steps>

Regrouping is retroactive, and it takes effect at once. Nothing is stored per topic: every topic number is computed when you read it, from the stored runs and the topic each prompt is filed under **now**. Move a prompt and the next read of either topic counts that prompt's whole history in its new topic, with nothing to rebuild and no wait for the next reading. The same holds everywhere a topic is a cut: the boards, sentiment by topic, `query_evidence` grouped or filtered by topic, and `answer_slice` filtered by topic. Historical topic charts will therefore shift when you reorganize. Do a reorganization in one sitting rather than drifting prompts between topics week to week, or your trend lines describe your bookkeeping instead of your market.

Deleting a topic does not delete its prompts. They become ungrouped and keep reporting individually.

## Merging, splitting and dragging

The structure the agent proposes, or the one you drift into, rarely stays right. Three reorganisations fix it without re-adding anything:

* **Merge into…** is for two topics that turned out to be one question. Every prompt of this topic moves into the topic you choose, with every reading, this topic is deleted, and you land on the topic you merged into.
* **Split…** is for one topic that turned out to be two. Name the new topic and tick the prompts that go to it; the rest stay. The new topic takes the engines and the country of the one it came from. A split needs at least two prompts and must leave at least one behind; taking every prompt is a rename.
* **Dragging** works on the Prompts grid grouped by Topic: drag a row onto another group's header, or onto the no-topic group to take it out of every topic. A selected row carries the whole selection with it. Dragging is a shortcut for a pointer; on a keyboard or a touch screen, use the prompt's **Move** or the bulk bar's **Move…**, which make the same change.

Merge into… and Split… are in the menu on a topic's own page (**Analytics → Topics**, then the topic), offered when there is another topic to merge into and when the topic has at least two prompts.

Every regroup (moving one prompt, a bulk move, a drag, a merge or a split) says the same thing before it happens: "Grouping is retroactive: topic numbers for past days will recompute. Prompts keep the engines they’re read on." Both halves hold for all of them:

* **History recomputes.** Topic numbers are worked out from the stored runs and where each prompt is filed now, so the topic a prompt lands in reports its whole history and the topic it left stops reporting it. Nothing needs rebuilding, and nothing waits for the next reading.
* **The engines do not change.** A moved prompt keeps the engines it was read on, whichever topic it lands in, so a reorganisation never starts or stops reading a prompt on an engine. That is why a bulk move, a drag, a merge and a split still work when your credit balance is at zero; moving a single prompt with its own **Move** needs a balance above zero, so at zero, tick it and use the bulk bar's **Move…** instead. The market is the exception: a prompt without a market of its own is read in the markets of the topic it lands in. A split's new topic copies its source's country but not a list of markets set on the source topic (its **Markets** menu item); set that on the new topic if the split prompts should stay in those markets.

None of the three is all-or-nothing, and each says what happened:

* A merge that could not move every prompt keeps the source topic, so nothing you asked to move is left without a topic. It says how many moved and how many did not; merge again to finish.
* A prompt added to the source topic while a merge is running is never deleted with it: it ends up with no topic.
* A split that created its topic but could not move some prompts says so; those prompts stay in the original topic, and you move them from the prompt list.

## Reading a topic rollup

Topic rates use the same shapes as everything else. Rates come back as `{value, n}` where `n` is the count of successful runs behind the number, and `null` means not measured rather than zero. Deltas come back as `{value, real}` and are suppressed to `{real: false, value: null}` when a two-proportion z-test at 95% cannot separate the change from noise.

A topic's `n` is the sum of its prompts' successful runs, so topics cross the noise floor sooner than the prompts inside them. That is the practical payoff: a topic can prove a change in a week that a single prompt would take a month to demonstrate.

Competitor rates roll up per topic too — and, as everywhere in Attensira, they share the same denominator as your own share of voice and do not sum to 100%. See [Competitors](/tracking/competitors) for why.


# AI traffic
Source: https://docs.attensira.com/traffic/ai-traffic

AI crawlers fetch your pages; models answer prompts. Two systems, two numbers, and why measurement lives in edge middleware.

AI traffic is available on Growth, Business and Agency.

## Crawlers and models are not the same thing

A **crawler** is a bot that fetches your pages over HTTP. A **model** is the system that answers a prompt for a person. They belong to different pipelines, run on different schedules, and produce different numbers. A page can be fetched constantly and never cited, or cited from something a model learned months ago and never re-fetched.

So do not reconcile the two. Crawler counts in AI traffic and citation counts in [Pages](/traffic/pages) are not two views of one quantity, and a rise in one does not predict the other.

## Why measurement has to happen in edge middleware

Crawler visits are only visible on the server, before any page renders. That is a hard technical constraint, not a design preference:

> AI crawlers do not execute JavaScript. A client-side beacon, no matter how it is written, has never seen one and never will: by the time the script runs, the only visitors left are humans with browsers.

Edge middleware runs on every request, including the ones that never become a rendered page. That is the only layer where a crawler fetch exists to be counted.

<Warning>
  Any measurement approach that depends on a script in the browser is measuring humans. If your existing analytics shows zero AI crawler traffic, that is what it is telling you about itself, not about the crawlers.
</Warning>

## How do you install it?

Attensira generates the install instructions for your workspace in **Settings**. They include the ingest endpoint and the credentials tied to your workspace, so use the generated copy rather than an example you found elsewhere — a snippet without your workspace's values sends data nowhere.

<Steps>
  <Step title="Open Settings in the app">
    Find the AI traffic section for your workspace.
  </Step>

  <Step title="Copy the generated middleware instructions">
    They are written for the edge layer of your stack and already carry your workspace's values.
  </Step>

  <Step title="Deploy and wait for the first requests">
    Nothing appears until a crawler actually fetches a page. On a low-traffic site that can take days.
  </Step>
</Steps>

## What this data does and does not tell you

It tells you that requests arrived, when, and against which paths. Read it as a coverage signal: pages that are never fetched cannot become fresh citations, and a section that crawlers never reach is worth checking for robots rules, auth walls, or redirects.

What it does not give you is verified attribution. Attensira does not claim to prove that a given request came from a specific named crawler. User agents are self-reported and trivially spoofed, and Attensira does not publish a verified-bot claim it cannot stand behind. Treat the data as directional evidence about coverage, not as an audited log of who visited.

<Note>
  Crawler activity is not evidence of visibility in answers. For whether models actually name or cite you, read [Pages](/traffic/pages) and your Mention and Citation rates.
</Note>

## How long is it kept?

Two layers, kept for different lengths:

| Layer | What it holds | Kept for |
| - | - | - |
| Raw requests | Each request as it arrived | **90 days** |
| Daily rollup | Per day, per page and per agent: how many requests, why the agent came, and whether the visit was referred by an AI assistant | **Indefinitely** |

The rollup is what lets a before-and-after reach back further than 90 days: whether crawlers read a page more after a change you shipped in March needs March's numbers in June. It holds no field that identifies a person: no user agent string, no referrer and no IP-derived value, only the counts and the assistant a referral resolved to.

How a day gets into the rollup:

* Every night, the last **7** completed days are folded again, so the days a dashboard reads first are never more than a night behind.
* A day gets its **final** fold when it is 88 days old, two days before its raw requests expire. A request stamped with a date older than that is refused when it arrives, because it could never reach the rollup.
* The first time your workspace is folded, every day still in raw retention is folded, so your history starts with the full 90 days you already had.

A window up to 365 days long can be read. Days inside the last 90 come from the raw requests and older days from the rollup, with no day counted twice. A window reaches back only as far as your history is complete: asking for more returns the longest window there is, and the response says how many days of history you have. A change is compared against the previous window of the same length only when that whole window exists; otherwise no change is shown, rather than a drop that is really the start of your history.

### Days are UTC days

Traffic days run from UTC midnight to UTC midnight, and every response says so (`day_basis: "utc"`). Answer readings use your workspace's own calendar day instead. Near midnight, a crawler fetch and an answer from the same evening can land on different days. A window that reaches into the rollup starts at a UTC midnight, so every day in it is a whole day.

## Privacy

Requests are measured at your own edge, on your own traffic. The rollup kept beyond 90 days holds counts only, with nothing that identifies a person. See the [privacy policy](https://attensira.com/privacy) and [terms of service](https://attensira.com/tos).


# Pages
Source: https://docs.attensira.com/traffic/pages

See which of your own URLs models attach as citations, how often, and where they land in the citation list.

## What does the Pages table show?

Pages lists the URLs on your own domain that models attached as citations when they answered your tracked Prompts. One row is one URL. A URL appears only after a model actually cited it in a successful run inside the window you are looking at — this is not a crawl of your sitemap.

A **Citation** is a single URL a model attached to an Answer. A **Mention** is your brand named in the answer text with no link. Pages counts citations only, so a page can be doing useful work in a market where your brand is named constantly and still show few rows here.

## How do you read each column?

Each row carries the URL, how many citations it collected in the window, its share of your domain's citations, and its average position in the citation list. Read them together: a page with few citations but a strong average position behaves differently from one cited often and always last.

| Column | What it counts | What it does not mean |
| - | - | - |
| Citations | Times this exact URL was attached to an answer | Not visits, not clicks, not crawler fetches |
| Share | This URL's citations divided by all citations to your domain in the window | Not a share of the whole market |
| Avg position | Mean index of the URL inside the model's citation list, computed per source domain | Not a rank of your brand against competitors |

<Note>
  Average position is a per-source-domain measure. It tells you where your URLs tend to sit in a list of links, not how you place against any competitor. There is no brand rank or position metric in Attensira.
</Note>

## Why is a number blank instead of zero?

Every rate in Attensira is a `{value, n}` pair, and every delta is a `{value, real}` pair. A blank `value` means **not measured** — no run produced data for that cell. A `0` with `n` above zero is a real, measured zero: runs happened and the URL was never cited.

Deltas are tested for significance with a two-proportion z-test at 95%. When a change falls below that noise floor you get `{real: false, value: null}`, which reads in the table as no change we can prove. It is not a claim that nothing moved, and it is never a zero.

<Warning>
  Treat a blank and a zero as different facts when you report internally. Rewriting "not measured" as 0% invents a failure that the data does not support.
</Warning>

## Why does a page you care about not appear?

The table is bounded by what was queried. If a Prompt that would surface the page is not in your workspace, no run touched it. If the Model that cites it is not tracked for your workspace, it was never asked. A new workspace tracks four model channels by default — ChatGPT, Perplexity, Claude and Google AI.

Sampling depth also bounds what you see. Each Prompt × Model × country runs at most once per workspace-local calendar day, with n=1 on Starter and n=3 on Growth, Business and Agency. Rare citations need more samples and more days before they show up at all. Failed runs are stored but excluded from `n`, so a bad day narrows the window rather than poisoning it.

Retention limits how far back you can look: 3 months on Starter, 12 on Growth, unlimited on Business.

## What to do with the table

<Steps>
  <Step title="Sort by citations and read the top ten">
    These are the pages models currently trust to answer your Prompts. Keep them accurate and current before writing anything new.
  </Step>

  <Step title="Compare against your intended landing pages">
    If models cite a support article where you expect a product page, the product page is not answering the question the Prompt asks.
  </Step>

  <Step title="Check average position on the pages that matter">
    A URL consistently late in the citation list is being used as corroboration rather than as the answer.
  </Step>

  <Step title="Widen the window before concluding anything">
    At n=1 per day, single-day movement is mostly sampling noise. Read weeks, not days.
  </Step>
</Steps>

## How does Attensira know which pages you have?

The Pages table above is citation-driven, but Attensira also keeps a separate inventory of every URL your site publishes, so the agent can reason about pages models have *not* cited yet.

That inventory is a map of your domain made by our web reader, and your home page is always included so a small site still has one page on file. When the web reader is unavailable, the map falls back to what your site publishes: `/sitemap.xml`, or the sitemaps your `robots.txt` declares in `Sitemap:` lines. In that fallback, a page that is not in a sitemap is not discovered.

Page reads fall back the same way, to a plain HTTP fetch with no JavaScript rendering. A page built entirely in JavaScript may then come back empty, and the agent says so rather than guessing what it says.

Discovery re-runs **once a day**, so pages you publish after signing up are picked up on their own. It is unmetered: finding out that a URL exists costs you nothing.

Reading each page is the separate, metered step. Attensira fetches a page and keeps a version of it: its title, meta description, headings, structured data, structure and visible text. It re-reads a page after **7 days**, or about daily while one of your open suggestions targets it, a capped batch at a time so a large site is worked through steadily rather than all at once. A read that finds the page different records a change; see [Page changes](/measure/page-changes). Full-text analysis of a page happens only when you or the agent explicitly asks for it.

<Note>
  Two different things are called "pages" in this product, and they are worth keeping apart: the table on this screen counts pages **models cited**, while the inventory described here is every page **your site publishes**. A URL can sit in the inventory for months without ever appearing in the table.
</Note>

## Related

<Columns>
  <Card title="AI traffic" href="/traffic/ai-traffic">
    Crawler fetches of your pages, which are a different system from model answers.
  </Card>

  <Card title="Chats" href="/agent/chats">
    Ask the agent to explain a page's citation pattern in context.
  </Card>
</Columns>