> ## Documentation Index
> Fetch the complete documentation index at: https://docs.attensira.com/llms.txt
> Use this file to discover all available pages before exploring further.

# REST API endpoint reference

> All sixteen Attensira REST endpoints — parameters, request bodies, response shapes, and which ones spend credits or delete data.

Sixteen endpoints under `https://api.attensira.com/v1`. Every one takes `Authorization: Bearer atn_live_<32 hex>`; none takes a workspace id, because [the key names the workspace](/api/authentication).

Each endpoint below names the [MCP tool](/mcp/tools) it is the twin of. The two surfaces carry the same capabilities on purpose.

## 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                                               |
| `DELETE /v1/prompts/{prompt}`    | Destructive    | Stops tracking a prompt; its history goes with it                              |
| `DELETE /v1/automations/{id}`    | Destructive    | Removes the automation, its schedule and its run history                       |

Nothing else on this page changes state or produces a bill.

## 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

<Info>Read-only. No credits. MCP tool: `search`.</Info>

Full-text search across answers, prompts, pages, sessions and automations in one pass. 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 titles, prompt text, page URLs and answer bodies |
| `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_<your key>"
```

```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": "pg_44a1", "title": "acme.com/pricing", "snippet": "…", "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}`.

<Note>
  **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.
</Note>

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/{area}

<Info>Read-only. No credits. MCP tool: `get_analytics`.</Info>

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`                                                           |
| `limit`   | query | integer | 20            | Maximum 100                                                               |
| `filter`  | query | string  | —             | Free-text filter on row labels — a topic name, a platform, a URL fragment |

| `area`       | One row per                      | Answers                                              |
| ------------ | -------------------------------- | ---------------------------------------------------- |
| `visibility` | the workspace                    | Share of voice per platform — the orienting call     |
| `prompt`     | tracked prompt                   | Which prompts you win and lose                       |
| `page`       | page of yours                    | Which of your pages get cited                        |
| `source`     | external domain                  | Which domains the models cite in your category       |
| `competitor` | rival                            | Workspace-wide share of voice per competitor         |
| `topic`      | prompt topic                     | Per-topic rollup                                     |
| `platform`   | AI answer surface                | Per-platform rollup                                  |
| `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_<your key>"
```

```json Response theme={null}
{
  "area": "prompt",
  "days": 30,
  "rows": [
    {
      "id": "prm_8f21",
      "label": "best crm for small teams",
      "visibility": { "value": 0.12, "n": 25 },
      "mention": { "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 } },
        "claude":  { "tracked": false, "readable": false }
      }
    }
  ]
}
```

`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.

<Note>
  `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.
</Note>

### GET /v1/prompts/{prompt}

<Info>Read-only. No credits. MCP tool: `get_prompt`.</Info>

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_<your key>"
```

```json Response theme={null}
{
  "id": "prm_8f21",
  "text": "best crm for small teams",
  "topic": "buying intent",
  "country": "US",
  "visibility": { "value": 0.12, "n": 25 },
  "delta": { "value": -0.05, "real": true },
  "platforms": {
    "chatgpt": { "tracked": true, "readable": true, "rate": { "value": 0.2, "n": 15 },
                 "delta": { "value": -0.03, "real": false } }
  },
  "competitors": [{ "name": "rival.com", "rate": { "value": 0.6, "n": 25 } }],
  "sources": [{ "url": "https://g2.com/categories/crm", "rate": { "value": 0.44, "n": 25 } }]
}
```

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

