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

> ## Agent Instructions
> Arcmira has three API capabilities: Search (entities, mentions, appearances, commercial intelligence), Monitors (trackers plus alert delivery), and Transcripts (read, generate, correct). Community Review is the cross-surface correction layer.
> Discover pages at https://docs.arcmira.com/llms.txt, then fetch the matching page's .md export. Each capability has a self-contained '<capability> for coding agents' page; prefer it.
> Auth: Authorization: Bearer arc_sk_... or x-api-key: arc_sk_... (x-api-key wins if both are sent). Never put keys in browser code. Check GET /v1/me for tier, scopes, and remaining rows before metered pulls.
> Search: resolve names with GET /v1/entities/lookup and pin ent_* IDs before pulling metered rows. Only person entities have appearances. Mention and recommendation rows carry canonical numeric timestamps in start_seconds/end_seconds (integer seconds; 0 means full episode); the MM:SS string fields are deprecated. Commercial routes need Pro+ tier AND the recommendations:read scope and bill 10 rows per row.
> Monitors: a tracker watches one entity; a monitor groups trackers with shared delivery (email, Slack, HMAC-signed webhooks). Create trackers with POST /v1/trackers, then attach by id: POST /v1/monitors/{id}/trackers { trackerIds: [...] }. Each fired alert occurrence debits 100 rows. Poll GET /v1/monitors/{id}/alerts?n= for recent deliveries. Send Idempotency-Key on every POST/PATCH.
> Transcripts: switch on the access field (unlocked, locked, premium_pending, not_transcribed, unauthenticated). Honor Retry-After when polling POST /v1/transcriptions jobs. Corrections (POST /v1/videos/{video_id}/corrections; kinds line_edit, speaker_reassign, speaker_identify, add_person, entity_tag, segment_rewrite) cost 0 rows; anchored kinds need revision + anchor.contentHash (djb2 base-36); handle 409 (re-anchor) and 412 (expectedSeq). segment_rewrite replaces an inclusive segment range with new segments (empty replacements array deletes; timestamps repaired server-side).
> Community Review is free (0 rows) and surface-typed: POST /v1/feedback with a type matching the surface you called, the reproducing query, and corrections targeting public IDs (ent_*, men_*, com_*). Nothing auto-applies. Do not send untyped notes.
> Errors: switch on error.code, follow doc_url, quote X-Request-Id to support. Honor Retry-After on 429. Retries of POST/PATCH must reuse the same Idempotency-Key; replays return Idempotency-Replayed: true.

# Requests & conventions

> The contracts shared by every endpoint: cursor pagination, idempotent writes, public ID schemes, headers, and versioning.

Conventions that hold across the whole API, so you learn them once.

## Public IDs

| Resource       | Form               | Notes                                                                            |
| -------------- | ------------------ | -------------------------------------------------------------------------------- |
| Entity         | `ent_123881`       | Merge-aware: old IDs keep resolving; lookups surface `merged_from_id`            |
| Mention        | `men_2049815`      | What you pass to Community Review for mention and appearance rows                |
| Commercial row | `com_119682`       | What you pass to Community Review for reclassification                           |
| Monitor        | `mon_abc123`       | Rows minted before the Monitors rename carry a legacy `wl_` prefix; both resolve |
| Tracker        | `trk_9f2ab4c8d1e6` |                                                                                  |

IDs are opaque and stable: rely on the prefix, never parse the suffix. Alert-history rows carry public forms (`entity_id` as `ent_*`, `mention_id` as `men_*`) alongside numeric `media_id`/`appearance_id`, which stay numeric everywhere in the API.

## Pagination

List endpoints use **cursor pagination**. No total counts: fast, stable, infinite-scroll-friendly.

| Parameter | Description                                                                      |
| --------- | -------------------------------------------------------------------------------- |
| `limit`   | 1-100, default 20                                                                |
| `cursor`  | Opaque token from a previous response's `next_cursor`; omit on the first request |

```json theme={null}
{
  "data": [ ],
  "has_more": true,
  "next_cursor": "eyJvZmZzZXQiOjI1fQ"
}
```

