# Proof Agent Docs

Proof gives agents two endpoints to do almost everything: one read, one write. Targets are the
visible text you see in the document, not opaque refs or tokens. You never send a base token.

- Read:  `GET /api/agent/<slug>/v3/document`
- Write: `POST /api/agent/<slug>/v3/edit`

## If Proof Looks Wrong

Use one tool call:

  POST /api/bridge/report_bug

If you already have a document slug, you can also call:

  POST /d/<slug>/bridge/report_bug

Send what you know. The server decides whether the report is complete, enriches it with server traces and document events, creates a GitHub issue when it has enough context, and returns a `fixerBrief` you can hand directly to another agent.

### Transient failure guidance

If a non-destructive read or action fails because of a timeout, slow load, or stale state, retry once. If the retry still fails, report it with raw evidence instead of a summary from memory.

### Detailed issue reporting guidance

If the failure looks transient:

- Retry once if the action was non-destructive and the failure looked like intermittent downtime, a timeout, a slow load, or a stale read.
- If the retry succeeds and the doc fully recovers, treat it as recovery noise unless you saw data loss, a stuck paused state, or the same failure twice on the same slug.
- If the retry still fails, report it with raw evidence instead of summarizing it from memory.
- Include whether the failure came from the homepage, library, shared doc HTML, the v3 read (`v3/document`), the v3 write (`v3/edit`), presence, or event polling.

Report these issues right now:

- A doc stays paused after refresh/reopen and blocks normal reading or writing.
- `v3/document`, shared doc HTML, or the homepage repeatedly times out or returns 5xx for the same slug.
- `v3/edit` writes fail after a fresh read and one safe retry.
- The UI shows blank, stale, duplicated, or rolled-back content after the server said a request succeeded.
- Comments, suggestions, or marks disappear, reappear, or disagree across refreshes or devices.
- Two surfaces disagree about document status, for example the page says paused but writes still apply, or `v3/document` looks fresh while shared HTML is obviously stale.

Usually report these only when they repeat:

- A single slow homepage or library load that succeeds on retry.
- A one-off timeout with no document slug and no persistent symptoms.
- Expected retryable conflicts (`CONFLICT`, `TARGET_NOT_FOUND`, `TARGET_AMBIGUOUS`) that resolve when you retry against `error.current`.
- Brief reconnect jitter where content, comments, and writes recover normally.
The most useful evidence is:
- Exact request URL, method, status, and `x-request-id`
- The document slug, if there is one
- Whether retry changed the outcome
- The raw response body or console/network error text

If the response says `status: "needs_more_info"`, ask the suggested questions and call the same endpoint again.

If you want reference code while debugging or writing up the report, you can optionally inspect the open-source repo:

  https://github.com/EveryInc/proof-sdk

## I Just Received A Proof Link

