Eventonomy

Hooks & Filters

Every write operation fires a before-filter (abortable) and an after-action (full data). Every list endpoint has a query-args filter. Every REST response has a prepare-filter.

Full reference: docs/EXTENDING.md §2 in the plugin root. This page covers the most frequently used hooks with examples.

What You Will Learn

  • The evnm_before_* / evnm_after_* naming convention
  • Event lifecycle hooks
  • RSVP, ticket, and order hooks
  • Query-args and REST-prepare filters
  • App config, email, capability, and feature-flag hooks

Naming Conventions

Pattern Type Use
evnm_before_{verb}_{resource} Filter Receives validated payload; return WP_Error to abort.
evnm_after_{verb}_{resource} Action Fires post-commit; receives full result. Never read $_POST.
evnm_{resource}_query_args Filter Modify read args before the query runs.
evnm_rest_prepare_{resource} Filter Add/redact/compute fields on a REST response item.

Event Lifecycle

// Abort if the event is in the past.
add_filter( 'evnm_before_create_event', function ( array $event, array $context ) {
    if ( strtotime( $event['start_utc'] ) < time() ) {
        return new WP_Error( 'evnm_event_in_past', 'Events cannot start in the past.', [ 'status' => 422 ] );
    }
    return $event;
}, 10, 2 );

// Push to CRM after creation.
add_action( 'evnm_after_create_event', function ( array $event, array $context ) {
    my_crm_push( $event['id'], $event['title'], $event['start_utc'] );
}, 10, 2 );

// Abort if capacity would drop below current RSVPs.
add_filter( 'evnm_before_update_event', function ( array $changes, array $existing, array $context ) {
    if ( isset( $changes['capacity'] ) && $changes['capacity'] < $existing['rsvp_count'] ) {
        return new WP_Error( 'evnm_capacity_below_rsvps', 'Capacity cannot drop below current RSVPs.', [ 'status' => 409 ] );
    }
    return $changes;
}, 10, 3 );

// Detect rescheduling.
add_action( 'evnm_after_update_event', function ( array $event, array $changed_keys, array $context ) {
    if ( in_array( 'start_utc', $changed_keys, true ) ) {
        do_action( 'my_plugin_event_rescheduled', $event['id'] );
    }
}, 10, 3 );

// Archive on delete.
add_action( 'evnm_after_delete_event', function ( int $event_id, array $snapshot, array $context ) {
    my_archive( $snapshot ); // full pre-delete snapshot
}, 10, 3 );

RSVP, Ticket, Order, Occurrence Lifecycle

Same before_ / after_ pattern for every resource:

// Watch for paid order completion.
add_action( 'evnm_after_update_order', function ( $order, $changed_keys, $context ) {
    if ( in_array( 'status', $changed_keys, true ) && 'paid' === $order['status'] ) {
        foreach ( $order['line_items'] as $item ) {
            my_grant_perk( $order['user_id'], $item['ticket_id'] );
        }
    }
}, 10, 3 );

Available hooks: evnm_before_create_rsvp / evnm_after_create_rsvp, …_update_rsvp, …_delete_rsvp; same pattern for ticket, order, occurrence.

Magic-link actions: evnm_magic_link_requested, evnm_magic_link_verified, evnm_rsvp_promoted.

Query-Args Filters

Use these to modify SQL queries without touching the repository:

// Restrict public REST reads to published events only.
add_filter( 'evnm_events_query_args', function ( $args, $context ) {
    if ( 'rest' === $context['source'] && ! is_user_logged_in() ) {
        $args['status'] = 'published';
    }
    return $args;
}, 10, 2 );

Available: evnm_events_query_args, evnm_occurrences_query_args, evnm_rsvps_query_args, evnm_tickets_query_args, evnm_orders_query_args, evnm_attendees_query_args, evnm_calendar_ics_query_args.

REST Response Filters

Add, redact, or compute fields on every API response for that resource:

add_filter( 'evnm_rest_prepare_event', function ( $data, $event, $request ) {
    $data['weather_hint'] = my_forecast( $event['start_utc'], $event['city'] );
    return $data;
}, 10, 3 );

Available: evnm_rest_prepare_event, evnm_rest_prepare_occurrence, evnm_rest_prepare_rsvp, evnm_rest_prepare_ticket, evnm_rest_prepare_order.

App Config

// Expose a feature flag to the frontend store.
add_filter( 'evnm_rest_app_config', function ( array $cfg ): array {
    $cfg['features']['my_feature'] = my_feature_enabled();
    return $cfg;
} );

Email

add_filter( 'evnm_email_subject', function( $subject, $email_key, $data ) {
    if ( 'rsvp_confirmation' === $email_key ) {
        return 'You are going to: ' . $data['event']['title'];
    }
    return $subject;
}, 10, 3 );

Valid $email_key values: rsvp_confirmation, magic_link, order_receipt, event_reminder, waitlist_promoted, event_cancelled, approval_result.

Also: evnm_email_body, evnm_email_recipients - same ($value, $email_key, $data) signature.

Capabilities

// Grant co-organizers edit access.
add_filter( 'evnm_user_can_manage_event', function ( $can, $user_id, $event, $cap ) {
    $co = (array) evnm_get_meta( 'event', $event['id'], 'co_organizers' );
    return in_array( $user_id, array_map( 'intval', $co ), true ) ? true : $can;
}, 10, 4 );

Add-On Accessors (stable across major versions)

$currency = evnm_setting( 'currency', 'USD' );
$all      = evnm_settings();
$has_wait = evnm_feature_enabled( 'waitlist', [ 'event_id' => 42 ] );

Never import \Eventonomy\Core\Settings or \Eventonomy\Core\Features directly - these accessors are the stable contract.

Formatting Filters

  • evnm_event_jsonld - ($data, $event, $view) - modify or suppress Schema.org JSON-LD on single-event pages.
  • evnm_permalink_base - ($base) - translate or override the single-event URL slug.
  • evnm_format_money - ($formatted, $amount, $args) - override currency formatting.
  • evnm_format_event_datetime - ($formatted, $utc, $event_tz, $args) - override date/time display.

Cron / Notification Actions

evnm_cron_send_reminders (hourly), evnm_cron_recompute_occurrences (daily, $event_id|null), evnm_cron_expire_holds (5-min), evnm_cron_cleanup (daily), evnm_notification_dispatch ($notification, $channel).

What's Next?

Learn how to work with blocks, templates, and the Interactivity store.

Blocks & Templates →