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

# Events: Message sent, delivered and received

> Receive a webhook when a message is sent to a lead, delivered to the lead, or received from the lead, instead of polling the API.

These events tell your server about each message between your agent and a lead, as it happens. Use them instead of polling the API for new messages.

| Event type | Fires when | Channels |
| - | - | - |
| `message.sent` | A message to the lead is accepted by the provider. The sender can be the agent, a cadence, or a person on your team. | WhatsApp, SMS, email, iMessage, Instagram, Messenger |
| `message.delivered` | The provider confirms the message reached the lead's phone or inbox | WhatsApp, SMS, email |
| `message.received` | The lead sends a message | WhatsApp, SMS, email, iMessage, Instagram, Messenger |

Each event fires at most once per message. Calls do not fire these events.

A message that fails to send never fires `message.sent`. Use [Outreach failed](/docs/en/api/webhooks/outreach-failed.md) for failures.

When WhatsApp reports a message as read before it reports it as delivered, `message.delivered` still fires once.

## Payload

```json theme={null}
{
  "event_type": "message.received",
  "event_id": "...",
  "webhook_id": "...",
  "delivery_id": "...",
  "timestamp": "2026-10-22T16:30:05.000Z",
  "client_id": "...",
  "test": false,
  "data": {
    "message": {
      "id": "uuid",
      "direction": "inbound",
      "channel": "whatsapp",
      "type": "text",
      "text": "Hi, do you have availability tomorrow?",
      "status": "delivered",
      "provider_message_id": "wamid.HBgL...",
      "thread_id": "uuid",
      "sent_at": "2026-10-22T16:30:00.000Z"
    },
    "lead": {
      "id": "uuid",
      "first_name": "Tony",
      "last_name": "Stark",
      "email": "tony@example.com",
      "phone": "+15550000000",
      "company": "Stark Industries",
      "source": "api",
      "external_id": null,
      "metadata": {}
    },
    "workflow": {
      "id": "uuid",
      "name": "Lead to purchase intent"
    }
  }
}
```

## Fields

### `data.message`

| Field | Type | Description |
| - | - | - |
| `id` | string (uuid) | Message ID. Use it to ignore a delivery you already processed. |
| `direction` | string | `outbound` for `message.sent` and `message.delivered`, `inbound` for `message.received` |
| `channel` | string | `whatsapp`, `sms`, `email`, `imessage`, `instagram` or `messenger` |
| `type` | string \| null | Message type when known, such as `text`, `template`, `image` or `audio` |
| `text` | string \| null | Message text |
| `status` | string \| null | `sent` for `message.sent`, `delivered` for `message.delivered`. For `message.received` it is the stored status of the inbound message. |
| `provider_message_id` | string \| null | The provider's ID for the message, when known |
| `thread_id` | string (uuid) \| null | Conversation ID. `null` for email. |
| `sent_at` | string (ISO 8601) \| null | When the message was sent |

### `data.lead`

| Field | Type | Description |
| - | - | - |
| `id` | string (uuid) | Lead ID |
| `first_name` | string \| null | Lead's first name |
| `last_name` | string \| null | Lead's last name |
| `email` | string \| null | Lead's email |
| `phone` | string \| null | Lead's phone |
| `company` | string \| null | Lead's company |
| `source` | string | Source stored for the lead |
| `external_id` | string \| null | ID from an external system |
| `metadata` | object | Lead metadata |

### `data.workflow`

The task the message belongs to. When Nexor cannot tell which task it is, both fields are `null`.

| Field | Type | Description |
| - | - | - |
| `id` | string (uuid) \| null | Task ID |
| `name` | string \| null | Task name |

## Filtering

1. Use the **Task** selector to receive messages from every task or from one task. A message with no known task does not match a single-task webhook.
2. Add a **Channel** filter to receive messages from one channel only, for example only WhatsApp.

A filter only applies to events that carry its field. On a webhook that also listens to stage changes, a Channel filter does not limit the stage-change events.

## Testing

Press **Test** on the webhook to send a sample of the selected message event to your endpoint. Test deliveries have `"test": true`.


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