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

# Project webhooks

> Events sent to project-scoped webhooks - connections, workflow executions, and custom events

Project webhooks are scoped to the whole project rather than a single phone number. They never receive message or conversation events - for those, see [Message events](/docs/platform/webhooks/message-events).

Configure them in **Integrations → Webhooks → Platform webhooks**.

## Available events

| Event                                | Fires when                                                                   |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| `whatsapp.phone_number.created`      | A customer connects WhatsApp through a setup link                            |
| `whatsapp.phone_number.deleted`      | A phone number is removed from your project                                  |
| `whatsapp.phone_number.offboarded`   | Meta offboards a phone number from the Cloud API                             |
| `whatsapp.phone_number.disconnected` | Meta reports that your partner access was removed or the app was uninstalled |
| `whatsapp.phone_number.reconnected`  | Meta reports that the Cloud API connection was restored                      |
| `whatsapp.account.disabled`          | Meta disables the WhatsApp Business Account                                  |
| `whatsapp.account.restricted`        | Meta restricts capabilities on the WhatsApp Business Account                 |
| `whatsapp.account.reinstated`        | Meta reinstates a previously disabled WhatsApp Business Account              |
| `whatsapp.account.violation`         | Meta reports a policy violation on the WhatsApp Business Account             |
| `workflow.execution.handoff`         | A workflow hands off to a human agent                                        |
| `workflow.execution.failed`          | A workflow execution fails                                                   |
| `project.event`                      | A custom project event is emitted                                            |
| `kapso_agent.run.approval_required`  | An API-triggered agent run needs a tool approval                             |
| `kapso_agent.run.completed`          | An API-triggered agent run completes                                         |
| `kapso_agent.run.failed`             | An API-triggered agent run fails                                             |
| `kapso_agent.run.cancelled`          | An API-triggered agent run is cancelled                                      |

Sent only to project webhooks. These do not include message or conversation events.

## whatsapp.phone\_number.created

Fires when a customer successfully connects their WhatsApp through a setup link.

See [Connection detection](/docs/platform/setup-links/detect-connection) for implementation guide.

**Payload**:

```json theme={null}
{
  "phone_number_id": "123456789012345",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "customer": {
    "id": "880e8400-e29b-41d4-a716-446655440003"
  }
}
```

## whatsapp.phone\_number.deleted

Fires when a WhatsApp phone number is removed from your project. This event triggers at the start of the teardown process, before the number is fully deleted.

**Payload**:

```json theme={null}
{
  "phone_number_id": "123456789012345",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "customer": {
    "id": "880e8400-e29b-41d4-a716-446655440003"
  }
}
```

## whatsapp.phone\_number.offboarded

Fires when Meta offboards a phone number from the Cloud API.

These three events (`offboarded`, `disconnected`, `reconnected`) are only available on project-scoped webhooks using payload version `v2`. Subscribing with `payload_version: "v1"` returns `422`.

**Payload**:

```json theme={null}
{
  "id": "wae_9f2c1d7b8a4e3f5069c1b2a3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f607",
  "event": "whatsapp.phone_number.offboarded",
  "occurred_at": "2026-08-14T12:00:00.000000Z",
  "phone_number_id": "123456789012345",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "customer": {
    "id": "880e8400-e29b-41d4-a716-446655440003"
  },
  "connection_type": "dedicated",
  "source": {
    "provider": "meta",
    "event": "ACCOUNT_OFFBOARDED",
    "business_account_id": "102290129340398"
  }
}
```

| Field             | Description                                                        |
| ----------------- | ------------------------------------------------------------------ |
| `id`              | Stable event id, unique per phone number and underlying Meta event |
| `connection_type` | `dedicated` or `coexistence`                                       |
| `customer`        | Present only when the number belongs to a customer                 |
| `source.event`    | The raw Meta account update event that produced this notification  |

## whatsapp.phone\_number.disconnected

Fires when Meta reports that your partner access to the number was removed (`PARTNER_REMOVED`) or the partner app was uninstalled (`PARTNER_APP_UNINSTALLED`). The number can no longer send or receive messages through Kapso until it is reconnected.