When `has_more` is `false`, `next_cursor` is `null`; stop. Cursors are opaque: pass them back verbatim, never construct or decode them. If the underlying ordering changes (rare), an old cursor may include duplicates or gaps; restart from `null` for a strict snapshot.

<CodeGroup>
  ```typescript TypeScript theme={null}
  async function* list(url: URL, headers: HeadersInit) {
    let cursor: string | null = null;
    do {
      if (cursor) url.searchParams.set('cursor', cursor);
      const page = await fetch(url, { headers }).then(r => r.json());
      yield* page.data;
      cursor = page.has_more ? page.next_cursor : null;
    } while (cursor);
  }
  ```

  ```python Python theme={null}
  def list_all(client, url, params, headers):
      cursor = None
      while True:
          if cursor:
              params['cursor'] = cursor
          body = client.get(url, params=params, headers=headers).json()
          yield from body['data']
          if not body['has_more']:
              return
          cursor = body['next_cursor']
  ```
</CodeGroup>

**Documented exceptions**:

* Alert history (`GET /v1/monitors/{id}/alerts`, `GET /v1/trackers/{id}/alerts`) takes `?n=` (1-100, default 25) and always returns `has_more: false`. It is a recent-deliveries window, not a paginated archive.
* Person appearances (`/v1/people/{slug}/appearances`) and the related-entity lists (`/v1/{type}/{slug}/{topics|people|...}`, `/v1/channels/{slug}/guests`) return `{ items, total, offset, limit, hasMore }` and no cursor, so they serve one page. For paginated appearance evidence use `/v1/mentions?entity_id=...&is_appearance=true`.
* Discovery search (`/v1/entities/search`) is a single ranked page: `limit` 1-25, default 10, no cursor. Channel sponsors (`/v1/channels/{id}/sponsors`) is a single rollup: `limit` 1-200, default 100, no cursor.

## Idempotency

Every `POST` and `PATCH` accepts an `Idempotency-Key` header. Send one on **every** mutation; a UUID v4 per logical operation is the safe default.

1. Arcmira processes the request and caches the response (body, status, and a hash of the request body) against your key for **24 hours**, scoped per API key.
2. A retry with the same key returns the cached response with `Idempotency-Replayed: true`.
3. Reusing a key with a **different** body returns `409 idempotency_conflict`: mint a new key for the new operation.

```bash theme={null}
curl -X POST 'https://api.arcmira.com/v1/monitors' \
  -H "Authorization: Bearer $ARCMIRA_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Competitor watch" }'
```

Caveats: `GET`/`DELETE` don't take idempotency keys (already idempotent); the cache may not outlive 24 hours, so long retry windows should mint fresh keys and handle the resulting conflicts; on transcript corrections, only final outcomes are cached and a `412` never replays.

## Headers on every response

| Header                                      | Description                                                                                                                 |
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `X-Request-Id`                              | Correlation ID; same value as `error.request_id` on failures. Log it; quote it to support                                   |
| `X-Arcmira-Version`                         | API version serving the request                                                                                             |
| `RateLimit-Limit` / `-Remaining` / `-Reset` | Per-key throttle state, on every response past the key check; see [Usage, limits & billing](/usage-and-billing#rate-limits) |
| `Retry-After`                               | On 429, and on in-flight transcription polls: seconds to wait. Always honor it                                              |
| `Idempotency-Replayed`                      | `true` when a successful response came from the idempotency cache                                                           |

## Versioning and deprecation

* The API is versioned in the path (`/v1`). Additive changes (new fields, new endpoints) ship without notice; **removing or renaming** anything gets a `deprecated` marker in the OpenAPI spec with migration prose, a [changelog](/changelog) entry, and a deprecation window.
* Build tolerant readers: ignore unknown fields, and handle unknown enum values gracefully (new event names, statuses, and error codes will appear).
* The live OpenAPI 3.1 document is at [`GET /v1/openapi.json`](https://api.arcmira.com/v1/openapi.json) and mirrors this site's [API reference](/api-reference/meta/health-check).
