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

# Webhooks

> Get Wiredesk events on your own server as they happen: new messages, handover requests, leads and finished conversations, signed per Standard Webhooks and retried for two days.

Webhooks send a workspace's events to a URL you own. Each event is a signed `POST` with a JSON body, sent as it happens. A delivery that fails is retried for about two days.

Add endpoints under **Developers → Webhooks** in the sidebar. Only owners and admins see the Developers section. A workspace can have up to 10 endpoints, and each one picks the events it gets.

An endpoint sends events only while the teammate who added it is still an owner or admin of the workspace, the same rule as [API keys](https://wiredesk.ai/docs/developers/api.md). If they are removed or demoted, the endpoint is switched off and the page says why. An owner or admin who switches it back on becomes the one it sends under.

## Events

| Event | Sent when |
| - | - |
| `message.created` | A customer, the agent or a teammate writes in a chat or email conversation, including conversations started through the [API](https://wiredesk.ai/docs/developers/api.md#talk-to-an-agent). |
| `handover.requested` | The agent asks for a person to take over. Once per request: a customer asking again while they wait does not send another. |
| `lead.captured` | The agent saves a lead with a **Capture leads** action. |
| `conversation.ended` | A conversation has closed and been summarised. Calls are summarised when they end. Chats and emails close after 30 quiet minutes and are summarised within the next 15. |

Phone and browser-voice transcripts are not sent line by line, because a line is still being written while the caller speaks. Read the full transcript from [`GET /conversations/{id}/messages`](https://wiredesk.ai/docs/api-reference/conversations/list-messages) when `conversation.ended` arrives.

The widget's opening message, shown before anyone types, is not sent either.

## The request

```http theme={null}
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: Wiredesk-Webhooks/1.0
webhook-id: msg_2f8c0e4a9b7d4c1e8f3a6b5d0c9e7f12
webhook-timestamp: 1760000000
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
```

```json theme={null}
{
  "type": "message.created",
  "timestamp": "2026-10-08T14:03:12.000Z",
  "data": {
    "message": {
      "id": "982",
      "object": "message",
      "conversation_id": "2b0c…",
      "role": "agent",
      "text": "Saturdays we're open 10 to 4.",
      "created_at": "2026-10-08T14:03:12.000Z"
    },
    "conversation": { "id": "2b0c…", "object": "conversation", "channel": "chat", … }
  }
}
```

`data` holds the same objects the [API](https://wiredesk.ai/docs/developers/api.md) returns:

| Event | `data` |
| - | - |
| `message.created` | `message` and its `conversation`. `role` is `user` (the customer), `agent` (the AI agent) or `human_agent` (a teammate). |
| `handover.requested` | `conversation`, the agent's `reason`, and `times_asked`. |
| `lead.captured` | `lead`, with `name`, `email`, `phone`, the other `fields` the action collects, and its `conversation_id`. |
| `conversation.ended` | `conversation`, with its `summary`, `outcome`, `topic` and `sentiment`, and `message_count`. |

Answer with any `2xx` status, quickly. Do slow work after you respond. Redirects are not followed and count as a failure.

## Check the signature

Every request is signed per [Standard Webhooks](https://www.standardwebhooks.com), so any of its libraries can check it. The signed content is `webhook-id`, `webhook-timestamp` and the raw body, joined with dots, signed with HMAC-SHA256 using your endpoint's signing secret (`whsec_…`).

```js theme={null}
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.WIREDESK_WEBHOOK_SECRET); // "whsec_…"

app.post("/wiredesk", express.raw({ type: "application/json" }), (req, res) => {
  let event;
  try {
    event = wh.verify(req.body, req.headers); // throws if the signature or timestamp is wrong
  } catch {
    return res.status(401).end();
  }
  res.status(200).end();
  handle(event); // after answering
});
```

Without a library:

1. Base64-decode the secret after the `whsec_` prefix. That is the HMAC key.
2. Compute `base64(HMAC-SHA256(key, "<webhook-id>.<webhook-timestamp>.<raw body>"))`.
3. `webhook-signature` holds one or more `v1,<signature>` values separated by spaces. Accept the request if yours matches any of them, compared in constant time.
4. Reject a `webhook-timestamp` more than five minutes from your clock, so an old request cannot be replayed.

Verify the raw body exactly as it arrived. Parsing and re-serialising the JSON changes the bytes and the signature will not match.

## Retries

A delivery that does not get a `2xx` within 10 seconds is retried, up to seven attempts in all:

| Attempt | After the one before |
| - | - |
| 1 | Sent straight away |
| 2 | 5 minutes |
| 3 | 30 minutes |
| 4 | 2 hours |
| 5 | 6 hours |
| 6 | 12 hours |
| 7 | 24 hours |

Retries run every 15 minutes, so a retry can arrive up to 15 minutes after its time.

* `webhook-id` is the same on every attempt of one delivery. Use it to ignore one you already handled.
* Events can arrive out of order. Use `timestamp`, or the objects' own timestamps, to order them.
* An endpoint whose deliveries have all failed for three days is switched off, and the page says why. Fix it, then switch it back on. Events from while it was off are not sent. Read them from the [API](https://wiredesk.ai/docs/developers/api.md).

## Delivery log, test and rotate

Each endpoint on the settings page shows its last 10 deliveries: the event, whether it was **Delivered**, **Retrying** or **Failed**, the HTTP status, the number of attempts and the error. The log keeps 30 days.

* **Send test event** sends a signed `webhook.test` event straight away and tells you how it went. It is sent even when the endpoint is switched off, so you can check a fix first. It is never retried, and a failed test does not count toward the three days. Changing the endpoint's address restarts that count.
* **Rotate secret** gives you a new signing secret. For the next 24 hours every delivery carries two signatures, one per secret, so you can deploy the new secret without failing any events. After that only the new one signs. Rotating again within those 24 hours replaces only the new secret: the one you started with keeps signing until its 24 hours are up.
* The signing secret is shown once, when you add the endpoint or rotate it. If you lose it, rotate.
* Switching an endpoint off stops its deliveries. **Remove** deletes it and its log.

## Related

* [REST API](https://wiredesk.ai/docs/developers/api.md)
* [Notifications](https://wiredesk.ai/docs/workspace/notifications.md), for email and text alerts to your team
* [Security model](https://wiredesk.ai/docs/developers/security-model.md)


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