<Info>Read-only. No credits. MCP tool: `get_page`.</Info>

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_<your key>"
```

```json Response theme={null}
{
  "url": "https://acme.com/pricing",
  "title": "Pricing — Acme",
  "citation": { "value": 0.04, "n": 120 },
  "delta": { "value": null, "real": false },
  "platforms": { "perplexity": { "tracked": true, "readable": true, "rate": { "value": 0.09, "n": 60 } } },
  "prompts": [{ "id": "prm_8f21", "text": "best crm for small teams", "rate": { "value": 0.08, "n": 25 } }]
}
```

If this comes back empty, check `GET /v1/analytics/bot` before rewriting the page — a model cannot cite a page it has never fetched.

***

## Track

### POST /v1/prompts

<Warning>Spends credits. Answers `201`. MCP tool: `add_prompts`.</Warning>

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                                   |
| `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_<your key>" \
  -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" }],
  "skipped": [{ "text": "crm with free tier", "reason": "already tracked" }],
  "credits_spent": 2,
  "credits_remaining": 4198
}
```

`credits_spent` and `credits_remaining` are `null` when your plan does not meter the action. Your plan also caps how many prompts you can hold at once — see [Plans](/account/plans).

### DELETE /v1/prompts/{prompt}

<Warning>Destructive. MCP tool: `remove_prompt`.</Warning>

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

<Info>Answers `201`. No credits. MCP tool: `add_competitor`.</Info>

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", "created": true }
```

`created: false` means the domain was already tracked, so nothing changed. Competitor rates use the same denominator as your own, so adding one never changes your numbers.

***

## Automations

### GET /v1/automations

<Info>Read-only. No credits. MCP tool: `list_automations`.</Info>

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.

### GET /v1/automations/{id}

<Info>Read-only. No credits. MCP tool: `get_automation`.</Info>

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

<Info>Answers `201`. Creating is free; each run it performs spends credits. MCP tool: `create_automation`.</Info>

| Field           | Type      | Default | Notes                                                                                                  |
| --------------- | --------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `name`          | string    | —       | Required                                                                                               |
| `instruction`   | string    | —       | Required. What it should do each run, in plain language                                                |
| `trigger`       | enum      | —       | Required. `manual`, `once`, `daily` or `weekly`. `manual` and `once` never fire on a clock             |
| `tools`         | string\[] | —       | Optional allow-list of tool names it may use. Omit to allow everything the plan includes               |
| `timezone`      | string    | UTC     | Optional IANA zone the schedule is written in, e.g. `Europe/London`                                    |
| `run_at_minute` | integer   | —       | Optional minutes past local midnight, 0–1439. `540` is 09:00. Only meaningful for `daily` and `weekly` |

```bash Shell theme={null}
curl -X POST https://api.attensira.com/v1/automations \
  -H "Authorization: Bearer atn_live_<your key>" \
  -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`.

### POST /v1/automations/{id}/runs

<Warning>Spends credits. Answers `200`. MCP tool: `run_automation`.</Warning>

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_<your key>" \
  -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/{id}

<Warning>Destructive. MCP tool: `delete_automation`.</Warning>

Permanently deletes an automation and its run history. This cannot be undone — confirm with `GET /v1/automations/{id}` first.

```json Response theme={null}
{ "id": "aut_71cd", "deleted": true }
```

***

## Agent

### POST /v1/ask

<Warning>Spends credits. Answers `200`. MCP tool: `ask`.</Warning>

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.

<Note>
  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.
</Note>

### GET /v1/sessions/{id}

<Info>Read-only. No credits. MCP tool: `get_session`.</Info>

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_<your key>"
```

```json Response theme={null}
{
  "id": "ses_9d02",
  "status": "complete",
  "answer": "Your pricing page is cited on Perplexity but not on ChatGPT, because…",
  "events": [{ "type": "fetch", "at": "2026-09-02T08:00:04Z", "detail": "read acme.com/pricing" }]
}
```

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.

***

## Account

### GET /v1/account

<Info>Read-only. No credits. MCP tool: `get_account`.</Info>

The org, workspace, plan, credit balance, which platforms the plan tracks, and what this key may do. No parameters.

```json Response theme={null}
{
  "org": { "id": "org_3a10", "name": "Acme" },
  "project": { "id": "prj_5b22", "name": "Acme", "domain": "acme.com" },
  "plan": { "name": "Growth", "platforms": ["chatgpt", "perplexity"] },
  "credits": { "balance": 4200, "renews": "2026-10-01T00:00:00Z" },
  "scopes": ["read", "write"]
}
```

Call this before reporting any per-platform number: `plan.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. `credits.balance` and `credits.renews` are `null` when the plan does not meter credits.

An endpoint that fails with `403` is missing a scope listed in `scopes`.

### GET /v1/ai-traffic/install

<Info>Read-only. No credits. MCP tool: `get_ai_traffic_install`.</Info>

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.
