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

# Webhooks

> Subscribe to real-time events with webhooks, verify signatures, and manage delivery reliability.

Webhooks let you receive real-time HTTP notifications when events occur in your IllumiChat workspace. Instead of polling the API, configure a webhook endpoint and IllumiChat will send a POST request to your URL each time a subscribed event fires.

## List Webhooks

Returns all webhooks configured for the workspace.

```bash theme={null}
curl -X GET "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks" \
  -H "Authorization: Bearer <your-api-key>"
```

**Response** `200 OK`

```json theme={null}
{
  "data": [
    {
      "id": "wh_aaa111",
      "url": "https://api.acme.com/webhooks/illumichat",
      "events": ["message.created", "conversation.created", "contact.created"],
      "active": true,
      "createdAt": "2025-08-01T12:00:00Z"
    }
  ],
  "total": 1,
  "limit": 25,
  "offset": 0
}
```

***

## Create Webhook

Registers a new webhook endpoint. Requires `owner` or `admin` role.

```bash theme={null}
curl -X POST "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.acme.com/webhooks/illumichat",
    "events": ["message.created", "conversation.created", "contact.created"],
    "secret": "whsec_your_signing_secret"
  }'
```

| Parameter | Type      | Required | Description                                                      |
| --------- | --------- | -------- | ---------------------------------------------------------------- |
| `url`     | string    | Yes      | The HTTPS endpoint URL that receives webhook payloads.           |
| `events`  | string\[] | Yes      | List of event types to subscribe to. Use `["*"]` for all events. |
| `secret`  | string    | No       | Signing secret for HMAC verification. Auto-generated if omitted. |
| `active`  | boolean   | No       | Whether the webhook is active. Default `true`.                   |

**Response** `201 Created`

<Warning>
  The `secret` field is only returned in the create response. Store it securely -- you will need it to verify webhook signatures. It cannot be retrieved later.
</Warning>

***

## Get Webhook

Retrieves a webhook by ID.

```bash theme={null}
curl -X GET "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks/wh_aaa111" \
  -H "Authorization: Bearer <your-api-key>"
```

**Response** `200 OK` with the webhook object (excluding `secret`).

***

## Update Webhook

Updates a webhook's configuration. Requires `owner` or `admin` role.

```bash theme={null}
curl -X PATCH "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks/wh_aaa111" \
  -H "Authorization: Bearer <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["message.created", "conversation.created", "conversation.ended", "contact.created"],
    "active": true
  }'
```

**Response** `200 OK` with the updated webhook object.

***

## Delete Webhook

Permanently removes a webhook and all its delivery history. Requires `owner` or `admin` role.

```bash theme={null}
curl -X DELETE "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks/wh_aaa111" \
  -H "Authorization: Bearer <your-api-key>"
```

**Response** `204 No Content`

***

## List Deliveries

Returns recent delivery attempts for a webhook. Uses cursor-based pagination.

```bash theme={null}
curl -X GET "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks/wh_aaa111/deliveries?limit=20" \
  -H "Authorization: Bearer <your-api-key>"
```

**Response** `200 OK`

```json theme={null}
{
  "data": [
    {
      "id": "del_aaa111",
      "event": "message.created",
      "status": "success",
      "statusCode": 200,
      "attempt": 1,
      "duration": 234,
      "createdAt": "2025-09-15T10:00:01Z"
    }
  ],
  "nextCursor": "eyJpZCI6ImRlbF9iYmIyMjIifQ==",
  "hasMore": true
}
```

***

## Retry Delivery

Manually retries a failed webhook delivery.

```bash theme={null}
curl -X POST "https://app.illumichat.com/api/workspaces/ws_abc123/webhooks/wh_aaa111/deliveries/del_bbb222/retry" \
  -H "Authorization: Bearer <your-api-key>"
```

**Response** `202 Accepted`

***

## Event Types

Subscribe to any combination of the following event types.

| Event                  | Description                                                   |
| ---------------------- | ------------------------------------------------------------- |
| `message.created`      | A new message was sent in a conversation (user or assistant). |
| `message.updated`      | A message was edited or its metadata changed.                 |
| `conversation.created` | A new conversation was started.                               |
| `conversation.ended`   | A conversation was closed or a widget session ended.          |
| `contact.created`      | A new contact was created (e.g., via widget lead form).       |
| `contact.updated`      | A contact's information was updated.                          |
| `ticket.created`       | A support ticket was created from a conversation.             |
| `ticket.updated`       | A ticket's status or assignment changed.                      |
| `ticket.resolved`      | A ticket was marked as resolved.                              |
| `assistant.updated`    | An assistant's configuration was changed.                     |
| `member.joined`        | A new member joined the workspace.                            |
| `member.left`          | A member was removed or left the workspace.                   |

