---
id: webhooks
title: Outbound webhook contract
---

# Outbound webhook contract

> Generated from `config/webhooks/asyncapi.json`. Do not edit this file directly.

There is no universal webhook envelope. Select the payload family for the event and production delivery path you consume.

## Verification and delivery

Signatures use HMAC-SHA256 over `<unix-seconds>.<raw-json-body>` and are sent as `t=<unix-seconds>,v1=<hex-hmac>`. Verify the signature against the exact request bytes before parsing JSON.

A delivery has 5 total attempts. After a failed initial attempt, retries wait 30, 60, 300, 900 seconds.

The connection deadline is 5 seconds and the response/read deadline is 10 seconds. Verify the signature, enqueue durable processing, and return a 2xx response within the response/read deadline. Do not perform long-running work before acknowledging the delivery.

Integration delivery retries: HTTP 408; HTTP 425; HTTP 429; HTTP 5xx; connection and timeout failures.

Legacy delivery retries: HTTP 408; HTTP 429; HTTP 499; HTTP 5xx; connection and timeout failures.

Test send: The Test button always creates a sample WebhookEvent and uses the legacy delivery route, even when production delivery for the selected event uses the integration route.

## Headers

Common: `Content-Type`, `X-Webhook-Signature`, `X-Webhook-Event-Id`, `X-Webhook-Event-Type`, `X-Webhook-Timestamp`.

Integration aliases: `X-ToucanTix-Event-Id`, `X-ToucanTix-Event-Key`, `X-ToucanTix-Event-Version`, `X-ToucanTix-Occurred-At`, `X-ToucanTix-Delivery-Id`, `X-ToucanTix-Signature`.

Legacy aliases: `X-Toucantix-Webhook-Signature`, `X-Toucantix-Webhook-Timestamp`, `X-Toucantix-Webhook-Sample`.

Custom headers cannot replace these authoritative headers. `Authorization` is allowed.

## Event matrix

### order.settled

Production: integration: canonicalOrder.

Test send: canonicalOrder via legacy delivery.

### offerable.booking.created

Production: integration: legacyOfferableBooking.

Test send: genericOfferableTest via legacy delivery.

### offerable.booking.updated

Production: integration: legacyOfferableBooking.

Test send: genericOfferableTest via legacy delivery.

### offerable.booking.cancelled

Production: integration: legacyOfferableBooking.

Test send: genericOfferableTest via legacy delivery.

### offerable.quota.updated

Production: integration: legacyOfferableQuota.

Test send: legacyOfferableQuota via legacy delivery.

### contact.created

Production: integration: canonicalContact; legacy: legacyRecordChange (Service writes can publish both variants when both subscription paths are active.).

Test send: legacyRecordChange via legacy delivery.

### contact.updated

Production: integration: canonicalContact; legacy: legacyRecordChange (Service writes can publish both variants when both subscription paths are active.).

Test send: legacyRecordChange via legacy delivery.

### contact.deleted

Production: legacy: legacyRecordChange.

Test send: legacyRecordChange via legacy delivery.

### contact.newsletter_consent.changed

Production: integration: canonicalContact.

Test send: legacyRecordChange via legacy delivery.

### contact.subscription.added

Production: integration: canonicalContact.

Test send: legacyRecordChange via legacy delivery.

### contact.subscription.removed

Production: integration: canonicalContact.

Test send: legacyRecordChange via legacy delivery.

### segment.created

Production: legacy: legacyRecordChange.

Test send: legacyRecordChange via legacy delivery.

### segment.updated

Production: legacy: legacyRecordChange.

Test send: legacyRecordChange via legacy delivery.

### segment.deleted

Production: legacy: legacyRecordChange.

Test send: legacyRecordChange via legacy delivery.

### segment.membership.added

Production: integration: canonicalSegmentMembership.

Test send: canonicalSegmentMembership via legacy delivery.

## Payload families

### Canonical order event

```json
{
  "id": "evt_123",
  "key": "order.settled",
  "type": "order.settled",
  "version": 1,
  "occurred_at": "2026-08-29T12:00:00Z",
  "tenant": {
    "id": "42",
    "slug": "museum"
  },
  "data": {
    "order": {
      "id": "100",
      "number": "OR1001",
      "payment_state": "settled"
    },
    "line_items": []
  }
}
```

### Offerable booking event

```json
{
  "id": "whevt_123",
  "type": "offerable.booking.created",
  "created_at": "2026-08-29T12:00:00Z",
  "tenant_id": "42",
  "tenant_slug": "museum",
  "data": {
    "offerable_id": "200",
    "offerable_type": "EVENT",
    "booking": {
      "id": "300",
      "quantity": 2
    },
    "current_attributes": {
      "remaining_quota": 18
    },
    "previous_attributes": {
      "remaining_quota": 20
    }
  }
}
```

### Generic offerable test-send event

```json
{
  "id": "whevt_sample123",
  "type": "offerable.booking.created",
  "created_at": "2026-08-29T12:00:00Z",
  "tenant_id": "42",
  "tenant_slug": "museum",
  "data": {
    "sample": true,
    "offerable_id": "sample-offerable-id",
    "offerable_type": "EVENT",
    "offerable_name": "Sample event",
    "current_attributes": {
      "max_quota": 100,
      "booked_quota": 10,
      "remaining_quota": 90,
      "max_participants": 100
    },
    "previous_attributes": {
      "max_quota": 100,
      "booked_quota": 9,
      "remaining_quota": 91,
      "max_participants": 100
    }
  }
}
```

### Offerable quota event

```json
{
  "id": "whevt_124",
  "type": "offerable.quota.updated",
  "created_at": "2026-08-29T12:00:00Z",
  "tenant_id": "42",
  "tenant_slug": "museum",
  "data": {
    "offerable_id": "200",
    "offerable_type": "EVENT",
    "current_attributes": {
      "max_quota": 100,
      "booked_quota": 12,
      "remaining_quota": 88
    },
    "previous_attributes": {
      "max_quota": 100,
      "remaining_quota": 89
    }
  }
}
```

### Canonical contact event

```json
{
  "id": "evt_125",
  "key": "contact.updated",
  "version": 1,
  "occurred_at": "2026-08-29T12:00:00Z",
  "tenant": {
    "id": "42",
    "slug": "museum"
  },
  "data": {
    "contact": {
      "id": "400",
      "email": "alex@example.com",
      "newsletter_accepted": true
    }
  }
}
```

### Legacy record-change event

```json
{
  "id": "whevt_126",
  "type": "segment.updated",
  "created_at": "2026-08-29T12:00:00Z",
  "tenant_id": "42",
  "tenant_slug": "museum",
  "data": {
    "resource": {
      "id": "500",
      "name": "Members"
    },
    "changes": {
      "name": ["Guests", "Members"]
    }
  }
}
```

### Canonical segment membership event

```json
{
  "id": "evt_127",
  "key": "segment.membership.added",
  "type": "segment.membership.added",
  "version": 1,
  "occurred_at": "2026-08-29T12:00:00Z",
  "tenant": {
    "id": "42",
    "slug": "museum"
  },
  "data": {
    "segment": {
      "id": "500",
      "name": "Members"
    },
    "matched_resource": {
      "type": "Request",
      "id": "600"
    },
    "matched_at": "2026-08-29T12:00:00Z"
  }
}
```
