Attendee Lifecycle
Attendee Lifecycle
Every attendee in Tickets Please moves through a defined set of states from initial registration to final resolution. Understanding these states helps you manage your events effectively, build custom integrations, and troubleshoot issues when an attendee is stuck in an unexpected status.
The Seven States
Each attendee record has exactly one status at any given time:
| Status | Description |
|---|---|
pending | Awaiting payment. The attendee has started checkout but payment has not been confirmed yet. |
completed | Payment confirmed or RSVP accepted. The attendee is fully registered for the event. |
checked_in | The attendee has been checked in at the event venue. |
refunded | A refund has been processed. This is a terminal state — no further transitions are possible. |
cancelled | The registration was cancelled by an administrator or the buyer. This is a terminal state. |
failed | Payment failed (e.g., card declined, gateway error). The attendee can retry. |
expired | The cart hold or checkout session expired before payment completed. |
Most attendees follow a straightforward path: pending to completed to checked_in. The other states handle edge cases like payment failures, cancellations, and refunds.
Terminal States
Two states are terminal, meaning the attendee cannot transition out of them:
- refunded — the refund has been processed and the attendee’s spot has been released back to the ticket’s available capacity.
- cancelled — the registration has been cancelled. Capacity is released.
Once an attendee reaches either of these states, no further status changes are allowed. If a cancelled attendee wants to re-register, they need a new registration entirely.
Transition Map
Not every state change is valid. Tickets Please enforces a strict transition map to prevent invalid status jumps:
| From | Allowed Transitions |
|---|---|
pending | completed, failed, expired, cancelled |
completed | checked_in, refunded, cancelled |
checked_in | completed (undo check-in) |
failed | pending (retry payment) |
expired | (no transitions — terminal in code) |
refunded | (no transitions — terminal) |
cancelled | (no transitions — terminal) |
Any attempt to perform an invalid transition (e.g., moving directly from pending to checked_in) returns a WP_Error. Your code or integration should check for this error response when changing attendee statuses programmatically.
Visual Flow
pending ──→ completed ──→ checked_in │ │ │ │ │ │ │ │ └──→ refunded (terminal) │ │ └──→ cancelled (terminal) │ └──→ failed ──→ pending (retry) └──→ expired (terminal) └──→ cancelled (terminal)
checked_in ──→ completed (undo check-in)How Transitions Happen
Status changes occur through different pathways depending on the context:
- Payment gateways move attendees from
pendingtocompleted(success) orfailed(decline). WooCommerce order status hooks trigger these transitions automatically. - Cart expiration moves attendees from
pendingtoexpiredwhen the checkout session times out. - Admin actions trigger transitions like check-in, undo check-in, refund, and cancel from the Attendee Management screen.
- RSVP submissions create attendees directly in
completedstatus since no payment is involved. - Retry payment moves a
failedattendee back topendingwhen the buyer re-attempts checkout.
Hooks
Tickets Please fires two WordPress action hooks on every valid status transition. Use these hooks to build custom integrations like CRM syncs, SMS notifications, or analytics tracking.
General Transition Hook
do_action( 'tickets_please_attendee_status_changed', $attendee_id, $old_status, $new_status );Fires on every valid status change. The three parameters give you the attendee post ID, the previous status string, and the new status string.
Status-Specific Hook
do_action( "tickets_please_attendee_status_{$new_status}", $attendee_id, $old_status );Fires when an attendee enters a specific status. For example, tickets_please_attendee_status_checked_in fires whenever any attendee is checked in. This is convenient when you only care about one particular status.
Example: Send a Slack Notification on Check-In
add_action( 'tickets_please_attendee_status_checked_in', function( $attendee_id, $old_status ) { $name = get_post_meta( $attendee_id, '_attendee_name', true ); $event_id = get_post_meta( $attendee_id, '_attendee_event_id', true ); $event_name = get_the_title( $event_id );
wp_remote_post( 'https://hooks.slack.com/your-webhook', array( 'body' => wp_json_encode( array( 'text' => $name . ' just checked in to ' . $event_name, ) ), 'headers' => array( 'Content-Type' => 'application/json' ), ) );}, 10, 2 );Invalid Transitions
When code attempts an invalid transition, Tickets Please does not fire any hooks. Instead, it returns a WP_Error object with the error code invalid_status_transition. Always check the return value when changing statuses programmatically:
$result = tickets_please_update_attendee_status( $attendee_id, 'checked_in' );if ( is_wp_error( $result ) ) { // Handle invalid transition error_log( $result->get_error_message() );}Common Questions
Why is my attendee stuck in “pending”? The most common cause is an incomplete payment. Check the linked WooCommerce order to see if payment was received. If the order is marked as processing or completed but the attendee is still pending, the payment gateway hook may not have fired correctly.
Can I manually change an attendee’s status to anything I want? No. The state machine enforces valid transitions. You cannot skip states or move backward except through the defined paths (e.g., undo check-in). This prevents data inconsistencies like checking in someone who never paid.
What happens to capacity when an attendee is refunded or cancelled? The attendee’s spot is released back to the ticket’s available capacity. If the ticket was sold out, it becomes available for purchase again.
Can I add custom statuses? The state machine uses a fixed set of 7 states. Custom statuses are not supported because the transition logic, capacity management, and email notifications all depend on the known state list.
Why can’t an expired attendee retry their purchase?
In the current implementation, expired is treated as a terminal state. If someone’s cart expired, they need to start a new registration from the event page rather than resuming the expired session.
Do hooks fire on bulk actions?
Yes. When you use bulk check-in or bulk refund from the attendee list, each individual attendee transition fires its own hooks. If you bulk check in 50 attendees, the tickets_please_attendee_status_checked_in hook fires 50 times.
Next Steps
- Attendee Management — the admin interface where you manage attendees and trigger status changes
- Check-In — the check-in workflow and dedicated check-in screen
- Email Notifications — emails that fire automatically on status transitions