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

# Webhooks

> How the MessageDesk Developer API delivers workspace events to a subscribed URL: transport, reliability, and the payload for each event.

# Webhooks

A **subscription** ties a URL to one event name in one workspace. Each time that event happens, MessageDesk sends an HTTP `POST` to the URL. This is how instant triggers work: an integration provider subscribes when you turn an automation on and unsubscribes when you turn it off.

<Note>
  **These are the Developer API's event subscriptions.** The [Send Webhook relay action](/relays/webhooks#outbound-webhooks) is a separate feature with its own endpoint URL, signing secret and payload, configured in [Developer Tools](/settings/workspace-settings/developer-tools).
</Note>

***

## Managing subscriptions

| Endpoint                                                                                  | What it does                                                                                          |
| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
| [Subscribe to an event](/developer/api-reference/endpoints/subscribe-to-an-event)         | Registers a URL for one event name in one workspace and returns the subscription, including its `id`. |
| [Unsubscribe from an event](/developer/api-reference/endpoints/unsubscribe-from-an-event) | Deletes a subscription by `id` and stops deliveries to its URL.                                       |

One subscription covers one event name. Subscribe once per event you want. Keep the returned `id`; it's the only handle for unsubscribing.

***

## Delivery

| Property              | Value                                                |
| --------------------- | ---------------------------------------------------- |
| **Method**            | `POST`                                               |
| **`Content-Type`**    | `application/json`                                   |
| **Body**              | The event payload below, with no envelope.           |
| **Expected response** | `200 OK`. Anything else counts as a failed delivery. |

Deliveries aren't signed. Subscription URLs are created only through an authenticated call and providers use unguessable hook URLs, so treat the URL itself as the secret. If you point a subscription at your own endpoint, accept only the payload shapes below and check `workspaceId` before acting on anything.

***

## Reliability and limits

Here's how delivery actually behaves, so you can decide what to trust it with.

* **Events are pushed as they happen.** There's no polling interval.
* **We don't retry.** If a delivery fails, that event is gone. There's no backoff and no queue holding it for later.
* **Events deliver once.** There's no event ID or idempotency key in the payload; if you need a dedupe key, build one from the payload's fields.
* **Order isn't guaranteed.** Two events fired moments apart can arrive out of order. Don't depend on Contact Created landing before Contact Updated.
* **Actions fire triggers.** An action taken through the API looks the same as one taken by a person, so Send a message fires `MessageSent`, and Create or update a contact fires `ContactCreated` or `ContactUpdated`. There are no loop guards on our side.

<Warning>
  **Don't use a webhook subscription as your system of record.** If an event has to reach another system every single time, reconcile against MessageDesk on a schedule as well, or [talk to us](mailto:support@messagedesk.com) about your requirements.
</Warning>

***

## Events and payloads

Only the events below are delivered. Subscribing to any other event name is accepted but never fires. Payloads carry the event itself and not much else; if you need the contact behind a message, look it up with a [Find contacts](/developer/api-reference/endpoints/find-contacts) call.

<Note>
  **See a real payload before any event fires.** Each event has a matching **Sample** endpoint under Triggers, for example [Sample inbound messages](/developer/api-reference/endpoints/sample-inbound-messages). It returns up to three of the workspace's most recent records formatted exactly as the webhook will deliver them. If the workspace has none yet, it returns three generated placeholder records in the same shape. Providers call these to show sample data while you build an automation; they're not a history API.
</Note>

<AccordionGroup>
  <Accordion title="MessageReceived, MessageSent">
    Schema: `DeveloperProviderMessageWebhook`

    ```json theme={null}
    {
      "workspaceId": "01HZX3K9Q7W8E2R4T6Y8U0I1O2",
      "conversationId": "01HZX3M1A2B3C4D5E6F7G8H9J0",
      "from": "+17755550123",
      "to": "+17755550456",
      "text": "Thanks, see you at 3pm.",
      "createdAt": "2026-09-22T17:04:11Z"
    }
    ```

    For `MessageReceived`, `from` is the contact and `to` is your workspace number. For `MessageSent` the two are reversed.
  </Accordion>

  <Accordion title="ContactCreated, ContactUpdated, ContactDeleted">
    Schema: `DeveloperProviderContactWebhook`

    ```json theme={null}
    {
      "id": "01HZX3N5K6L7M8N9P0Q1R2S3T4",
      "workspaceId": "01HZX3K9Q7W8E2R4T6Y8U0I1O2",
      "firstName": "Dana",
      "lastName": "Whitfield",
      "email": "dana@example.com",
      "phone": "+17755550456",
      "createdAt": "2026-09-22T17:04:11Z",
      "properties": { "Plan": "Pro", "Region": "West" }
    }
    ```

    Contacts are the exception to the thin-payload rule: `properties` always carries the workspace's custom fields, keyed by field name.
  </Accordion>

  <Accordion title="CommentCreated">
    Schema: `DeveloperProviderCommentWebhook`

    ```json theme={null}
    {
      "workspaceId": "01HZX3K9Q7W8E2R4T6Y8U0I1O2",
      "conversationId": "01HZX3M1A2B3C4D5E6F7G8H9J0",
      "text": "Following up tomorrow.",
      "createdAt": "2026-09-22T17:04:11Z"
    }
    ```
  </Accordion>

  <Accordion title="CallReceived">
    Schema: `DeveloperProviderCallWebhook`

    ```json theme={null}
    {
      "workspaceId": "01HZX3K9Q7W8E2R4T6Y8U0I1O2",
      "from": "+17755550456",
      "to": "+17755550123",
      "createdAt": "2026-09-22T17:04:11Z"
    }
    ```

    Fires only on numbers where MessageDesk handles both voice and texting. Hosted numbers, where your original carrier still takes calls, don't fire it.
  </Accordion>

  <Accordion title="OptOutCreated, OptOutDeleted">
    Schema: `DeveloperProviderOptOutWebhook`

    ```json theme={null}
    {
      "workspaceId": "01HZX3K9Q7W8E2R4T6Y8U0I1O2",
      "conversationId": "01HZX3M1A2B3C4D5E6F7G8H9J0",
      "optOutReason": "STOP",
      "inboundAddress": "+17755550456",
      "createdAt": "2026-09-22T17:04:11Z"
    }
    ```
  </Accordion>
</AccordionGroup>

***

## See also

* [Subscribe to an event](/developer/api-reference/endpoints/subscribe-to-an-event) and [Unsubscribe from an event](/developer/api-reference/endpoints/unsubscribe-from-an-event).
* [Relays → Send Webhook](/relays/webhooks#outbound-webhooks). The signed, relay-driven webhook for your own systems.
