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

# Webhooks

> Receive incoming WhatsApp messages and track outbound send and delivery outcomes

WhatsApp events use the existing [webhook subscription API](https://inkbox.ai/docs/api/webhooks/subscriptions.md) and [signature verification](https://inkbox.ai/docs/signing-keys.md). Attach the subscription to the agent identity. You can select WhatsApp events before approval and mix them with other notification families; selecting them does not enable WhatsApp.

## Event types

| Event | Meaning |
| :- | :- |
| `whatsapp.received` | A new authorized incoming message |
| `whatsapp.sent` | Sending positively confirmed |
| `whatsapp.delivered` | Delivery positively confirmed |
| `whatsapp.delivery_failed` | Explicit outbound failure recorded |
| `whatsapp.delivery_unconfirmed` | Delivery observation is unknown; this is not evidence that sending failed |
| `whatsapp.reaction_received` | A participant adds or removes a whole-message reaction; `data.reaction` carries its versioned receipt. |

Only `whatsapp.received` is new incoming content. Delivery notifications update the original message's tracking; do not wake an agent to reply or automatically resend because one arrives. Preserve the originating identity and conversation when routing a reply.

## Subscribe

```python Python theme={null}
inkbox.webhooks.subscriptions.create(
    agent_identity_id=identity.id,
    url="https://example.com/inkbox-events",
    event_types=[
        "whatsapp.received",
        "whatsapp.sent",
        "whatsapp.delivered",
        "whatsapp.delivery_failed",
        "whatsapp.delivery_unconfirmed",
        "whatsapp.reaction_received",
    ],
)
```

```typescript TypeScript theme={null}
await inkbox.webhooks.subscriptions.create({
  agentIdentityId: identity.id,
  url: "https://example.com/inkbox-events",
  eventTypes: [
    "whatsapp.received", "whatsapp.sent", "whatsapp.delivered",
    "whatsapp.delivery_failed", "whatsapp.delivery_unconfirmed", "whatsapp.reaction_received",
  ],
});
```

```bash cURL theme={null}
curl -X POST "https://inkbox.ai/api/v1/webhooks/subscriptions" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"agent_identity_id":"11111111-1111-4111-8111-111111111111","url":"https://example.com/inkbox-events","event_types":["whatsapp.received","whatsapp.sent","whatsapp.delivered","whatsapp.delivery_failed","whatsapp.delivery_unconfirmed","whatsapp.reaction_received"]}'
```

Use `scope=identity` when updating or deleting a mixed subscription. Disabling WhatsApp preserves other selected notification families. Current identity availability and permissions still control delivery.

## Payload

Every event uses the standard envelope with stable `id`, `event_type`, ISO timestamp, and `data`. Deduplicate retries by the event ID, not just the message text or phone number.

`data.message` uses the webhook-specific WhatsApp message shape. Unlike the REST response, it omits `is_blocked` and `status_refresh_pending` entirely; these fields are not null placeholders. Use the SDK webhook message type, not a strict REST message schema. It otherwise follows the [WhatsApp message object](https://inkbox.ai/docs/api/whatsapp/messages.md#message-object): `content`, stable message and conversation IDs, original local number, actual `sender_number`, remote-only `participants`, and explicit send/delivery state. `data.contacts` and `data.agent_identities` carry the permission-filtered peer matches where available.

Group messages use the same events. Check `data.message.is_group` and `group_name`, then route replies with `conversation_id`, not the name. `remote_number` is null for groups; `sender_number` can be null when the author’s number is unavailable. Quoted replies and media retain their normal message fields. Receiving a reaction does not satisfy a group’s incoming-message prerequisite. For group events, `data.contacts` and `data.agent_identities` are empty. Message events identify the message author with `data.message.sender_number`; reaction events identify the reactor with `data.reaction.remote_number`, which can be null. Never substitute the target message’s author for an unknown reactor.

Media entries preserve their stable ID, filename, MIME type, nullable size, availability and nullable temporary URL. Treat unavailable bytes explicitly. Refresh an expired URL with [`GET /whatsapp/media/{id}`](https://inkbox.ai/docs/api/whatsapp/media#get-current-media-access), not by re-sending or re-uploading the message.

## Group mentions

A native group mention arrives as the existing `whatsapp.received` event. `data.message.agent_mentioned` is a boolean that defaults to `false` when absent. There is no separate mention event or subscription.

To mention the agent, type `@` in WhatsApp and select the agent from the normal mention picker. Do not parse raw transport identifiers from message text to detect mentions. Use `agent_mentioned` for native mention detection.

In a compatible agent integration’s mention-only group reply mode, a native mention or a literal `@agent-handle` can trigger a reply. Other group messages remain conversation context without requiring a reply. This reply policy does not change the event type or bypass group send permissions.

## Other mentioned participants

`data.message.mentions` describes native mentions in the current incoming message, including direct chats and groups. It defaults to `[]`. Each entry contains:

* `text`: the matching visible mention token, or `null` when unavailable.
* `phone_number`: the participant’s E.164 number when a mapping is available and visible to the agent, or `null` otherwise.
* `is_agent`: whether this mention refers to the receiving agent.

For example, an existing `whatsapp.received` event can contain this message excerpt:

```json theme={null}
{
  "is_group": true,
  "agent_mentioned": false,
  "mentions": [
    {"text": "@15555550123", "phone_number": "+15555550123", "is_agent": false}
  ]
}
```

Use a known `phone_number` with your existing contact lookup if you need a saved contact name. Do not invent names or treat raw transport identifiers as phone numbers. Mentions of other participants provide conversation context; they do not trigger a group reply in mention-only mode. Direct chats do not require a mention to receive a reply.

## Update direct-only consumers

Existing message and conversation lists and `whatsapp.received` subscriptions can include group conversations. Upgrade SDK/CLI consumers to `0.7.29` or later before processing group responses. Older strict clients can fail to parse an entire message or conversation list when just one entry is a group, as well as individual group reads and webhook payloads. Handle `is_group`, nullable `remote_number`, and nullable `sender_number`; do not assume every event has one remote phone number. Route group replies by `conversation_id`.

## Companion admission

A sponsored group message can include `sender_access: "sponsored"` and top-level `companion` metadata. The same identity preference, sponsor rules, initialization history, and revocation contract apply as for other supported channels. Preserve receipt-time admission and the current activation through retries; do not infer admission from the event phase. See [per-message sender access](https://inkbox.ai/docs/webhooks.md#per-message-sender-access).

## Conversation context

`whatsapp.received` can use the existing `texts` class in the subscription's [context configuration](https://inkbox.ai/docs/api/webhooks/subscriptions.md#conversation-context-context_config). Context items identify their actual channel, including `whatsapp`. Delivery-only events do not represent another incoming-message turn.

WhatsApp does not add a new context class. Existing `whatsapp.received` events can carry the top-level [Companion metadata](https://inkbox.ai/docs/webhooks.md#companion-conversation-events) for a sponsored group. Subscribe and route only the event types your runtime handles.

Group received-message context stays within that group; it does not merge a member’s direct-message, SMS, or iMessage history.

WhatsApp enters `texts` context only for a `whatsapp.received` trigger. Email, SMS, and iMessage triggers retain their existing context behavior, even when the same subscription also selects WhatsApp events.

## Reaction events

`whatsapp.reaction_received` carries `data.reaction` plus the original target in `data.message`; the target is not a new incoming message. Its value is a [reaction receipt](https://inkbox.ai/docs/api/whatsapp/reactions.md), including the target message, direction, nullable actor number, revision, and status. A removal uses `status: "removed"`. Deduplicate by event ID and retain version ordering for the same actor and target message; do not reapply an older reaction after a newer revision. Unknown actor numbers remain null.

Message events preserve `data.message` with quote/thread IDs and currently applied reactions. A reaction or delivery event must not start a new text-reply turn unless your application deliberately handles that event type.


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