Same payload as `whatsapp.phone_number.offboarded`, plus a `disconnection` object with the lowercased Meta values:

```json theme={null}
{
  "id": "wae_9f2c1d7b8a4e3f5069c1b2a3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f607",
  "event": "whatsapp.phone_number.disconnected",
  "occurred_at": "2026-08-14T12:00:00.000000Z",
  "phone_number_id": "123456789012345",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "connection_type": "dedicated",
  "source": {
    "provider": "meta",
    "event": "PARTNER_REMOVED",
    "business_account_id": "102290129340398"
  },
  "disconnection": {
    "reason": "primary_inactivity",
    "initiated_by": "system"
  }
}
```

`disconnection.reason` and `disconnection.initiated_by` come straight from Meta and may be absent.

## whatsapp.phone\_number.reconnected

Fires when Meta reports that the Cloud API connection was restored (`ACCOUNT_RECONNECTED`). Same payload as `whatsapp.phone_number.offboarded`, with `source.event` set to `ACCOUNT_RECONNECTED`.

## Account enforcement events

`whatsapp.account.disabled`, `whatsapp.account.restricted`, `whatsapp.account.reinstated` and `whatsapp.account.violation` report Meta enforcement against the WhatsApp Business Account (WABA), not against a single number.

Like the phone number lifecycle events, they are only available on project-scoped webhooks using payload version `v2`. Subscribing with `payload_version: "v1"` returns `422`.

Because the underlying Meta payloads carry no phone number, you receive one delivery per project and WABA, with every affected number listed in `phone_numbers` - not one delivery per number.

**Payload**:

```json theme={null}
{
  "id": "wae_9f2c1d7b8a4e3f5069c1b2a3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f607",
  "event": "whatsapp.account.restricted",
  "occurred_at": "2026-08-14T12:00:00.000000Z",
  "business_account_id": "102290129340398",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "phone_numbers": [
    {
      "id": "123456789012345",
      "display_phone_number": "+1 555 010 1234"
    }
  ],
  "restrictions": [
    {
      "type": "RESTRICTED_BIZ_INITIATED_MESSAGING",
      "expires_at": "2026-09-14T12:00:00Z"
    }
  ],
  "source": {
    "provider": "meta",
    "event": "ACCOUNT_RESTRICTION"
  }
}
```

