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

# Webhooks

> Send inbox events to your own API as they happen — keep your CRM, order system, or analytics in sync with every message, conversation, and contact change.

Webhooks send signed event notifications from Adraa Inbox to a URL you control,
as each event happens. Use them to keep your own systems in sync — log every
customer message in your CRM, open a ticket when a conversation starts, or feed
an analytics pipeline.

## Connect your endpoint

1. An admin opens **Settings → Webhooks**.
2. Enter your **webhook URL** (must be `https://` and publicly reachable) and
   pick the events to send.
3. Click **Connect** and copy the signing secret (`whsec_…`) — it's shown only
   once. Use **Rotate** later to issue a new one.
4. Click **Send test event** to deliver a sample `message.received` payload and
   inspect your server's response.

## Events

| Event | Fires when |
| - | - |
| `message.received` | A customer message arrives on any channel (WhatsApp, Instagram, TikTok, email, web widget). |
| `message.sent` | A message is sent to the customer — an agent reply, a public API send, a WhatsApp template or interactive message, a broadcast, an AI auto-reply, a flow step, or a message the business sends from the WhatsApp Business app. Does not fire for failed sends or imported history. |
| `conversation.created` | A new conversation starts. |
| `contact.created` | A contact first appears in the workspace. |
| `contact.updated` | An agent changes a contact's fields or language. |
| `conversation.resolved` | A conversation is closed. |
| `conversation.assigned` | A conversation is assigned to an agent. |

## The request we send

Each event is a `POST` with a JSON body:

```json theme={null}
{
  "event": "message.received",
  "timestamp": "2026-07-02T10:00:00.000Z",
  "webhookId": "…",
  "company": { "id": "…", "name": "acme" },
  "contact": {
    "id": "…",
    "name": "Sara",
    "phone": "+9665xxxxxxxx",
    "email": null,
    "customFields": { "customer_tier": "gold" }
  },
  "conversation": { "id": "…", "channel": "whatsapp", "status": "open" },
  "message": { "id": "…", "text": "where is my order?", "attachments": [] }
}
```

`conversation` is omitted for `contact.*` events, `message` is present on
`message.received` and `message.sent` — on `message.sent` it also carries
`sentBy` — and `assignment` (`agentId`, `agentName`) is added on
`conversation.assigned`. Test deliveries include `"test": true`.

### `message.sent`

```json theme={null}
{
  "event": "message.sent",
  "timestamp": "2026-09-27T12:00:00.000Z",
  "webhookId": "ckw...",
  "company": { "id": "ckc...", "name": "acme" },
  "contact": {
    "id": "cku...",
    "name": "Sara",
    "phone": "+966500000000",
    "email": null,
    "customFields": { "tier": "gold" }
  },
  "conversation": { "id": "ckv...", "channel": "whatsapp", "status": "open" },
  "message": {
    "id": "ckm...",
    "text": "Your order ships today",
    "attachments": [],
    "sentBy": "agent"
  }
}
```

`sentBy` tells you where the message came from:

| Value | Sent by |
| - | - |
| `agent` | A teammate replying from the inbox |
| `api` | Your own integration, through the public API |
| `ai` | An AI auto-reply |
| `flow` | A flow step |
| `whatsapp_app` | Someone on your team using the WhatsApp Business app, or another app on your number |

### Verify the signature

Every delivery carries these headers:

| Header | Value |
| - | - |
| `X-Adraa-Event` | The event name. |
| `X-Adraa-Webhook-Id` | The endpoint's id. |
| `X-Adraa-Timestamp` | Unix seconds when the request was signed. |
| `X-Adraa-Signature` | `sha256=` + HMAC-SHA256 of `"{timestamp}.{rawBody}"` using your signing secret. |

```js verify-signature.js theme={null}
import crypto from "crypto";

function isFromAdraa(req, rawBody, secret) {
  const timestamp = req.headers["x-adraa-timestamp"];
  const signature = req.headers["x-adraa-signature"];
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", secret).update(`${timestamp}.${rawBody}`).digest("hex");
  return (
    Boolean(signature) &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  );
}
```

<Tip>
  Reject requests whose timestamp is more than a few minutes old to prevent
  replays.
</Tip>

## Your response

Deliveries are **one-way**. Return any `2xx` status to acknowledge the event —
an empty body or `{}` is fine. We record the status and body on the delivery log
so you can debug your endpoint, but nothing in your response is acted on.

## Delivery behavior

Deliveries are fire-and-forget: they never delay message processing. Your
endpoint has **10 seconds** to respond; non-2xx responses and timeouts are
logged and ignored — there are no automatic retries. The **Recent deliveries**
table in settings keeps the last 50 requests with their response bodies for
debugging.

## Example uses

* **CRM sync:** log every `message.received` against the matching customer
  record in your CRM.
* **Ticket sync:** mirror `conversation.created` / `conversation.resolved`
  into Jira or Zendesk.
* **Analytics:** stream all events into your warehouse to report on response
  times and volume by channel.
* **Alerting:** page an on-call channel when `conversation.created` fires
  outside business hours.

<Note>
  Need to send messages or change data rather than just receive events? That's
  the [public API](/api-reference/introduction), and no-code automations are
  covered by the [Zapier integration](/guide/zapier).
</Note>


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