Skip to content

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:

StatusDescription
pendingAwaiting payment. The attendee has started checkout but payment has not been confirmed yet.
completedPayment confirmed or RSVP accepted. The attendee is fully registered for the event.
checked_inThe attendee has been checked in at the event venue.
refundedA refund has been processed. This is a terminal state — no further transitions are possible.
cancelledThe registration was cancelled by an administrator or the buyer. This is a terminal state.
failedPayment failed (e.g., card declined, gateway error). The attendee can retry.
expiredThe 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:

FromAllowed Transitions
pendingcompleted, failed, expired, cancelled
completedchecked_in, refunded, cancelled
checked_incompleted (undo check-in)
failedpending (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 pending to completed (success) or failed (decline). WooCommerce order status hooks trigger these transitions automatically.
  • Cart expiration moves attendees from pending to expired when 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 completed status since no payment is involved.
  • Retry payment moves a failed attendee back to pending when 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