# Events and the protocol

What the embed tells your page, when, and with what, and the messages underneath.

Listen with `onEvent` at mount, or `embed.on(type, listener)` for one type, `"*"` for all. Every event is a flat object with a `type`. Ids are the installation’s, except `account.id` and `user.id`, which are yours. An event carries only what your server could already read through the Partner API: never a token or a run’s data.

```js
const embed = await GoRunnerEmbed.mount({
  container: "#automations",
  getCode,
  onEvent: (event) => console.log(event.type, event),
})

embed.on("run.failed", ({ run, workflow, message }) => {
  notify(`${workflow.name} failed: ${message ?? ""}`)
})
```

Events tell your page what happens in the open embed. Your server hears about accounts whether or not anyone has them open, through [notifications](https://docs.gorunner.io/embed/notifications).

## Every event

| Event | Fields | When |
| --- | --- | --- |
| **The embed** |   |   |
| `ready` | `account: { id, name }`, `user: { id, name }`, `role`, `path`, `page` | Signed in and showing its first page: when `mount()` resolves, after the frame reloads, and after `switchAccount()`. |
| `error` | `code`, `message`, `fatal` | Something went wrong after mounting. See [The error event](https://docs.gorunner.io/embed/events#error). |
| `session.renewed` | `expiresAt` | The session was renewed without the person noticing, by itself or with a new code. It ends 30 minutes after it began. |
| **Where it is** |   |   |
| `route.changed` | `path`, `page`, `title`, `replace` | The person moved inside the embed. A path the protocol can’t name isn’t told. `page` can go back to `mount()` or `navigate()`. The embed never writes your page’s history: mirror it yourself for Back and reload. |
| `title.changed` | `title`, `parent?` | The page’s name changed, such as a workflow once it loads, with the list it belongs to. |
| `back` | `path`, `page` | The person pressed the embed’s back arrow. With `onBack`, your page decides where to go; without it, the frame goes back by itself. |
| `unsaved.changed` | `unsaved`, `workflow: { id }` or `null` | The open workflow gained or lost unsaved changes. Use it to warn before your page navigates away. `embed.unsaved` holds the same. |
| **Workflows** |   |   |
| `workflow.created` | `workflow: { id, name }` | Made new, duplicated, imported, or from a template. |
| `workflow.saved` | `workflow: { id, name }` | Changes saved. |
| `workflow.published` | `workflow: { id, name }` | Published, including a template made with **Create and publish**. |
| `workflow.unpublished` | `workflow: { id, name }` | Switched off. |
| `workflow.deleted` | `workflow: { id }` | Deleted. |
| **Runs** |   |   |
| `run.started` | `run: { id }`, `workflow`, `kind` | A run was started from the embed. `kind` is `test`, `production`, `again`, `from_step`. |
| `run.finished` | `run: { id, status, durationMs? }`, `workflow` | A run started from the embed ended: `completed` or `cancelled`. |
| `run.failed` | `run: { id, durationMs? }`, `workflow`, `step?`, `message?` | A run started from the embed failed, at `step`, because of `message`, when known. |
| **Connections** |   |   |
| `connection.created` | `connection: { id, name, piece }` | A new connection is ready, from anywhere in the embed or your page’s `connect()`. Signing one in again fires nothing. |
| `connection.removed` | `connection: { id }` | A connection was deleted. |

Workflow and run events come from one place in the frame, whoever pressed the button: the person, the assistant or a template. So each happens once.

The frame watches a run it started for up to 30 minutes: every 2 seconds for the first 2, then every 10. A run waiting for an approval or a delay is still running. A run that ends later isn’t told here.

## The error event

`fatal: true` means the embed can go no further: its state becomes `ended`, and it shows the person why. `fatal: false` is worth knowing while the embed carries on. Branch on `code`; `message` is for a developer.

| `code` | Fatal | What happened |
| --- | --- | --- |
| `account_suspended` | No | Everything can be read; nothing can change or run. |
| `not_available` | No | A page the person reached isn’t available to them here: switched off, or its record couldn’t be loaded. |
| `session_failed` | No | A renewal failed: your page didn’t hand over a new code in time. The frame offers to try again. |
| `code_expired` | Either | The code was used or too old, twice. During a renewal the frame carries on; on a reload it is fatal. |
| `access_ended` | Yes | The person no longer has access to this account. |
| `licence` | Yes | The license no longer includes Embed, or the installation is paused. |
| `unavailable` | Yes | The installation runs with sign-in turned off, or no page is switched on for this person any more. |
| `code_failed` | Yes | Signing in again, after a reload or an account switch, your `getCode` failed. |
| `unreachable` | Yes | Signing in again, the frame couldn’t reach the installation. |
| `version_mismatch` | Yes | After an upgrade, the frame and the SDK share no protocol version. |
| `timeout` | Yes | Signing in again, the frame didn’t answer in time. |
| `crashed` | Yes | The frame hit an error it couldn’t recover from. It offers the person a reload. |

`not_allowed_here`, `container_not_found`, `invalid_options` and `unmounted` reach you as a [`mount()` rejection](https://docs.gorunner.io/embed/sdk#errors), not as this event. So does `not_available` when the `page` you named at mount is switched off.

## The protocol

Your page and the frame talk only through `postMessage`, in `gorunner-embed/1`. The SDK speaks it for you.

- **One envelope**: `{ protocol: "gorunner-embed/1", id, type, payload }`, both ways.
- **Exact origin, exact window**: Each side reads a message only from the exact origin and window it expects, and never posts to `"*"`. The frame answers only a page on the allowed websites.
- **Checked and bounded**: Every message is checked against its shape before it is read. One over 262,144 characters as JSON is refused unread.
- **Nothing widens access**: Your page can turn things off and move between pages it may show. No message turns anything on.
- **The messages**: The frame sends `ready`, `authenticated`, `configured`, `failed`, `event`, `request`, `reply`. Your page sends `init`, `command`, `reply`; the commands are `navigate`, `connect`, `setTheme`, `setAppearance`, `turnOff`, `teardown`.
- **What version 1 promises**: The envelope, the message types, the handshake order, how a reply is matched, and the meaning of every code never change. Anything else may only be added, and both sides ignore what they don’t know.

Related: [SDK](https://docs.gorunner.io/embed/sdk) · [Notifications](https://docs.gorunner.io/embed/notifications) · [Error codes › SDK](https://docs.gorunner.io/errors?kind=sdk) · [Versions and upgrades](https://docs.gorunner.io/embed/versions)

Source: https://docs.gorunner.io/embed/events
