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

# Events

> Everything that happens to a document, for stream, /events and webhooks.

Everything that happens to a document is an **event**. The same events serve three purposes:

* the **stream mode** of `POST /documents`;
* reconnecting to or following a document with `GET /documents/{id}/events`;
* **webhooks** (final events only; coming soon).

## Event shape

```json theme={null}
{
  "id": "evt_01JAB3K9Z4Q8W2M7V6X5T1R0NP",
  "type": "content.block_completed",
  "version": 1,
  "document_id": "doc_01JAB3H8K2M7V6X5T1R0NPQ4ZW",
  "sequence": 14,
  "timestamp": "2026-10-02T09:14:03.512Z",
  "display": { "es": "Escribiendo…", "en": "Writing…" },
  "data": { }
}
```

| Field | Meaning |
| - | - |
| `id` | Unique. Use it to avoid processing the same webhook twice. |
| `type` | What happened. **If the client doesn't know a type, it ignores it**: that way we can add new types without breaking anyone. |
| `version` | Version of the `data` format for that type. |
| `sequence` | 1, 2, 3… with no gaps within each document. It is the SSE `id:`. |
| `display` | Short text ready to show ("Reviewing…"). Optional. |
| `data` | The details. Only `content.*` and `edit.*` events carry document content. |

## Types

**Lifecycle**

| Type | `data` |
| - | - |
| `document.created` | `{ operation, format }` |
| `document.started` | `{ attempt }` |
| `document.ready` ⏹ | `{ status: ready·ready_with_warnings·needs_review, pages, validation_summary }` |
| `document.failed` ⏹ | `{ error: { code, message, retryable } }` |
| `document.canceled` ⏹ | `{}` |

⏹ = final event: nothing else arrives after it.

**Content (rendered live)**

| Type | `data` | Stored? |
| - | - | - |
| `style.resolved` | `{ style, unsupported[] }`: the understood style, to render with it from the start | yes |
| `content.block_started` | `{ block_id, index, type, level? }` | yes |
| `content.block_delta` | `{ block_id, text }`: new text (batched every \~150 ms) | yes |
| `content.block_completed` | `{ block_id, block }`: complete block in Content IR | yes |
| `edit.planned` | `{ operations, unsupported }`: how many changes will be applied and what can't be done (only in `edit`) | yes |
| `edit.operation_applied` | `{ op, block?, target?, before?, after? }`: an applied change (only in `edit`) | yes |
| `edit.operation_rejected` | `{ op, block?, reason }`: a change that couldn't be applied, and why | yes |

**Process**

| Type | `data` |
| - | - |
| `render.completed` | `{ iteration, pages, duration_ms }` |
| `validation.issue_found` | `{ code, severity, message, location, fixable }` |
| `validation.completed` | `{ iteration, errors, warnings, info }` |
| `repair.applied` | `{ iteration, fix, issue_code }` |
| `repair.rejected` | `{ iteration, fix, issue_code, reason }` |
| `preview.page_ready` | `{ page, total, output_id, width, height }`: real image of a page |
| `output.available` | `{ output_id, kind: docx·pptx·xlsx·pdf·preview·html, bytes }` (the URL is requested separately or comes in the final record) |

## Typical order

**Create from a sentence**

```
document.created → document.started → style.resolved
→ content.block_started → content.block_delta… → content.block_completed   (× each block)
→ render.completed → validation.* → repair.* (if needed)
→ preview.page_ready (× pages) → output.available (Word/PowerPoint/Excel and PDF) → document.ready
```

* **Create from text**: the same, but the blocks arrive almost all at once.
* **Review**: no `content.*`: render, review, fixes, pages and final.
* **Edit**: `edit.planned`, one `edit.operation_applied` (or `_rejected`) per change, then the same as review.

**How to render it on the client**: with `style.resolved` you set up the styles; with `content.*` you build the view (approximate, without pages); with `preview.page_ready` you replace it with the real pages.

## SSE (stream and `/events`)

```
Content-Type: text/event-stream

id: 13
event: content.block_started
data: {"id":"evt_…","type":"content.block_started","sequence":13,…}

: heartbeat
```

* **Reconnect**: `GET /documents/{id}/events` with the header `Last-Event-ID: 13` (or `?after=13`). First the later stored events are resent (the complete blocks), then it continues live. The document keeps being processed even if the client disconnects.
* Heartbeat (`: heartbeat`) every 15 s so no proxy cuts the connection.
* After the final event, the server closes the connection.
* Events are kept for 7 days. After that, `GET /documents/{id}/events` responds `410 events_expired`, though the document record is still available.

## Webhooks (coming soon)

* Same event format. By default only the final ones (`document.ready`, `document.failed`, `document.canceled`). **Never events with content.**
* Signed per **Standard Webhooks**: headers `webhook-id`, `webhook-timestamp` and `webhook-signature` (HMAC-SHA256 with a `whsec_…` secret).
* Retries for \~10 h with increasing delays (5 s, 30 s, 2 min, 10 min, 30 min, 1 h, 3 h, 6 h). The client must deduplicate by `webhook-id`.
* Public HTTPS URLs only (SSRF protection).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.