# Webhooks

Configure up to five endpoints per workspace. Every delivery carries an HMAC-SHA256 signature over "{timestamp}.{raw body}". Verify it in constant time and reject anything where the timestamp is more than 300 seconds old. Event ids are stable across retries, so your consumer must be idempotent.

```http
Mailhaap-Signature: t=1755792000,v1=5257a869e7ecebeda32affa62cdca3fa...
Mailhaap-Event-Id: evt_01J8XQ...
Mailhaap-Delivery-Attempt: 1
```

## Retry schedule

Seven attempts over 24 hours: immediately, then 30 seconds, 2 minutes, 10 minutes, 1 hour, 6 hours, 24 hours. After seven consecutive failures the endpoint moves to disabled_by_failures and the owner is emailed. Every undelivered event stays fetchable from GET /v1/events, so a disabled endpoint never means lost data.

## Event types

- email.queued · email.sent · email.delivered · email.deferred
- email.bounced · email.complained · email.failed
- email.opened · email.clicked
- contact.unsubscribed
- domain.verified · domain.degraded
- campaign.completed · campaign.throttled · campaign.blocked
- tenant.limit_reached

```json
{
  "id": "evt_01J8XQ...",
  "type": "email.bounced",
  "created_at": "2026-08-21T16:05:02.114Z",
  "api_version": "2026-08-01",
  "data": {
    "message_id": "msg_01J8XQ...",
    "to": "mehmet@example.com",
    "from": "info@abcemlak.com.tr",
    "subject": "Order A-1043 confirmed",
    "tags": [{ "name": "category", "value": "order_confirmation" }],
    "bounce": {
      "class": "hard",
      "subtype": "mailbox_not_found",
      "smtp_code": "550"
    }
  }
}
```

> **A block is not a hard bounce** — bounce.class can be hard, soft, block or technical. A block is a rejection caused by receiver policy or sender reputation — it is about us, not the recipient. Suppressing on a block would destroy your list for a problem that was ours, so we do not, and neither should you.
