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

# Events & lifecycle

> Every event the widget emits, when it fires, and what each payload contains

The widget emits a stream of **lifecycle events** during a call. You
can consume them in three places:

* **Parent page** — via `postMessage` from the iframe (see [Embedding](/guides/click-to-call/embedding))
* **Custom code** — via `window.c2c.on(name, handler)` inside injected
  scripts (see [Custom code](/guides/click-to-call/custom-code))
* **Integrations** — forwarded server-side to your webhooks (see
  [Integrations](/guides/click-to-call/integrations))

## Common payload shape

Every event carries a common envelope:

```ts theme={null}
{
  name: string;            // e.g. "call_started"
  data: {                  // event-specific fields (see below)
    ...
    context: {             // URL context — always included
      [key: string]: string;
    };
  };
  timestamp: string;       // ISO 8601
}
```

<Info>
  The `context` object is the URL context you passed to the widget (see
  [Embedding — URL context](/guides/click-to-call/embedding#url-context)).
  It's attached to every event so downstream systems always have
  correlation data like `orderId` or `customerTier`.
</Info>

## Event catalogue

### `widget_loaded`

Fired once, when the widget finishes initial render. Useful to verify
the widget is actually present on pages where it should be, or to fire
a pageview-equivalent in analytics.

```json theme={null}
{
  "name": "widget_loaded",
  "data": {
    "isPreview": false,
    "context": { "orderId": "A-12345" }
  }
}
```

* `isPreview` — `true` when rendered inside the Call Studio preview,
  `false` in production.

***

### `call_started`

Fired when the user taps the call button and the outbound call request
has been accepted.

```json theme={null}
{
  "name": "call_started",
  "data": {
    "destinationId": "dest_xyz",
    "routingKey": null,
    "hasFormValues": false,
    "context": { "orderId": "A-12345" }
  }
}
```

* `destinationId` — the profile the call is routed to, or `null` for
  the default profile.
* `routingKey` — routing key supplied via the session, if any.
* `hasFormValues` — `true` if the pre-call form was used.

***

### `call_ended`

Fired when the call is terminated cleanly — either the visitor tapped
hang-up or the agent (or IVR / queue) hung up. This is the most
important event for most integrations.

If the call fails because of a network or media issue, you get
[`call_error`](#call_error) instead — `call_ended` is not emitted on
error paths.

```json theme={null}
{
  "name": "call_ended",
  "data": {
    "duration": 187,
    "reason": "user_hangup",
    "context": { "orderId": "A-12345" }
  }
}
```

* `duration` — call length in seconds. Measured from the moment the
  visitor started the call.
* `reason` — one of:

| Value           | Meaning                                 |
| --------------- | --------------------------------------- |
| `user_hangup`   | The visitor tapped hang-up              |
| `remote_hangup` | The agent, IVR, or queue ended the call |

***

### `call_error`

Fired when the call fails to start, or when it drops mid-call because
of a media failure. A spike in these events is your early warning
sign that something is wrong on the infrastructure or network side.

```json theme={null}
{
  "name": "call_error",
  "data": {
    "errorCode": "media_failed",
    "errorMessage": "Media connection lost",
    "context": { "orderId": "A-12345" }
  }
}
```

* `errorCode` — machine-readable label. Currently one of:
  * `media_failed` — the WebRTC media connection dropped
  * other codes may surface depending on the failure path; always
    check `errorMessage` for details.
* `errorMessage` — human-readable description.

***

### `survey_submitted`

Fired when the visitor completes the post-call survey.

```json theme={null}
{
  "name": "survey_submitted",
  "data": {
    "rating": 5,
    "answerCount": 2,
    "hasComment": true,
    "context": { "orderId": "A-12345" }
  }
}
```

* `rating` — average rating across the visitor's answers (1–5).
* `answerCount` — how many survey questions the visitor answered.
* `hasComment` — whether the visitor added free-text feedback.

The actual answers and comment text are stored server-side on the
call record and are available through the console — they are not
included in the event payload to keep events small.

***

### `custom_button_clicked`

Fired when the visitor interacts with a custom button you added in
Call Studio.

```json theme={null}
{
  "name": "custom_button_clicked",
  "data": {
    "label": "Open chat",
    "action": "link",
    "context": { "orderId": "A-12345" }
  }
}
```

* `label` — the label configured on the button in Call Studio.
* `action` — the action type configured (e.g. `link`, `copy`, etc.).

## State transitions

The widget has a finite state machine:

```
idle ──▶ calling ──▶ ended ──▶ survey ──▶ survey_submitted ──▶ closed
              │
              └──▶ error
```

| From → to                     | Triggers event              |
| ----------------------------- | --------------------------- |
| `idle` → `calling`            | `call_started`              |
| `calling` → `ended`           | `call_ended`                |
| `calling` → `error`           | `call_error`                |
| `ended` → `survey`            | (transition only, no event) |
| `survey` → `survey_submitted` | `survey_submitted`          |

## Forwarding to your server

Every event above can be forwarded to your webhook endpoints through
**Integrations**. Unlike `postMessage` or `window.c2c.on` (both
client-only), integrations are delivered server-side with retries and
a circuit breaker.

See [Integrations](/guides/click-to-call/integrations) for setup.

## What's next

<CardGroup cols={2}>
  <Card title="Custom code" href="/guides/click-to-call/custom-code">
    Listen for these events from your own injected scripts.
  </Card>

  <Card title="Integrations" href="/guides/click-to-call/integrations">
    Forward events to your webhooks and servers.
  </Card>
</CardGroup>