<Tip>
  Use `["*"]` as the events array to subscribe to all current and future event types.
</Tip>

***

## Payload Format

Every webhook delivery sends a JSON payload with a consistent structure.

```json theme={null}
{
  "id": "evt_aaa111",
  "event": "message.created",
  "timestamp": "2025-09-15T10:00:00.123Z",
  "workspaceId": "ws_abc123",
  "data": {
    "id": "msg_xyz",
    "conversationId": "conv_aaa111",
    "role": "user",
    "content": "How do I update my payment method?",
    "assistantId": "ast_abc123",
    "createdAt": "2025-09-15T10:00:00Z"
  }
}
```

| Field         | Type   | Description                                             |
| ------------- | ------ | ------------------------------------------------------- |
| `id`          | string | Unique event ID. Use this to deduplicate deliveries.    |
| `event`       | string | The event type that triggered this delivery.            |
| `timestamp`   | string | ISO 8601 timestamp of when the event occurred.          |
| `workspaceId` | string | The workspace where the event originated.               |
| `data`        | object | Event-specific payload. Structure varies by event type. |

***

## Signature Verification

Every webhook request includes an `X-IllumiChat-Signature` header containing an HMAC-SHA256 signature of the request body. Always verify this signature to ensure the payload was sent by IllumiChat.

The signature is computed as:

```
HMAC-SHA256(webhook_secret, raw_request_body)
```

### Verification Examples

<CodeGroup>
  ```javascript Node.js theme={null}
  import crypto from "node:crypto";

  function verifyWebhookSignature(rawBody, signature, secret) {
    const expected = crypto
      .createHmac("sha256", secret)
      .update(rawBody)
      .digest("hex");

    return crypto.timingSafeEqual(
      Buffer.from(signature),
      Buffer.from(expected)
    );
  }

  // Express.js example
  app.post("/webhooks/illumichat", express.raw({ type: "application/json" }), (req, res) => {
    const signature = req.headers["x-illumichat-signature"];
    const secret = process.env.ILLUMICHAT_WEBHOOK_SECRET;

    if (!verifyWebhookSignature(req.body, signature, secret)) {
      return res.status(401).json({ error: "Invalid signature" });
    }

    const event = JSON.parse(req.body);
    // Process the event...

    res.status(200).json({ received: true });
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib
  from flask import Flask, request, jsonify

  app = Flask(__name__)

  def verify_signature(payload: bytes, signature: str, secret: str) -> bool:
      expected = hmac.new(
          secret.encode(),
          payload,
          hashlib.sha256,
      ).hexdigest()
      return hmac.compare_digest(signature, expected)

  @app.route("/webhooks/illumichat", methods=["POST"])
  def handle_webhook():
      signature = request.headers.get("X-IllumiChat-Signature", "")
      secret = os.environ["ILLUMICHAT_WEBHOOK_SECRET"]

      if not verify_signature(request.data, signature, secret):
          return jsonify({"error": "Invalid signature"}), 401

      event = request.get_json()
      # Process the event...

      return jsonify({"received": True}), 200
  ```
</CodeGroup>

<Warning>
  Always use a constant-time comparison function (such as `crypto.timingSafeEqual` or `hmac.compare_digest`) when verifying signatures. Standard string comparison is vulnerable to timing attacks.
</Warning>

***

## Retry Policy

If your endpoint does not return a `2xx` status code within 30 seconds, IllumiChat retries the delivery with exponential backoff.

| Attempt | Delay       | Total Elapsed |
| ------- | ----------- | ------------- |
| 1       | Immediate   | 0 seconds     |
| 2       | 60 seconds  | \~1 minute    |
| 3       | 300 seconds | \~6 minutes   |

After 3 failed attempts, the delivery is marked as `failed`. You can manually retry failed deliveries at any time using the Retry Delivery endpoint.

<Accordion title="Best practices for reliable webhook handling">
  **Return 200 quickly.** Acknowledge the webhook immediately and process the event asynchronously.

  **Deduplicate by event ID.** The same event may be delivered more than once during retries. Use the `id` field to detect duplicates.

  **Verify the signature.** Always validate the `X-IllumiChat-Signature` header before processing.

  **Handle unknown events gracefully.** New event types may be added in the future. Return `200` for events you do not recognize.
</Accordion>

<Note>
  If a webhook consistently fails (more than 50 consecutive failures over 7 days), IllumiChat automatically disables the webhook and notifies workspace admins.
</Note>
