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.

Compatibility

New fields may be added to any payload object, and new event types may be introduced, at any time without notice. Your implementation should ignore fields and event types it does not recognize, and should not depend on the order of keys or the number of fields in an object.

Existing fields are stable: their names, types, and formats will not change.

Request format

Every delivery sends the following HTTP headers:

HeaderValue
Content-Typeapplication/json
X-Webhook-EventThe event type string, e.g. ticket.purchased
X-Webhook-Signaturesha256=<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, but always includes an event object.

{
  "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

TypeTriggerdata 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. Every event type also includes an event object.

Event object

FieldTypeNotes
idintegerSame as the envelope event_id
namestringEvent name
yearstringEvent year, e.g. 2026; combine with name to identify a specific year of a recurring event
start_dateISO-8601|nullStart date and time of the event
end_dateISO-8601|nullEnd date and time of the event; the same as start_date for single-day events

Order object

FieldTypeNotes
idintegerInternal order ID
finish_dateISO-8601 stringWhen payment was collected and the order completed
billing_namestring
billing_emailstring
billing_addressstring
billing_citystring
billing_statestringTwo-letter state code
billing_provincestring|nullNon-US province, if applicable
billing_zipstring
billing_countrystringISO 3166-1 alpha-2 country code, e.g. US
order_typestringHuman-readable order type name
payment_typestringe.g. Credit Card, Free
ccnumberstringMasked card number, e.g. xxxx-xxxx-xxxx-4242
transaction_idstring|nullProcessor transaction reference
ticket_totaldecimal stringSum of ticket prices
registration_totaldecimal stringSum of registration prices (ticket events only)
merchandise_totaldecimal stringTicket events only
tax_totaldecimal string
marketplace_tax_totaldecimal string
discount_totaldecimal stringPromo codes and referral discounts
fee_totaldecimal stringProcessing fees
bonus_fee_totaldecimal stringEvent-specific bonus fees (subset of fee_total)
gift_card_paymentdecimal stringAmount paid with a gift card
order_totaldecimal stringGrand total charged

Promo object

Included as promo on tickets, registrations, and products. null when no promo code was used.

FieldTypeNotes
codestringThe promo code entered
discountdecimal stringAmount discounted from this item by the promo code

Custom answer object

Included in the answers array on tickets and registrations, one entry per answer to the event's custom questions. Checkbox questions produce one entry per selected option, all sharing the same question_id. Questions that were not answered are omitted.

FieldTypeNotes
question_idintegerUnique question ID; stable even if the question text is edited
questionstringQuestion text
answerstring|nullSelected option text for multiple-choice questions, otherwise the entered text; null for file uploads
additional_textstring|nullExtra text entered with a selected option, when the option allows it

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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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",
          "sms_marketing_opt_in": true,
          "promo": {
            "code":     "SUMMER10",
            "discount": "3.50"
          },
          "answers": [
            {
              "question_id":     15,
              "question":        "T-shirt size",
              "answer":          "Medium",
              "additional_text": null
            }
          ]
        }
      ]
    }
  }
}

Attendee fields

FieldTypeNotes
idintegerTicket group ID — uniquely identifies this attendee record
order_idintegerThe order this attendee belongs to
namestringAttendee name as entered during checkout
emailstringAttendee email
phonestring|null
statusstringTicket group status name
billing_namestringName on the order (may differ from attendee name)
billing_emailstringEmail on the order
order_dateISO-8601 stringWhen the order was completed
ticketsarrayOne entry per ticket in this group — see below

Ticket fields (within attendee.tickets[])

FieldTypeNotes
idintegerUnique ticket ID — use this to identify an individual ticket
order_idintegerThe parent order — ties this ticket to its group and billing record
event_time_namestringName of the event time group, e.g. 5K Run
event_time_startISO-8601|nullStart time of the event time; null for reserved-seat tickets with a custom reservation window
section_namestring|nullVenue section, if assigned
row_namestring|nullSeat row, if assigned
seat_namestring|nullSpecific seat, if assigned
price_labelstring|nullTicket tier label, e.g. Adult, VIP
pricedecimal stringFace value of the ticket
taxdecimal string
feedecimal stringProcessing fee for this ticket
sms_marketing_opt_inbooleanWhether the ticket holder opted in to SMS marketing (text alerts) for this ticket; false if no opt-in was recorded
promoobject|nullPromo code applied to this ticket — see Promo object
answersarrayCustom question answers — see Custom answer object. Answers to once-per-order questions appear on only one ticket in the order.

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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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",
      "sms_marketing_opt_in": true,
      "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,
      "promo":               null,
      "answers": [
        {
          "question_id":     21,
          "question":        "How did you hear about us?",
          "answer":          "Other",
          "additional_text": "Running club newsletter"
        },
        {
          "question_id":     22,
          "question":        "Emergency contact phone",
          "answer":          "5551112222",
          "additional_text": null
        }
      ]
    }
  }
}

Registration fields

FieldTypeNotes
idintegerUnique registration ID
order_idintegerThe order this registration belongs to
first_namestring
last_namestring
emailstring
phonestring|null
sms_marketing_opt_inbooleantrue only when the participant entered a mobile phone number and explicitly opted in to marketing text messages
addressstring|null
citystring|null
statestring|nullFull state name
provincestring|nullNon-US province
zipstring|null
country_codestringISO 3166-1 alpha-2 country code, e.g. US
genderstring|nullM or F
birthdatedate|nullYYYY-MM-DD
bib_numberstring|nullAssigned race bib; null until assigned
categorystring|nullRegistration category/division name
registration_dateISO-8601 stringWhen the registration was created
checked_in_datetimeISO-8601|nullSet when the participant checks in on race day; null otherwise
promoobject|nullPromo code applied to this registration — see Promo object
answersarrayCustom question answers — see Custom answer object

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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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

FieldTypeNotes
waiver_idintegerID of the waiver template
waiver_version_idintegerID of the specific version that was signed; versions are created when the waiver text is edited
waiver_namestringName of the waiver
signed_atISO-8601 stringTimestamp 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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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",
      "promo":              null
    }
  }
}

Product fields

FieldTypeNotes
idintegerUnique product purchase ID
order_idintegerThe order this product purchase belongs to
product_namestring
sizestring|nullSelected size, if the product has sizes
quantityinteger
pricedecimal stringUnit price
shippingdecimal string
taxdecimal string
feedecimal stringProcessing fee
totaldecimal stringPrice, shipping, tax, and fee combined
fulfillment_statusstringUnfulfilled, Fulfilled, or Cancelled
promoobject|nullPromo code applied to this product — see Promo object

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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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

FieldTypeNotes
idintegerUnique donation ID
order_idintegerThe order this donation belongs to
charity_namestring
donor_namestring|null
amountdecimal stringDonation amount before fee
feedecimal string
totaldecimal stringAmount plus fee, if the donor covered the fee
commentstring|nullOptional 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": {
    "event": {
      "id":         42,
      "name":       "Summer Fun Run",
      "year":       "2026",
      "start_date": "2026-07-04T08:00:00-06:00",
      "end_date":   "2026-07-04T08:00:00-06:00"
    },
    "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

FieldTypeNotes
sponsor_idintegerUnique sponsor ID
order_idintegerThe order this sponsorship belongs to
sponsor_namestring
category_namestringSponsorship tier/category name
pricedecimal string
taxdecimal string
marketplace_taxdecimal string
feedecimal stringProcessing fee