---
title: "Events"
description: "Get told when a document changes, whoever changed it, and keep your own state in sync."
source: "https://www.embedpdf.com/docs/engine/concepts/events"
---

# Events

Every change to a document produces an event: an annotation created, a page rotated, a field
filled in. Listen to them to keep your interface in sync without reading the document again.

```ts
const stop = doc.events.on('annotations.created', (event) => {
  addToSidebar(event.annotation);
});

// later
stop();
```

`on()` returns a function that stops listening. To hear every event, use `subscribe()`:

```ts
const stop = doc.events.subscribe((event) => {
  console.log(event.type);
});
```

## What an event contains

An event carries exactly what the call that made the change returned, plus its `type` and where
it came from (`origin`). The event for `create()` has the same `annotation` that `create()`
resolved with:

```ts
doc.events.on('annotations.updated', ({ annotation, origin }) => {
  if (origin.kind === 'remote') showToast(`${annotation.author} changed a comment`);
});
```

| Field           | What it is                                                                        |
| :-------------- | :-------------------------------------------------------------------------------- |
| `origin.kind`   | `'local'` when the change was made through this `doc`, `'remote'` when it wasn't. |
| `origin.sub`    | The user who made it (on the cloud).                                              |
| `origin.ts`     | When it happened, in milliseconds.                                                |
| `origin.tx`     | The write it came from: its `opId`, and its place among that write's events.      |
| `origin.undoOf` | On an undo's events: the `opId` of the change it undid.                           |

An event fires after the change is saved, never before, and before the call's promise resolves.
By the time `await page.annotations.create(…)` returns, your listeners have run.

## Every event

| Type                                                                      | When                                                                                      |
| :------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------- |
| `annotations.created`, `.updated`, `.deleted`                             | An annotation changed.                                                                    |
| `annotations.reordered`                                                   | A page's annotations were restacked. `order` is the page's new order, bottom to top.      |
| `annotations.restored`                                                    | An undo brought back deleted annotations.                                                 |
| `annotations.flattened`                                                   | Annotations were baked into a page.                                                       |
| `pages.reordered`, `.rotated`, `.deleted`, `.inserted`                    | The page order or rotation changed.                                                       |
| `pages.named`                                                             | A page name was added or removed.                                                         |
| `pages.flattened`                                                         | All annotations of pages were baked in.                                                   |
| `pages.scaleSet`                                                          | A page's measurement scale changed.                                                       |
| `forms.valueSet`                                                          | A field was filled in.                                                                    |
| `forms.created`, `.updated`, `.deleted`, `.widgetAdded`, `.widgetRemoved` | A field's structure changed.                                                              |
| `forms.widgetUpdated`, `forms.widgetsReordered`                           | A widget was moved or restyled, or a page's widgets were restacked.                       |
| `forms.widgetDeleted`, `forms.widgetRestored`                             | A widget was deleted, or an undo brought it back.                                         |
| `forms.calculationsReordered`                                             | The order calculate scripts run in changed.                                               |
| `forms.restored`                                                          | An undo brought back a deleted field.                                                     |
| `forms.repaired`                                                          | Many values or fields changed at once.                                                    |
| `attachments.created`, `.deleted`                                         | An attached file was added or removed.                                                    |
| `metadata.updated`                                                        | The title, author or another standard metadata field.                                     |
| `metadata.customUpdated`                                                  | Your own metadata keys.                                                                   |
| `redaction.applied`                                                       | Redactions were applied.                                                                  |
| `signatures.prepared`, `.completed`, `.cancelled`                         | A signature was started or finished.                                                      |
| `document.versioned`                                                      | A new version of the document was saved, such as after signing. Read what you show again. |
| `stream.desynced`                                                         | (cloud) Events were missed. Read what you show again.                                     |

Changes that touch one page (annotations) carry that `page`, so you can update only what's on
screen.

## Which write an event came from

Every write takes an `opId` in its options, a name for the write, and every event it fires carries
it as `origin.tx.id`. Leave it out and the engine makes one up. That's how you match an event to a
change you made:

```ts
const opId = crypto.randomUUID();
doc.events.on('annotations.updated', ({ origin }) => {
  if (origin.tx?.id === opId) markSaved();
});
await page.annotations.update(ref, { contents: 'Seen' }, { opId });
```

An `opId` is 1 to 255 visible ASCII characters (what an HTTP header can carry), on every engine.
On the cloud, a write retried with the same `opId` is applied once, and the retry gets the first
answer. The cloud engine retries a write lost on the network itself, under the same `opId`, so use
a fresh `opId` for every write. A [change](https://www.embedpdf.com/docs/engine/documents/changes#asking-again), and the
annotation, form and metadata calls a change can hold, do the same on the local engine.

The `opId` is also how you undo a write: `doc.apply({ undoOf: opId })`. See
[Changes & undo](https://www.embedpdf.com/docs/engine/documents/changes).

Some calls make many changes at once. An [import](https://www.embedpdf.com/docs/engine/annotations/import-export) of 50
annotations fires 50 `annotations.created` events, one after the other, all with the same
`origin.tx`:

```ts
doc.events.on('annotations.created', ({ annotation, origin }) => {
  addToSidebar(annotation);
  if (origin.tx && origin.tx.index === origin.tx.count - 1) redrawSidebar(); // the last of the batch
});
```

`tx.id` is the same for all of them, and `tx.index` counts from `0` to `tx.count - 1`. A write
that makes one change fires one event, with `tx.index` `0` and `tx.count` `1`.

## Who hears what

The local engine tells you about changes made through this engine. Two tabs with the same file
are two separate copies: a change in one doesn't reach the other.

> **Live changes from everyone**
>
> With CloudPDF, everyone who has the document open hears every change as it's saved, with
> `origin.kind: 'remote'` and the user who made it. Add a comment in one browser and it appears in
> the others, through the same `events.on()` you already use.
>
> [Collaborate with CloudPDF](https://www.cloudpdf.com/docs/engine/concepts/events)

## Good to know

- **Each change arrives once.** Your own changes aren't sent back to you a second time.
- **A listener that throws** doesn't stop the other listeners, and doesn't make the call fail.
- **`origin.kind` is about the open document, not the user.** "Did I do this?" (undo, optimistic
  UI): check `origin.kind`. "Did this user do it?": check `origin.sub`.
