Generated reference
Outbound webhook contract
This English reference is generated from the reviewed, tracked implementation contract.
Contract sha256:6ca4835ad0f5cd8c5890b3a281e92efd417607d0c8b41d26d481164b4f912ca7
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
{
"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
{
"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
{
"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
{
"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
{
"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": "[email protected]",
"newsletter_accepted": true
}
}
}
Legacy record-change event
{
"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
{
"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"
}
}