> ## 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.

# Link your system

> Send inbox events to your own API and respond with actions — reply with live data, trigger flows, update contacts, or attach business context for agents.

Link your system sends signed webhook events from Adraa Inbox to a URL you
control, and lets your API act on them in the same HTTP response. When a
customer asks "where is my order?", your server can look the order up by the
contact's phone number and reply instantly — no flow required.

## Connect your endpoint

1. An admin opens **Settings → Link your system**.
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). |
| `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 only on
`message.received`, and `assignment` (`agentId`, `agentName`) is added on
`conversation.assigned`. Test deliveries include `"test": true`.

### 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>

## Respond with actions

Return HTTP `200` with JSON. Any actions you include run immediately:

```json theme={null}
{
  "actions": [
    { "type": "reply", "text": "Order #1234 ships tomorrow 🎉" },
    { "type": "set_contact_fields", "fields": { "last_order_id": "1234" } },
    {
      "type": "set_context",
      "data": {
        "orders": [
          { "id": "1234", "status": "shipped", "total": "250 SAR" }
        ]
      }
    }
  ]
}
```

| Action               | What it does                                                                                                                                               |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `reply`              | Sends `text` to the customer on the conversation's channel, as an automated agent message.                                                                 |
| `trigger_flow`       | Runs the flow named by `flowId` on the conversation, with optional `variables`. WhatsApp conversations only.                                               |
| `set_contact_fields` | Merges `fields` into the contact's custom fields (the **Metadata** section of the contact panel).                                                          |
| `set_context`        | Replaces the contact's **Linked system** panel with `data` — structured business context like orders or bookings that agents see next to the conversation. |

An empty body or `{}` is fine when you only want to record the event — for
example, feeding an analytics pipeline.

### Limits

* Up to **5 actions** per response, of which at most **3 replies** (4,096
  characters each).
* `fields` is capped at 32 keys / 16 KB; `data` at 32 KB.
* `reply` and `trigger_flow` are skipped on `contact.*` events (no conversation
  in scope).
* Invalid actions are recorded in the delivery log; valid ones still run.

## Delivery behavior

Deliveries are fire-and-forget: they never delay message processing, and a
reply lands moments after the customer's message. Your endpoint has **10
seconds** to respond; non-2xx responses and timeouts are logged and ignored —
there are no automatic retries, so a slow endpoint can't double-message a
customer. The **Recent deliveries** table in settings keeps the last 50
requests with response bodies and per-action results for debugging.

## Example uses

* **Order status:** reply with live order data looked up by phone number, and
  push the order list into the Linked system panel for agents.
* **CRM enrichment:** on `contact.created`, tag the contact with tier and
  account owner from your CRM.
* **Dynamic routing:** on `message.received`, trigger a VIP flow for customers
  your database marks as high-value.
* **Ticket sync:** mirror `conversation.created` / `conversation.resolved`
  into Jira or Zendesk.

<Note>
  Prefer polling or building against the REST API instead? Everything the
  inbox exposes is also available on the [public API](/api-reference/introduction),
  and no-code automations are covered by the [Zapier integration](/guide/zapier).
</Note>
