# Webhooks

Source: https://docs.revoplyai.com/webhooks/

> Events we post to your HTTPS endpoint as they happen, signed so you can verify them, delivered at least once.

When something happens in your account (a customer writes, a conversation is handed to
your team, an appointment is booked) we send a `POST` with a JSON body to every endpoint
subscribed to that event. Webhooks are part of the Business plan; an Owner or Admin sets
them up under **Integrations → Webhooks**.

## The envelope [#the-envelope]

Every event has the same outer shape. `data` depends on `type`; each
[event page](/webhooks/events/) documents it.

```json
{
  "id": "evt_5dabddce7c097ab703f21e8fdf0ab52b",
  "type": "message.received",
  "apiVersion": "2026-10",
  "createdAt": "2026-10-01T09:30:05+00:00",
  "test": false,
  "company": {
    "id": "0b3d6f2e-8a41-4c75-9e20-5f7a1c3d9b84",
    "name": "متجر الرياض"
  },
  "data": {
    "conversation": {
      "id": "WhatsApp_3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
      "channelId": "7c1e4b2a-9d3f-4e58-a6b1-2f0c8d7e5a13",
      "channelType": "whatsapp"
    },
    "contact": {
      "id": "3f6a2d18-0b7c-4e91-8a5d-c24e1f9b7a60",
      "name": "نورة العتيبي",
      "phone": "+966501234567",
      "whatsAppId": "966501234567"
    },
    "message": {
      "id": "m_wamid.HBgMOTY2NTAxMjM0NTY3FQIAEhggQTNFQjU2RkQ5RTcyOEIyRDQ1",
      "text": "السلام عليكم، متى يوصل طلبي رقم 10482؟",
      "type": "text",
      "receivedAt": "2026-10-01T09:30:00+00:00"
    }
  }
}
```

| Field          | Type             | Meaning                                                                               |
| -------------- | ---------------- | ------------------------------------------------------------------------------------- |
| `id`           | string           | The event's id, `evt_…`. The same on every retry and resend: deduplicate on it.       |
| `type`         | string           | The event, such as `message.received`. Acknowledge types you do not know.             |
| `apiVersion`   | string           | The shape of `data`, currently `2026-10`. See [Versioning](/get-started/versioning/). |
| `createdAt`    | string           | When it happened, UTC.                                                                |
| `test`         | boolean          | `true` for what **Send test** posts: sample data about nobody real. Always present.   |
| `company.id`   | string (UUID)    | The account or [project](/get-started/introduction/#concepts) it happened in.         |
| `company.name` | string or `null` | That company's name.                                                                  |
| `data`         | object           | The event's details.                                                                  |

## Headers [#headers]

| Header                | Value                                                                            |
| --------------------- | -------------------------------------------------------------------------------- |
| `Content-Type`        | `application/json; charset=utf-8`                                                |
| `User-Agent`          | `RevoplyAI-Webhooks/1.0`                                                         |
| `X-Revoply-Signature` | `t=<unix seconds>,v1=<hex HMAC-SHA256>`; see [Signatures](/webhooks/signatures/) |
| `X-Revoply-Event`     | The event's `type`, so you can route before parsing                              |
| `X-Revoply-Delivery`  | The delivery's id (a UUID), as the delivery log shows it; quote it to support    |

## Delivery [#delivery]

* **Answer with any `2xx` within 10 seconds.** Acknowledge first, then do the work: a
  receiver that holds the request open while it calls other systems will time out.
  Anything else (another status, a redirect, a timeout, a refused connection) is a failure
  and is [retried](/webhooks/retries-and-delivery-log/).
* **At least once.** An event can arrive more than once, for example when your `2xx` was
  lost on the way back. Every delivery of it carries the same `id`; record the ids you have
  handled and skip repeats.
* **No ordering guarantee.** Deliveries are sent in parallel and retried on a schedule, so
  a later event can arrive before an earlier one. Order by `createdAt`, and do not assume,
  for example, that `conversation.started` arrives before the first `message.received`.
* **Verify before you trust.** Check the signature and its timestamp on every request;
  see [Verify webhook signatures](/guides/verify-webhook-signatures/).

## When nothing is sent [#when-nothing-is-sent]

* **Switched-off endpoints** receive nothing; events that happen while an endpoint is off
  are not sent to it later.
* **Outside the Business plan**, endpoints are kept but receive nothing until the account
  is back on it.
* **A pause on our side.** We can pause all calls to merchants' systems, for example
  during an incident. The Webhooks page then shows a notice; events that happen during a
  pause are not sent later, and tests and resends are refused until it ends.

## Events [#events]

| Event                                                              | What happened                                 |
| ------------------------------------------------------------------ | --------------------------------------------- |
| [`message.received`](/webhooks/events/message-received/)           | A customer wrote.                             |
| [`conversation.started`](/webhooks/events/conversation-started/)   | A new conversation began.                     |
| [`conversation.handover`](/webhooks/events/conversation-handover/) | The conversation was handed to the team.      |
| [`conversation.assigned`](/webhooks/events/conversation-assigned/) | The conversation was assigned, or unassigned. |
| [`conversation.resolved`](/webhooks/events/conversation-resolved/) | The conversation was resolved or closed.      |
| [`contact.created`](/webhooks/events/contact-created/)             | A contact was added.                          |
| [`contact.updated`](/webhooks/events/contact-updated/)             | A contact's details changed.                  |
| [`contact.opted_out`](/webhooks/events/contact-opted-out/)         | A contact opted out of messages.              |
| [`lead.qualified`](/webhooks/events/lead-qualified/)               | A conversation became a qualified lead.       |
| [`appointment.booked`](/webhooks/events/appointment-booked/)       | An appointment was booked.                    |
| [`appointment.cancelled`](/webhooks/events/appointment-cancelled/) | An appointment was cancelled.                 |
| [`flow.completed`](/webhooks/events/flow-completed/)               | An automation finished.                       |
| [`flow.failed`](/webhooks/events/flow-failed/)                     | An automation failed.                         |
| [`ping`](/webhooks/events/ping/)                                   | A test from the dashboard.                    |
