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

# Webhook Events

> Receive real-time delivery status, reply, and opt-out events

Trackly SMS sends webhook events to notify you of message delivery updates, inbound replies, and opt-out changes. Events are delivered to your configured webhook URLs via HTTP POST.

<Note>
  Webhooks are configured per sending list in the dashboard under your list's **Delivery Forwarding** settings. Each webhook subscribes to one or more event types, and you can send a test event from the dashboard to verify your endpoint.
</Note>

## Event Types

| Event         | Trigger                                                                     |
| ------------- | --------------------------------------------------------------------------- |
| `delivery`    | A delivery status update was received for a message you sent                |
| `reply`       | A contact sent an inbound SMS to your list number                           |
| `unsubscribe` | A contact opted out (STOP reply, spam complaint, or repeated hard failures) |

## Request Format

Events are batched: each POST contains an `events` array with one or more events that occurred in the same processing cycle. Cycles run every few seconds, so events from the same short window arrive together in one POST (see [Delivery Semantics](#delivery-semantics)). Each event has a `type`, an ISO-8601 `timestamp`, and a `data` object whose fields depend on the event type.

```
POST https://your-webhook-url.com/trackly
Content-Type: application/json
X-Webhook-Secret: <your webhook secret, if configured>
```

```json theme={null}
{
  "events": [
    {
      "type": "delivery",
      "timestamp": "2026-07-06T10:30:00.000000",
      "data": {
        "message_id": "AbC12345",
        "provider_message_id": "O3iFf1Eu",
        "from": "+18005551234",
        "to": "+14155551234",
        "status": "delivered"
      }
    }
  ]
}
```

## Delivery Event

Sent when a delivery status update is received for a message you sent.

<Note>
  Not every route currently emits `delivery` events. Lists on routes without delivery-event support emit `reply` and `unsubscribe` events only.
</Note>

| Field                 | Description                                                                                                                                                                                                                                                                                                                                                                                                |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `message_id`          | The message ID returned by the [single-send API](/api-reference/v2/messages/send-single) when you submitted the message — use this to correlate the delivery status with your original send. (`/v2/send/bulk` does not currently return per-message IDs, so bulk senders should correlate on `to` + timestamps or use single sends.) May be `null` in rare cases; `provider_message_id` is always present. |
| `provider_message_id` | The carrier-route message reference from the delivery receipt.                                                                                                                                                                                                                                                                                                                                             |
| `from`                | Your sending list number (E.164).                                                                                                                                                                                                                                                                                                                                                                          |
| `to`                  | The recipient (E.164).                                                                                                                                                                                                                                                                                                                                                                                     |
| `status`              | Delivery status — see below.                                                                                                                                                                                                                                                                                                                                                                               |
| `segments`            | Number of SMS segments (present on routes that report it).                                                                                                                                                                                                                                                                                                                                                 |

### Delivery Statuses

| Status                          | Description                                                             |
| ------------------------------- | ----------------------------------------------------------------------- |
| `delivered`                     | Message delivered to the recipient's handset                            |
| `undelivered` / `undeliverable` | Message could not be delivered                                          |
| `expired`                       | Message expired before it could be delivered (e.g. handset unreachable) |
| `rejected`                      | Message rejected by the carrier                                         |
| `failed`                        | Message failed downstream                                               |
| `pending`                       | Message is still being processed                                        |
| `unknown`                       | Status could not be determined from the receipt                         |

`delivered` is the success terminal state. The exact vocabulary varies by carrier route — some routes pass the carrier's own status through verbatim, so you may also see intermediate values (e.g. `sent`, `accepted`). Treat any value other than `delivered` as non-success, and any value you don't recognize as non-terminal.

## Reply Event

Sent when a contact sends an inbound SMS to your list number. **Every** inbound message emits a `reply` event — including opt-out keywords. When a message is an opt-out, an `unsubscribe` event is emitted **in addition** to the `reply`, so a contact who texts `STOP` produces **both** a `reply` and an `unsubscribe` (typically in the same batch). To act on opt-outs, key off the `unsubscribe` event — do not assume a `reply` means the contact stayed subscribed. Replies are also stored and viewable in the dashboard under **Messages > Replies**. Like every event, it arrives inside the `events` array (see [Request Format](#request-format)).

```json theme={null}
{
  "events": [
    {
      "type": "reply",
      "timestamp": "2026-07-06T10:40:00.000000",
      "data": {
        "from": "+18005551234",
        "to": "+14155551234",
        "text": "Yes, I'm interested!"
      }
    }
  ]
}
```

<Note>
  `message_id` is an opaque identifier — do not assume a fixed length or format. Store them as variable-length strings.
</Note>

`from` is your sending list number; `to` is the contact who replied; `text` is their message body.

## Unsubscribe Event

Sent when a contact is opted out — an opt-out keyword reply, a carrier spam complaint, or repeated hard delivery failures. The opt-out matcher is intentionally broad: it recognizes STOP, STOPALL, UNSUBSCRIBE, QUIT, CANCEL, END, REMOVE, OPTOUT and many common variants and phrasings (e.g. "stop texting", "take me off the list", "no more texts"), plus spam-complaint and abuse language — treat these as examples, not an exhaustive list. Like every event, it arrives inside the `events` array (see [Request Format](#request-format)).

```json theme={null}
{
  "events": [
    {
      "type": "unsubscribe",
      "timestamp": "2026-07-06T10:35:00.000000",
      "data": {
        "from": "+18005551234",
        "to": "+14155551234",
        "reason": "replied stop"
      }
    }
  ]
}
```

The contact is immediately marked as unsubscribed (`active: false`) on the corresponding list. Future sends are blocked automatically. See [Compliance](/concepts/compliance) for the auto-removal rules.

<Note>
  **Re-subscription does not emit a dedicated event.** When a previously opted-out contact opts back in (an opt-in keyword such as START, UNSTOP, SUBSCRIBE, OPTIN), they are re-subscribed and sending resumes — but there is **no** `resubscribe` webhook event. The only signal you receive is the ordinary `reply` event carrying their inbound text (e.g. `"Start"`). If you mirror subscription state from webhooks, detect opt-ins from the `reply` body — do not expect a counterpart to the `unsubscribe` event.
</Note>

## Delivery Semantics

* **Batching cadence**: events are collected and flushed on a short polling cycle — roughly every **5 seconds** — so you normally receive an event within seconds of the underlying status change. This is best-effort, not a guaranteed interval: a quiet window produces no POST, and a burst is grouped into a single POST (tens of events).
* **Timeout**: your endpoint must respond within **10 seconds**. Return a 2xx status quickly and process events asynchronously.
* **No retries**: event delivery is fire-and-forget. A non-2xx response or timeout is logged but not retried.
* **At-least-once, and statuses can be revised**: carrier receipts can be redelivered upstream, and a later receipt can revise a message's status (e.g. `delivered` corrected to `failed`). `message_id` identifies the original send — **not** a unique event. To drop true duplicates, dedupe on `message_id` + `status` (or `provider_message_id` + `status`) and let the latest received status win.
* **Authentication**: if your webhook has a secret configured, every request includes it in the `X-Webhook-Secret` header — compare it against your stored value before trusting the payload.

## Next Steps

<CardGroup cols={2}>
  <Card title="Webhooks Integration" icon="webhook" href="/integrations/webhooks">
    Configure webhook endpoints
  </Card>

  <Card title="Send Message" icon="paper-plane" href="/api-reference/v2/messages/send-single">
    Send a message to trigger events
  </Card>
</CardGroup>
