Webhook Documentation
When a subscribed event occurs, a POST request is sent to your endpoint URL within a few minutes.
The request body is a JSON object with an event_type field that identifies what happened,
so a single URL can receive all event types and route them internally.
Request format
Every delivery sends the following HTTP headers:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Webhook-Event | The event type string, e.g. ticket.purchased |
X-Webhook-Signature | sha256=<HMAC-SHA256 hex digest> |
Verifying the signature
Each subscription has a signing secret shown on its edit page.
Compute HMAC-SHA256 of the raw request body using that secret and compare it to
the value in X-Webhook-Signature (after stripping the sha256= prefix).
Always use a constant-time comparison to prevent timing attacks.
PHP
$secret = 'your_signing_secret';
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';
$expect = 'sha256=' . hash_hmac('sha256', $payload, $secret);
if (! hash_equals($expect, $sig)) {
http_response_code(401);
exit;
}
Node.js
const crypto = require('crypto');
const secret = 'your_signing_secret';
const sig = req.headers['x-webhook-signature'];
const digest = 'sha256=' +
crypto.createHmac('sha256', secret)
.update(req.rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(
Buffer.from(digest),
Buffer.from(sig))) {
return res.status(401).end();
}
Envelope
Every payload shares the same top-level envelope. The data object varies by event type.
{
"event_type": "ticket.purchased", // string — one of the event types below
"event_id": 42, // integer — the event this subscription belongs to
"webhook_id": "550e8400-...", // string — UUID v4, unique per delivery attempt
"created_at": "2026-06-23T10:30:00-06:00", // ISO-8601 with the event timezone offset
"data": { ... }
}
Event types
| Type | Trigger | data shape |
|---|---|---|
ticket.purchased |
A ticket order was completed and payment collected. | order + attendee |
ticket.exchanged |
A ticket was exchanged for a different event time or type. | order + attendee |
ticket.transferred |
A ticket was transferred to a new holder. | order + attendee |
ticket.canceled |
A ticket was canceled. | order + attendee |
ticket.refunded |
A refund was issued for a ticket order. | order + attendee |
attendee.checked_in |
A ticket holder was scanned in at the gate. | order + attendee |
registration.created |
A new registration was confirmed. | order + registration |
registration.updated |
An existing registration was modified. | order + registration |
registration.canceled |
A registration was canceled. Also triggers alongside registration.refunded when a refund includes a cancellation. | order + registration |
registration.refunded |
A refund was issued for a registration. | order + registration |
registration.checked_in |
A participant was checked in at the event. | order + registration |
registration.transferred |
A registration was transferred to a different category. | order + registration |
registration.deferred |
A registration was deferred to a future year. | order + registration |
waiver.signed |
A participant signed a waiver, either during registration checkout or per-attendee for a ticket order after purchase. | registration + waiver |
product.purchased |
A merchandise order was completed and payment collected. | order + product |
product.fulfilled |
A purchased product was marked as fulfilled (picked up or shipped). | order + product |
donation.made |
A donation to a charity was completed as part of an order. | order + donation |
sponsorship.purchased |
A sponsorship was purchased for an event. | order + sponsorship |
Payload reference
The ticket and registration words are fixed for API consistency, event customizations to display text are ignored here.
Ticket events (ticket.* and attendee.checked_in) include an order and an attendee object.
Registration events (registration.*) include an order and a registration object.
The order object is the same shape in both cases.
The waiver.signed event includes a registration and a waiver object (no order).
product.purchased and product.fulfilled include an order and a product object.
donation.made includes an order and a donation object.
sponsorship.purchased includes an order and a sponsorship object.
Order object
| Field | Type | Notes |
|---|---|---|
id | integer | Internal order ID |
finish_date | ISO-8601 string | When payment was collected and the order completed |
billing_name | string | |
billing_email | string | |
billing_address | string | |
billing_city | string | |
billing_state | string | Two-letter state code |
billing_province | string|null | Non-US province, if applicable |
billing_zip | string | |
billing_country | string | Two-letter country code |
order_type | string | Human-readable order type name |
payment_type | string | e.g. Credit Card, Free |
ccnumber | string | Masked card number, e.g. xxxx-xxxx-xxxx-4242 |
transaction_id | string|null | Processor transaction reference |
ticket_total | decimal string | Sum of ticket prices |
registration_total | decimal string | Sum of registration prices (ticket events only) |
merchandise_total | decimal string | Ticket events only |
tax_total | decimal string | |
marketplace_tax_total | decimal string | |
discount_total | decimal string | Promo codes and referral discounts |
fee_total | decimal string | Processing fees |
bonus_fee_total | decimal string | Event-specific bonus fees (subset of fee_total) |
gift_card_payment | decimal string | Amount paid with a gift card |
order_total | decimal string | Grand total charged |
Ticket event payload
Used by: ticket.purchased, ticket.exchanged, ticket.transferred, ticket.canceled, ticket.refunded, attendee.checked_in
{
"event_type": "ticket.purchased",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2026-06-23T10:30:00-06:00",
"data": {
"order": {
"id": 456,
"finish_date": "2026-06-23T10:30:00-06:00",
"billing_name": "Jane Smith",
"billing_email": "jane@example.com",
"order_type": "Ticket Order",
"payment_type": "Credit Card",
"ccnumber": "xxxx-xxxx-xxxx-4242",
"ticket_total": "75.00",
"fee_total": "6.50",
"order_total": "81.50",
...
},
"attendee": {
"id": 789,
"order_id": 456,
"name": "Jane Smith",
"email": "jane@example.com",
"phone": "5551234567",
"status": "Active",
"billing_name": "Jane Smith",
"billing_email": "jane@example.com",
"order_date": "2026-06-23T10:30:00-06:00",
"tickets": [
{
"id": 101,
"order_id": 456,
"event_time_name": "5K Run",
"event_time_start": "2026-07-04T08:00:00-06:00",
"section_name": "General Admission",
"row_name": null,
"seat_name": null,
"price_label": "Adult",
"price": "35.00",
"tax": "0.00",
"fee": "3.25"
}
]
}
}
}
Attendee fields
| Field | Type | Notes |
|---|---|---|
id | integer | Ticket group ID — uniquely identifies this attendee record |
order_id | integer | The order this attendee belongs to |
name | string | Attendee name as entered during checkout |
email | string | Attendee email |
phone | string|null | |
status | string | Ticket group status name |
billing_name | string | Name on the order (may differ from attendee name) |
billing_email | string | Email on the order |
order_date | ISO-8601 string | When the order was completed |
tickets | array | One entry per ticket in this group — see below |
Ticket fields (within attendee.tickets[])
| Field | Type | Notes |
|---|---|---|
id | integer | Unique ticket ID — use this to identify an individual ticket |
order_id | integer | The parent order — ties this ticket to its group and billing record |
event_time_name | string | Name of the event time group, e.g. 5K Run |
event_time_start | ISO-8601|null | Start time of the event time; null for reserved-seat tickets with a custom reservation window |
section_name | string|null | Venue section, if assigned |
row_name | string|null | Seat row, if assigned |
seat_name | string|null | Specific seat, if assigned |
price_label | string|null | Ticket tier label, e.g. Adult, VIP |
price | decimal string | Face value of the ticket |
tax | decimal string | |
fee | decimal string | Processing fee for this ticket |
Registration event payload
Used by: registration.created, registration.updated, registration.canceled, registration.refunded, registration.checked_in, registration.transferred, registration.deferred
{
"event_type": "registration.created",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-06-23T11:00:00-06:00",
"data": {
"order": {
"id": 457,
"finish_date": "2026-06-23T11:00:00-06:00",
"billing_name": "John Doe",
"billing_email": "john@example.com",
"order_type": "Registration",
"payment_type": "Credit Card",
"ccnumber": "xxxx-xxxx-xxxx-1234",
"registration_total": "45.00",
"fee_total": "4.00",
"order_total": "49.00",
...
},
"registration": {
"id": 123,
"order_id": 457,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"phone": "5559876543",
"address": "123 Main St",
"city": "Boulder",
"state": "Colorado",
"province": null,
"zip": "80301",
"country_code": "US",
"gender": "M",
"birthdate": "1988-04-15",
"bib_number": "1042",
"category": "5K Adult",
"registration_date": "2026-06-23T11:00:00-06:00",
"checked_in_datetime": null
}
}
}
Registration fields
| Field | Type | Notes |
|---|---|---|
id | integer | Unique registration ID |
order_id | integer | The order this registration belongs to |
first_name | string | |
last_name | string | |
email | string | |
phone | string|null | |
address | string|null | |
city | string|null | |
state | string|null | Full state name |
province | string|null | Non-US province |
zip | string|null | |
country_code | string | Two-letter ISO country code |
gender | string|null | M or F |
birthdate | date|null | YYYY-MM-DD |
bib_number | string|null | Assigned race bib; null until assigned |
category | string|null | Registration category/division name |
registration_date | ISO-8601 string | When the registration was created |
checked_in_datetime | ISO-8601|null | Set when the participant checks in on race day; null otherwise |
Waiver event payload
Used by: waiver.signed
This event does not include an order object. It fires once per waiver per registration at the moment of signing.
{
"event_type": "waiver.signed",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440002",
"created_at": "2026-06-23T11:00:00-06:00",
"data": {
"registration": {
"id": 123,
"order_id": 457,
"first_name": "John",
"last_name": "Doe",
"email": "john@example.com",
"registration_date": "2026-06-23T11:00:00-06:00"
},
"waiver": {
"waiver_id": 7,
"waiver_version_id": 12,
"waiver_name": "General Liability Waiver",
"signed_at": "2026-06-23T11:00:05-06:00"
}
}
}
Waiver fields
| Field | Type | Notes |
|---|---|---|
waiver_id | integer | ID of the waiver template |
waiver_version_id | integer | ID of the specific version that was signed; versions are created when the waiver text is edited |
waiver_name | string | Name of the waiver |
signed_at | ISO-8601 string | Timestamp when the signature was recorded |
Product event payload
Used by: product.purchased, product.fulfilled
{
"event_type": "product.purchased",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440003",
"created_at": "2026-06-23T11:00:00-06:00",
"data": {
"order": {
"id": 458,
"finish_date": "2026-06-23T11:00:00-06:00",
"billing_name": "Jane Smith",
"billing_email": "jane@example.com",
"order_type": "Merchandise Order",
"payment_type": "Credit Card",
"ccnumber": "xxxx-xxxx-xxxx-4242",
"merchandise_total": "25.00",
"fee_total": "2.00",
"order_total": "27.00",
...
},
"product": {
"id": 321,
"order_id": 458,
"product_name": "Event T-Shirt",
"size": "Large",
"quantity": 1,
"price": "25.00",
"shipping": "0.00",
"tax": "0.00",
"fee": "2.00",
"total": "27.00",
"fulfillment_status": "Unfulfilled"
}
}
}
Product fields
| Field | Type | Notes |
|---|---|---|
id | integer | Unique product purchase ID |
order_id | integer | The order this product purchase belongs to |
product_name | string | |
size | string|null | Selected size, if the product has sizes |
quantity | integer | |
price | decimal string | Unit price |
shipping | decimal string | |
tax | decimal string | |
fee | decimal string | Processing fee |
total | decimal string | Price, shipping, tax, and fee combined |
fulfillment_status | string | Unfulfilled, Fulfilled, or Cancelled |
Donation event payload
Used by: donation.made
{
"event_type": "donation.made",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440004",
"created_at": "2026-06-23T11:00:00-06:00",
"data": {
"order": {
"id": 459,
"finish_date": "2026-06-23T11:00:00-06:00",
"billing_name": "Jane Smith",
"billing_email": "jane@example.com",
"order_type": "Ticket Order",
"payment_type": "Credit Card",
"ccnumber": "xxxx-xxxx-xxxx-4242",
"order_total": "35.00",
...
},
"donation": {
"id": 654,
"order_id": 459,
"charity_name": "Local Food Bank",
"donor_name": "Jane Smith",
"amount": "25.00",
"fee": "2.00",
"total": "27.00",
"comment": "Keep up the great work!"
}
}
}
Donation fields
| Field | Type | Notes |
|---|---|---|
id | integer | Unique donation ID |
order_id | integer | The order this donation belongs to |
charity_name | string | |
donor_name | string|null | |
amount | decimal string | Donation amount before fee |
fee | decimal string | |
total | decimal string | Amount plus fee, if the donor covered the fee |
comment | string|null | Optional message left by the donor |
Sponsorship event payload
Used by: sponsorship.purchased
{
"event_type": "sponsorship.purchased",
"event_id": 42,
"webhook_id": "550e8400-e29b-41d4-a716-446655440005",
"created_at": "2026-06-23T11:00:00-06:00",
"data": {
"order": {
"id": 460,
"finish_date": "2026-06-23T11:00:00-06:00",
"billing_name": "Acme Co.",
"billing_email": "sponsor@acme.example.com",
"order_type": "Sponsorship",
"payment_type": "Credit Card",
"ccnumber": "xxxx-xxxx-xxxx-4242",
"order_total": "500.00",
...
},
"sponsorship": {
"sponsor_id": 88,
"order_id": 460,
"sponsor_name": "Acme Co.",
"category_name": "Gold Sponsor",
"price": "475.00",
"tax": "0.00",
"marketplace_tax": "0.00",
"fee": "25.00"
}
}
}
Sponsorship fields
| Field | Type | Notes |
|---|---|---|
sponsor_id | integer | Unique sponsor ID |
order_id | integer | The order this sponsorship belongs to |
sponsor_name | string | |
category_name | string | Sponsorship tier/category name |
price | decimal string | |
tax | decimal string | |
marketplace_tax | decimal string | |
fee | decimal string | Processing fee |
