Webhook event types
The full list of events Zillo emits today. Adding a new event is a code change, not a migration — check back here when integrating.
| Event | When |
|---|---|
| order.completed | Fires after fulfillOrder() flips the order to `fulfilled`. Payload is the full order object. |
| order.refunded | Fires after a charge.refunded webhook reconciles. Payload includes the updated order with `partially_refunded` / `refunded` status. |
| gift_card.issued | Fires the first time a gift card row is created for an order item. Webhook re-deliveries from Stripe don't double-fire. |
| gift_card.redeemed | Fires every time the balance is decremented. Payload includes the updated gift card and the `redeemed_amount_cents`. |
| ticket.issued | One event per ticket on first issuance. Payload includes `qr_token` and `max_uses` (1 for single-entry tickets; >1 for multi-day or multi-entry passes — e.g. a 3-day festival pass). |
| ticket.redeemed | Fires on EVERY scan, not just the first — multi-entry tickets (`max_uses > 1`) emit one event per scan. Payload includes the updated ticket (with live `uses_after` / `remaining`) plus `was_first: true|false` so subscribers can distinguish opening scans from subsequent re-entries. |
| ticket_event.cancelled | Fires once when a merchant cancels an event occurrence. Every holder's tickets are voided and paid ones refunded — a paired `order.refunded` fires per affected paid order. Payload includes the event `id`, `product_id`, `event_at`, and counts (`orders_refunded`, `tickets_voided`, `total_refunded_cents`). |
| booking.confirmed | Fires after a booking is issued (post-fulfillment). For slot-based bookings, payload's `ticket_event_id` is set; for multi-pass walk-in bookings (`max_uses > 1`) it's `null` — no slot was reserved at purchase. |
| booking.redeemed | Fires on EVERY scan, not just the first — multi-pass walk-in bookings (`max_uses > 1`, e.g. 10-class yoga pack) emit one event per visit. Payload includes the updated booking (with live `uses_after` / `remaining`) plus `was_first: true|false` so subscribers can distinguish first scans from subsequent punches. |
| booking.rescheduled | Fires when a booking's time is changed — by the customer from their confirmation link or by the merchant. Payload is the updated booking with its new `starts_at` / `ends_at`. |
| booking.cancelled | Fires when a booking is cancelled — by the customer (within the cancellation cutoff) or by the merchant. Payload is the updated booking with `voided_at` set; any refund follows the product's refund-on-cancel policy. |
| voucher.issued | Fires the first time a voucher row is created for an order item. Payload includes `max_uses` (1 for single-use; >1 for prepaid packs like a 5-class yoga pass). |
| voucher.redeemed | Fires on EVERY scan, not just the first — multi-use packs (`max_uses > 1`) emit one event per redemption. Payload includes the updated voucher (with live `uses_after` / `remaining`), the new `redemption` event row, and `was_first: true|false` so subscribers can distinguish opening scans from subsequent ones. |
| experience_gift.issued | — |
| experience_gift.redeemed | — |
| experience_gift.voided | — |
| waiver.issued | Fires once per attendee at fulfilment when a booking/ticket product requires a liability waiver. Payload includes the waiver `id`, `order_id`, `attendee_index`, `status: "pending"`, and a `sign_url` for that attendee's signing page. |
| waiver.signed | Fires when an attendee completes and signs their waiver. Payload includes the waiver `id`, `order_id`, `attendee_index`, `status: "signed"`, and `signed_at`. |
| membership.activated | Fires on first activation (status transitioned to `active` or `trialing`). Renewals don't re-fire. |
| membership.cancelled | Fires when the membership transitions to `canceled`. |
| membership.checkin.created | Fires every time a check-in is recorded (append-only, not idempotent). On plans with a per-period cap (`max_checkins_per_period` — e.g. '5 classes a month'), payload includes a `usage` block with the live count, remaining allowance, and refill date. Capped plans reject scans beyond the allowance at the API layer (409 `exhausted`), so this event only fires for successful check-ins. |
| order.shipped | Fires when a merchant marks an order's physical-product lines shipped from the dashboard. Payload is the full order object plus the `tracking_number` and `carrier` entered at ship time (either may be null). Re-marking an already-shipped order is a no-op and doesn't re-fire. |
| merchant.deletion_requested | — |
| merchant.deletion_cancelled | — |
| merchant.stripe_disconnected | — |