| Field           | Description                                                                                                                                                                                         |
| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`            | Stable event id, unique per project, WABA and underlying Meta event                                                                                                                                 |
| `phone_numbers` | Every number in the project on that WABA                                                                                                                                                            |
| `restrictions`  | Present on `whatsapp.account.restricted`. `type` is the raw Meta restriction type; `expires_at` is omitted when the restriction has no expiry                                                       |
| `violation`     | Present on `whatsapp.account.violation`: `type` is the raw Meta violation type, with an optional `remediation` string                                                                               |
| `ban`           | Present on `whatsapp.account.disabled` and `whatsapp.account.reinstated`: `state` is `DISABLE` or `REINSTATE`, with an optional `date_label` string that Meta sends already localized and formatted |
| `source.event`  | The raw Meta account update event that produced this notification (`ACCOUNT_RESTRICTION`, `ACCOUNT_VIOLATION` or `DISABLED_UPDATE`)                                                                 |

Keys with no value are omitted from the payload.

`whatsapp.account.disabled` and `whatsapp.account.reinstated` both come from Meta's `DISABLED_UPDATE` event, split by ban state:

```json theme={null}
{
  "id": "wae_9f2c1d7b8a4e3f5069c1b2a3d4e5f60718293a4b5c6d7e8f90a1b2c3d4e5f607",
  "event": "whatsapp.account.disabled",
  "occurred_at": "2026-08-14T12:00:00.000000Z",
  "business_account_id": "102290129340398",
  "project": {
    "id": "990e8400-e29b-41d4-a716-446655440004"
  },
  "phone_numbers": [
    {
      "id": "123456789012345",
      "display_phone_number": "+1 555 010 1234"
    }
  ],
  "ban": {
    "state": "DISABLE",
    "date_label": "August 7, 2026"
  },
  "source": {
    "provider": "meta",
    "event": "DISABLED_UPDATE"
  }
}
```

## workflow\.execution.handoff

Fires when a workflow execution is handed off to a human agent.

**Payload**:

```json theme={null}
{
  "event": "workflow.execution.handoff",
  "occurred_at": "2025-12-08T12:00:00Z",
  "project_id": "990e8400-e29b-41d4-a716-446655440004",
  "workflow_id": "880e8400-e29b-41d4-a716-446655440001",
  "workflow_execution_id": "770e8400-e29b-41d4-a716-446655440002",
  "status": "handoff",
  "tracking_id": "track-abc123",
  "channel": "whatsapp",
  "whatsapp_conversation_id": "conv_789",
  "handoff": {
    "reason": "User requested human assistance",
    "source": "agent_tool"
  }
}
```

| Field            | Description                                                            |
| ---------------- | ---------------------------------------------------------------------- |
| `handoff.reason` | Optional reason provided during handoff                                |
| `handoff.source` | `agent_tool` (from agent step) or `action_step` (from workflow action) |

## workflow\.execution.failed

Fires when a workflow execution fails due to an error.

**Payload**:

```json theme={null}
{
  "event": "workflow.execution.failed",
  "occurred_at": "2025-12-08T12:00:00Z",
  "project_id": "990e8400-e29b-41d4-a716-446655440004",
  "workflow_id": "880e8400-e29b-41d4-a716-446655440001",
  "workflow_execution_id": "770e8400-e29b-41d4-a716-446655440002",
  "status": "failed",
  "tracking_id": "track-abc123",
  "channel": "whatsapp",
  "whatsapp_conversation_id": "conv_789",
  "error": {
    "message": "Workflow execution timed out"
  }
}
```

## project.event

Fires when a custom project event is emitted.

This event is only available on project-scoped webhooks. Phone-number webhooks do not receive custom project events.

Subscribing to this event requires project events to be available on your plan.

**Payload**:

```json theme={null}
{
  "id": "990e8400-e29b-41d4-a716-446655440004",
  "event": "project.event",
  "name": "lead.qualified",
  "occurred_at": "2026-06-27T14:30:00.000000Z",
  "project_id": "880e8400-e29b-41d4-a716-446655440003",
  "conversation_id": "770e8400-e29b-41d4-a716-446655440002",
  "properties": {
    "score": 92,
    "source": "pricing_page"
  }
}
```

`conversation_id` is included only when the event is linked to a WhatsApp conversation.

See [Events](/docs/platform/events) for emitting and triggering workflows from project events.

## Kapso Agent run events

Fire when a Kapso Agent run triggered through the [Agent API](/docs/kapso-agent/api) needs approval, completes, fails, or is cancelled. Runs started from the dashboard or Slack don't emit these events.

**Payload** (`kapso_agent.run.completed`):

```json theme={null}
{
  "id": "3c0f1a5b9d8e7f6a4b2c1d0e9f8a7b6c5d4e3f2a1b0c9d8e7f6a5b4c3d2e1f0a",
  "event": "kapso_agent.run.completed",
  "created_at": "2026-08-21T14:35:00Z",
  "data": {
    "run_id": "990e8400-e29b-41d4-a716-446655440004",
    "session_id": "880e8400-e29b-41d4-a716-446655440003",
    "status": "completed",
    "agent": { "type": "mode", "id": "api" },
    "result": { "type": "message", "content": "..." },
    "metadata": {},
    "created_at": "2026-08-21T14:30:00Z",
    "started_at": "2026-08-21T14:30:05Z",
    "finished_at": "2026-08-21T14:35:00Z",
    "status_url": "/platform/v1/kapso-agent/runs/990e8400-e29b-41d4-a716-446655440004"
  }
}
```

`kapso_agent.run.approval_required` includes `pending_approval` with the tool name and parameters. Failed and cancelled runs include `error` with `code` (`run_failed` or `run_cancelled`) and `message`. Null fields are omitted.
