# Webhooks

Webhooks let GetScaled push real-time event notifications to your application as messages are delivered, opened, clicked, bounced, replied to, unsubscribed, or as campaigns complete. Instead of polling the API, you register an HTTPS endpoint and GetScaled `POST`s a signed JSON payload to it whenever a subscribed event occurs.

## Registering a webhook

Create a webhook subscription with `POST /api/v1/webhooks`, supplying the `url` to deliver to and the `events` you want to receive:

```bash
curl -X POST https://api.getscaled.com/api/v1/webhooks \
  -H "Authorization: Bearer gsk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.example.com/hooks/getscaled",
    "events": [
      "email.delivered",
      "email.opened",
      "email.clicked",
      "email.bounced",
      "contact.unsubscribed",
      "campaign.complete",
      "reply.received"
    ]
  }'
```

The endpoint must be publicly reachable over HTTPS and respond with a `2xx` status to acknowledge receipt. You can list, update, and delete webhooks through the same `/api/v1/webhooks` resource.

## Event list

| Event | Fires when |
|---|---|
| `email.delivered` | An email is accepted by the recipient's mail server |
| `email.opened` | A recipient opens an email |
| `email.clicked` | A recipient clicks a tracked link |
| `email.bounced` | An email hard- or soft-bounces |
| `contact.unsubscribed` | A contact opts out / unsubscribes |
| `campaign.complete` | A campaign finishes sending to its audience |
| `reply.received` | An inbound reply is received for a message |

## Example payloads

Every webhook delivery is a JSON object with at least an event `type`, a unique event `id`, a `created_at` timestamp, and a `data` object carrying the event-specific fields.

### `email.delivered`

```json
{
  "id": "evt_01HZX9ABCDEF",
  "type": "email.delivered",
  "created_at": "2026-06-06T14:32:10Z",
  "data": {
    "message_id": "msg_8f2c1a",
    "campaign_id": "cmp_3b91",
    "contact_id": "con_77ad",
    "email": "jane@example.com",
    "channel": "email",
    "delivered_at": "2026-06-06T14:32:09Z"
  }
}
```

### `reply.received`

```json
{
  "id": "evt_01HZXA12345",
  "type": "reply.received",
  "created_at": "2026-06-06T15:04:51Z",
  "data": {
    "reply_id": "rpl_91be",
    "message_id": "msg_8f2c1a",
    "campaign_id": "cmp_3b91",
    "contact_id": "con_77ad",
    "from": "jane@example.com",
    "subject": "Re: Following up",
    "snippet": "Thanks for reaching out — yes, let's set up a call.",
    "received_at": "2026-06-06T15:04:50Z"
  }
}
```

## Signature verification

Webhook payloads are **signed**. Each delivery includes a signature header derived from the raw request body and a signing secret associated with your webhook. To verify a delivery:

1. Read the **raw** request body exactly as received (do not re-serialize it).
2. Compute the expected signature over that raw body using your webhook's signing secret.
3. Compare it to the signature header using a constant-time comparison.
4. Reject the request if they do not match.

Verifying the signature ensures the request genuinely came from GetScaled and was not tampered with in transit. Never act on an unverified webhook.

## Retry behavior

If your endpoint does not return a `2xx` (for example, it returns a `5xx`, times out, or is unreachable), GetScaled **retries delivery with exponential backoff** over a series of attempts. To handle retries safely:

- **Be idempotent.** Use the event `id` to de-duplicate; the same event may be delivered more than once.
- **Acknowledge fast.** Return `2xx` quickly and do heavy processing asynchronously, so deliveries don't time out.
- **Expect ordering gaps.** Events generally arrive in order but retries can reorder them; rely on timestamps in `data`, not arrival order.

You can send a test event to a registered webhook to confirm your endpoint and signature verification are working before relying on live traffic.

See also the developer landing at [https://getscaled.com/developers.md](https://getscaled.com/developers.md) and the OpenAPI spec at [https://api.getscaled.com/openapi.json](https://api.getscaled.com/openapi.json). Questions: [developers@getscaled.com](mailto:developers@getscaled.com).