No browser automation is required. Use HTTP directly (for example, `curl` or your tool's `web_fetch`).

If you received a shared link like:

  https://www.proofeditor.ai/d/<slug>?token=<token>

You can discover the API and read the document in one step using **content negotiation** on that same URL.

Fetch JSON:

  curl -H "Accept: application/json" "https://www.proofeditor.ai/d/<slug>?token=<token>"

Fetch raw markdown:

  curl -H "Accept: text/markdown" "https://www.proofeditor.ai/d/<slug>?token=<token>"

The JSON response includes:
- `markdown` (document content)
- `_links` (read, edit, docs)
- `agent.auth` hints (how to use the token)

### Quick copy/paste flow (token already in the shared URL)

```bash
SHARE_URL='https://www.proofeditor.ai/d/<slug>?token=<token>'
TOKEN='<token>'
SLUG='<slug>'
AGENT_ID='ai:your-agent'

curl -H "Accept: application/json" "$SHARE_URL"
curl -H "Authorization: Bearer $TOKEN" -H "X-Agent-Id: $AGENT_ID" "https://www.proofeditor.ai/api/agent/$SLUG/v3/document"
```

## Auth: Token From URL

If a URL contains `?token=`, treat it as an access token:

- Preferred: `Authorization: Bearer <token>`
- Also accepted: `x-share-token: <token>`

Bearer/share tokens authenticate document access. Presence identity is separate: send `X-Agent-Id: ai:<your-agent>` when joining the doc or posting presence. `by` records authorship on every write.

Authenticate with `Authorization: Bearer <token>` or `x-share-token: <token>`.

## Read The Document

Use:

  GET /api/agent/<slug>/v3/document

One call returns everything you need: document text plus comment and suggestion state.

  curl -H "Authorization: Bearer <token>" -H "X-Agent-Id: ai:your-agent" \
    "https://www.proofeditor.ai/api/agent/<slug>/v3/document"

Response shape:

```json
{
  "ok": true,
  "revision": 12,
  "title": "My Document",
  "markdown": "# Heading\n\nVisible document text...",
  "comments": [
    {
      "id": "comment-123",
      "quote": "the text the comment is anchored to",
      "resolved": false,
      "body": "Root comment body",
      "messages": [
        { "by": "human:reviewer", "text": "Root comment body", "root": true },
        { "by": "ai:your-agent", "text": "Fixed in this reply.", "resolvedHere": true }
      ]
    }
  ],
  "suggestions": [
    { "id": "mark-456", "kind": "replace", "quote": "old text", "content": "new text", "status": "pending" }
  ]
}
```

Comment threads are read from `comments[]`, and pending track-changes proposals from `suggestions[]`.
Each comment's `messages[]` is the chronological thread, with the root comment as `messages[0]` and
replies after it; a reply that resolved the thread carries `resolvedHere: true`. The top-level
`resolved` flag reflects the thread's current state. Use a comment's `id` as the reply/resolve target.

`revision` is an integer that increases on every change. It is optional input to the write endpoint
(see `baseRevision` below); you never need to read or send a base token. The read also returns
`mutationReady`: on a rare degraded read where the authoritative base is not yet resolved it is `false`
and `revision` is `null` — treat that as "revision unknown", omit `baseRevision`, and re-read shortly.

## Edit The Document

Use:

  POST /api/agent/<slug>/v3/edit

Send a `by` plus an `operations` array. The server resolves every target against the live document, so
you do not send a base token. `baseRevision` (the integer from `v3/document`) is optional — include it
only when you want a conflict guard. `Idempotency-Key` is optional.

```bash
curl -X POST "https://www.proofeditor.ai/api/agent/<slug>/v3/edit" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <token>" \
  -H "X-Agent-Id: ai:your-agent" \
  -d '{
    "by": "ai:your-agent",
    "operations": [
      { "op": "replace", "find": "old visible text", "with": "new text" },
      { "op": "insert", "after": "heading:Background", "markdown": "## Scope\n\nNew section." },
      { "op": "comment", "on": "text to anchor on", "body": "Is this still accurate?" }
    ]
  }'
```

On success the response echoes the post-edit document so you can chain without re-reading:

```json
{
  "ok": true,
  "applied": 3,
  "results": [ { "ok": true, "kind": "content", "applied": 2 }, { "ok": true, "kind": "comment", "id": "comment-789" } ],
  "revision": 13,
  "markdown": "...post-edit document...",
  "comments": [],
  "suggestions": []
}
```

### Operations

One `operations` array carries both content and review ops. Content ops apply first as one atomic batch;
review ops then apply in array order.

Content:
- `replace` — `{ find, with, occurrence?, before?, after? }`
- `insert` — `{ after | before, markdown }` where the anchor is a visible-text quote, `heading:Title`, `section:Title` (before/after a whole section, subsections included), `"start"`, or `"end"`
- `delete` — `{ find }`
- `set_document` — `{ markdown }` replaces the whole document, applied as a minimal block diff and safe with live collaborators

Review:
- `comment` — `{ on, body }`
- `reply` — `{ comment, body, resolve? }`
- `resolve` — `{ comment }`
- `unresolve` — `{ comment }`
- `suggest` — `{ kind: "insert" | "delete" | "replace", find, with? }` (`with` is required for `insert`/`replace`, omitted for `delete`)
- `accept` — `{ suggestion }`
- `reject` — `{ suggestion }`

`find` and `on` match the **visible text** in `markdown`, not raw markdown syntax (no `**bold**`
markers or list bullets). Disambiguate a repeated phrase with `occurrence` (a number, `"first"`, or
`"last"`) or with `before`/`after` context.

### Errors

Every failure uses one envelope:

```json
{ "ok": false, "error": { "code": "CONFLICT", "message": "...", "retryable": true, "opIndex": 0, "target": "...", "candidates": [], "current": { "...": "fresh document" } } }
```

`code` is one of a closed set: `AUTH`, `NOT_FOUND`, `INVALID_REQUEST`, `TARGET_NOT_FOUND`,
`TARGET_AMBIGUOUS`, `CONFLICT`, `TOO_LARGE`, `BUSY`, `PENDING`, `INTERNAL`.

- `retryable: false` (e.g. `AUTH`, `NOT_FOUND`, `INVALID_REQUEST`, `TOO_LARGE`) — fix the request; do not blindly retry.
- `retryable: true` (e.g. `TARGET_NOT_FOUND`, `TARGET_AMBIGUOUS`, `CONFLICT`) — the response embeds the fresh document as `error.current`; re-resolve your targets against it and retry in one shot.
- `BUSY` — the authoritative base is settling; back off briefly and retry.
- `opIndex` points at the failing operation; `target` echoes the quote/anchor that did not resolve.
- `TARGET_AMBIGUOUS` (and an out-of-range `occurrence`) include `error.candidates` — an array of `{ occurrence, snippet }` whose snippet marks the match with «guillemets». Pick one with `occurrence` (0-based) or `before`/`after` context.
- A mixed request applies content ops first (one atomic batch), then review ops in order. If a review op fails *after* content already committed, the response is `ok: false` with `partial: true`, `applied` (count committed), and `results` (the committed ops) alongside `error`. Re-read, then retry only the failed op — do not blindly resend the whole request (a same-`Idempotency-Key` retry safely replays this response instead of re-applying).

A `202` (`PENDING` semantics) means the write committed and live convergence is still settling; the body
includes `pending: true`. Re-read `v3/document` to confirm.

## Update Title Metadata

Use:

  PUT /api/documents/<slug>/title

Example:

  curl -X PUT "https://www.proofeditor.ai/api/documents/<slug>/title" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <token>" \
    -d '{"title":"Updated document title"}'

Authenticate with `Authorization: Bearer <token>` or `x-share-token: <token>`.

## Delete A Document

Use the lifecycle endpoint only when you hold the owner credential:

  DELETE /api/documents/<slug>

Authenticate with the `ownerSecret` returned by document creation. Same-origin browser clients may also use an authenticated Every owner session. Viewer, commenter, and editor access tokens cannot delete documents. A successful delete returns `shareState: "DELETED"`; future reads return `410`.

Example:

  curl -X DELETE "https://www.proofeditor.ai/api/documents/<slug>" \
    -H "x-share-token: <ownerSecret>"

## Presence And Event Polling

Presence and events are how a long-lived agent shows activity and notices changes. They are optional
for one-shot read/edit flows.

Show presence (identity via `X-Agent-Id`):

  curl -X POST "https://www.proofeditor.ai/api/agent/<slug>/presence" \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer <token>" \
    -H "X-Agent-Id: ai:your-agent" \
    -d '{"agentId":"your-agent","status":"reading","summary":"Joining the doc"}'

Presence identity can be supplied as `X-Agent-Id`, `agentId`, or `agent.id`. `by` is not used for presence.

Poll for changes:

  GET /api/agent/<slug>/events/pending?after=<cursor>&limit=100

Subscribe to new live events:

  GET /api/agent/<slug>/events/stream

Pass `after=<cursor>` or `Last-Event-ID: <cursor>` only when you intentionally want replay. Without a cursor, the stream starts with new events created after connection.
Event frames include `id:`, `event: <type>`, and JSON `data:`. The stream sends heartbeat comments and closes periodically; planned closes include an id-only `event: cursor` frame so clients can reconnect with the last seen `id`.
`text.settled` means visible text changed through live collaboration and the persisted state has settled. Treat it as a prompt to re-read `v3/document` and decide whether to act; it does not carry the edited text. Events are an activity signal, not the source of comment thread text — read `comments[]` from `v3/document` for that.

Ack processed events (editor/owner):

  POST /api/agent/<slug>/events/ack
  Body: {"upToId": <cursor>, "by": "ai:your-agent"}

## Create A New Shared Doc

If you need to create a share from scratch, use:

  POST /share/markdown

This is the canonical public create route.

```bash
curl -X POST "https://www.proofeditor.ai/share/markdown" \
  -H "Content-Type: application/json" \
  -d '{"title":"My Document","markdown":"# Hello\n\nFirst draft."}'
```

Save `slug`, `accessToken`, `ownerSecret`, `shareUrl`, and `tokenUrl` from the response.

## Recommended Workflow: Changing An Existing Doc

1. Read once with `GET /api/agent/<slug>/v3/document`. Note the `markdown` you want to change.
2. Send one `POST /api/agent/<slug>/v3/edit` with the operations you need. Quote the visible text exactly.
3. The response returns the post-edit document — inspect it instead of re-reading.
4. If you get a retryable error, re-resolve against `error.current` and retry once.

## Troubleshooting

### `web_fetch` / `WebFetch` cannot access `proofeditor.ai`

If your tool reports a blocked or disallowed domain, ask the user to allow `proofeditor.ai` in the assistant's **Capabilities** settings (Web access allowed domains), then retry the same request.

### `TARGET_NOT_FOUND` or `TARGET_AMBIGUOUS`

Your `find`/anchor quote must match the visible text in `markdown`. Re-read `v3/document` (or use the
`error.current` already in the response), copy the exact visible text, and disambiguate repeats with
`occurrence` or `before`/`after`.

### `CONFLICT`

A targeted region changed under you. The fresh document is in `error.current`; re-resolve your targets
against it and retry. You do not need a base token.

### `BUSY`

The authoritative base is briefly unavailable while collaboration settles. Retry with a short backoff.
