# Webhooks overview

Webhooks tell your server what happened: a message was sent, a call ended. mondochat never emails or texts your users. You decide what to send, with your own sender and branding.

Every event is a `POST` with the JSON body `{type, timestamp, data}`. Users appear by their external id, the `sub` you put in client tokens.

```json
{
  "type": "message.created",
  "timestamp": "2026-09-26T12:00:00.000000+00:00",
  "data": {"message_id": "01a0dd...", "conversation_id": "01a0dd...", "from": "alice", "to": "bob", "kind": "text", "first": true, "recipient_online": false, "open_url": "https://www.example.com/en/messages?with=alice"}
}
```

## Before you start

- A dashboard login for the app, and a server that accepts `POST` requests over https.
- A way to read the raw request body, so you can [verify the signature](/webhooks/verify/).
- Locally, `http://127.0.0.1:8081/...` is the only plain http address the local egress proxy lets through.

## Steps

1. Open **Webhooks** in the dashboard and choose **Add endpoint**.
2. Enter the https URL of your server and tick the events it wants.
3. Mark the endpoint **Answers callouts** only if you sell inside the chat or charge calls per minute. An app has at most one enabled callout endpoint.
4. Copy the signing secret (`whsec_...`). It is shown once.
5. [Verify the signature](/webhooks/verify/) of every request before you trust it.
6. Answer with a 2xx status within 15 seconds. Do slow work after you answer.
7. To tell users about messages while the chat is closed, subscribe to `message.created`, `conversation.unread` and `call.ringing`, and send your own mail, push or SMS with `open_url` as the link. For `message.created`, mail at once when `first` is true and `recipient_online` is false.

## Check it works

Choose **Send test event** on the endpoint. mondochat sends one signed `webhook.test` delivery to that endpoint alone and shows your response code. The delivery appears in the delivery list like any other. See [`webhook.test`](/api/webhooks/webhook-test/).

## When it fails

- The delivery list shows every delivery with its status and response code. Replay one from there.
- A non-2xx answer or a timeout is retried on a schedule. See [Delivery and retries](/webhooks/delivery/).
- Answering `410 Gone` disables the endpoint.
- A request that fails your signature check is yours to refuse. See [Verify signatures](/webhooks/verify/).
- A delivery to a private or internal address never leaves: all requests go through an egress proxy that refuses them.

## Limits

- Answer within 15 seconds.
- Message bodies are sent only with the setting `webhooks.message_text`. Photos, videos and voice notes never carry media, URLs or captions. A voice note's `text` is its preview, such as `🎤 0:07`.
- Deliveries can repeat and carry no ordering guarantee. Drop duplicates by the `webhook-id` header.
- Nudity between adults is not a moderation finding, so `moderation.flagged` never fires for it.

## Callouts in short

Callouts ask your server a question and wait for the answer. They are only used when commerce or per-minute calls are on, and they go to the one endpoint marked for callouts. They are signed like webhooks and carry a `webhook-type` header.

| `webhook-type` | We send | You answer |
|---|---|---|
| `catalog` | the viewer, the seller, the conversation and the language | the items for sale and an optional balance |
| `charge` | the purchase, buyer, seller, item and price | a status: `paid`, `insufficient`, `failed` or `pending` |
| `charge_status` | the purchase id | the same as `charge` |
| `call_minute` | the call, the payer, the minute and the price | a status: `paid`, `insufficient` or `failed` |

Answer with 2xx JSON under 64 KB within 5 seconds. A network error is retried once, and anything else is a failure. See [`catalog`](/api/webhooks/catalog/), [`charge`](/api/webhooks/charge/), [`charge_status`](/api/webhooks/charge-status/) and [`call_minute`](/api/webhooks/call-minute/).

## Related topics

- [Event catalog](/reference/webhook-events/)
- [Verify signatures](/webhooks/verify/)
- [Delivery and retries](/webhooks/delivery/)
- [`message.created`](/api/webhooks/message-created/)
- [Errors](/errors/)