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

# Event types

> The events a webhook can listen to, and the data each event carries.

An event is a fact that ImpelLabs reports: a message arrives, a lead comes in, a booking is made. A
[webhook](https://docs.impellabs.tech/api/webhooks.md) listens to the events you choose. A workflow can start on many of them.

Every delivery has the same envelope. The `type` is the event name and `data` holds the fields of the event.

```json theme={null}
{
  "id": "evt_0123456789abcdef",
  "type": "message.received",
  "created_at": "2026-11-01T09:00:00+00:00",
  "workspace_id": "0b9a7c40-0000-0000-0000-000000000000",
  "data": {
    "case_id": "…",
    "message_id": "…",
    "channel_id": "…",
    "contact_id": "…"
  }
}
```

## Read the live list

The list of your workspace is the source of truth. It can hold more names than the tables below, such as the events
of your installed apps and of your incoming webhooks.

```bash theme={null}
curl https://api.impellabs.tech/api/v1/webhooks/events \
  -H "Authorization: Bearer imp_..."
```

Each item is `{"name": "...", "label": "...", "line": "..."}`. Only an owner or an admin can read it.

When you register a webhook, you choose names from this list. A name that ends in `.*` takes every event that starts
with the text before the star, such as `message.*`. A name that is not in the list returns `400`.

## Events of the integration feed

These events report what happens on your channels and conversations. `data` holds the fields shown.

| Event | When it happens | `data` fields |
| - | - | - |
| `channel.history_synced` | Past chats of a number came in | `channel_id`, `kind`, `address`, `phase`, `progress` |
| `channel.status_changed` | A channel is switched on, paused, fails or is removed | `channel_id`, `kind`, `address`, `status`, `agent_id`, `removed` |
| `conversation.closed` | A conversation closes | `case_id`, `reason` |
| `conversation.started` | A conversation starts | `case_id`, `channel_id`, `contact_id` |
| `handoff.requested` | A conversation asks for a person | `case_id`, `reason` |
| `message.delivered` | A message reached the customer | `case_id`, `message_id`, `outbound_id`, `provider_message_id` |
| `message.failed` | A message did not go out | `case_id`, `message_id`, `outbound_id`, `provider_message_id`, `error`, `code` |
| `message.read` | The customer read a message | `case_id`, `message_id`, `outbound_id`, `provider_message_id` |
| `message.received` | A customer message comes in | `case_id`, `message_id`, `channel_id`, `contact_id` |
| `message.sent` | A message went out | `case_id`, `message_id`, `outbound_id`, `channel_id`, `provider_message_id`, `source` |
| `template.status_changed` | A message template is approved, rejected or paused | `connection_id`, `name`, `language`, `status`, `category`, `rejected_reason` |

Delivery receipts come from the channel. A message that a person wrote from the phone of the business went out
already. It is a `message.sent` event with `source` set to `business_app`, and no receipt event follows it.

## Events that also start workflows

These events can start a workflow, and a webhook can listen to them. The fields are the ones the catalogue lists for
each event. A name with a dot is a nested key: `channel.name` is the `name` key inside `channel`. A field that has no
value for an event can be empty or missing.

The customer fields are `channel.name`, `channel.id`, `contact_id`, `contact.name`, `contact.language`, `case.id`, `text`, `business.name`, `agent_id`, `contact.tags`, `contact.fields.<key>`, `case.status`, `hours.open_now`, `hours.opens_at`, `hours.opens`, `hours.when_closed`, `hours.closed_message`, `responsible.member_id`, `responsible.name`, `responsible.on_duty`, `responsible.back_at`, `responsible.back`.

| Event | When it happens | `data` fields |
| - | - | - |
| `agent.published` | An agent goes live | `agent_id`, `name`, `version` |
| `billing.stopped` | A customer writes while billing stops the agents | The customer fields |
| `booking.changed` | A booking is made, moved or cancelled | `booking_id`, `what`, `starts_at`, `name`, `place`, `customer`, `user_id` |
| `booking.created` | A booking is made | The customer fields and `booking.id`, `booking.what`, `booking.when`, `booking.with`, `booking.where`, `booking.app`, `booking.by` |
| `call.inbound` | A customer calls in | The customer fields and `call_id` |
| `campaign.member_converted` | A campaign member converts | `campaign_id`, `campaign`, `contact_id` |
| `campaign.member_joined` | Someone joins a campaign | `campaign_id`, `campaign`, `contact_id`, `source` |
| `campaign.member_responded` | A campaign member replies | `campaign_id`, `campaign`, `contact_id` |
| `case.handoff_expired` | Nobody took a handoff in time | The customer fields and `reason`, `minutes` |
| `case.idle` | A quiet conversation closes | The customer fields and `after_hours` |
| `case.reassigned` | A customer gets a new contact person | The customer fields and `handover.leaver`, `handover.to`, `handover.customer`, `handover.window_open`, `handover.template.name`, `handover.template.language` |
| `contact.created` | A new contact is added | `contact_id`, `channel` |
| `contact.identity_added` | A contact gets a new phone, email or handle | `contact_id`, `identity_id`, `kind`, `verified` |
| `contact.touch` | A contact comes from a source again | `contact_id`, `source`, `source_id`, `campaign`, `ad`, `form` |
| `customer.confirmed` | A customer taps yes to confirm | The customer fields and `key`, `ref`, `terms`, `message_id` |
| `customer.declined` | A customer taps to change what they were asked to confirm | The customer fields and `key`, `ref`, `terms`, `message_id` |
| `email.inbound` | An email comes in | `inbound_id`, `message_id`, `channel_id`, `address`, `owner_id`, `agent_id`, `contact_id`, `case_id`, `from`, `subject`, `text`, `category`, `confidence`, `language`, `needs_human`, `stage`, `initiated_by_us`, `started_by`, `thread_root`, `in_reply_to`, `reply_allowed`, `reply_reason`, `auto_send_now`, `business.name` |
| `knowledge.ready` | New knowledge is ready | `title`, `document_id` |
| `lead.submitted` | A form or lead comes in | `contact_id`, `source`, `source_id`, `campaign`, `ad`, `form`, `fields`, `name`, `email`, `phone` |
| `record.created` | A record is added to an app | `app`, `entity`, `record_id`, `fields`, `status_from`, `status_to`, `member_id`, `agent_id`, `case_id` |
| `record.status_changed` | A record in an app changes status or is archived | `app`, `entity`, `record_id`, `fields`, `status_from`, `status_to`, `member_id`, `agent_id`, `case_id` |
| `record.updated` | A record in an app changes | `app`, `entity`, `record_id`, `fields`, `status_from`, `status_to`, `member_id`, `agent_id`, `case_id` |

`record.created`, `record.updated` and `record.status_changed` carry the keys of what changed and never the values.
Read the record with the [app records](https://docs.impellabs.tech/api/workflows-and-tasks.md) calls and the `record_id`.

## Events of apps and of incoming webhooks

An installed app raises its own events with names that start with `app.`. A source that sends events into ImpelLabs
raises names that start with `in.`. A webhook can choose them. They appear in the live list once the app or the
source is on. Read [incoming events](https://docs.impellabs.tech/api/incoming-events.md) for the second kind.

## Events that never leave

ImpelLabs delivers only the events in the live list. Other events, such as the platform's own background work, never
go to a webhook.

## The test event

A test call sends the event `webhook.test` with `data` set to `{"message": "A test from ImpelLabs."}`. A test is sent
once and is not tried again.


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