Notification and email hooks

HappenBoard sends its transactional email through a small set of seams: one filter picks the recipient’s language, one filter per template rewrites or cancels the message just before it goes out, and one filter sets the reminder schedule for appointment bookings.

The happenboard_mp_* actions are outbound only. HappenBoard fires them into MailerPress, where the operator builds the actual email in the automation interface. They exist only in HappenBoard Pro, and they only fire when MailerPress is active and the integration is switched on.

happenboard_booking_reminder_offsets

Filter Free Since 1.0 src/Service/Booking/Notifications/BookingNotifier.php

Sets how long before an appointment the reminder emails go out. The default is two reminders, 24 hours and 1 hour before the start.

Signature

apply_filters( 'happenboard_booking_reminder_offsets', array<int,int> $offsets, int $booking_id, array<string,mixed> $row )

Parameters

$offsets array<int,int>
Hours before the start. Defaults to array( 24, 1 ).
$booking_id int
Primary key of the booking.
$row array<string,mixed>
The booking row. Empty array on the unschedule path, see the notes.

Return value

array<int,int> A list of whole hours before the booking start. An empty array disables booking reminders.

Example

add_filter(
    'happenboard_booking_reminder_offsets',
    function ( array $offsets, int $booking_id, array $row ): array {
        // Two days before, the day before, and two hours before.
        return array( 48, 24, 2 );
    },
    10,
    3
);

Notes

  • Three arguments are passed, so register with 10, 3.
  • Values are whole hours. Zero, negative and duplicate values are dropped, and any reminder that would fire less than a minute from now is skipped, so a booking made an hour before it starts gets no reminder.
  • Return the same list every time. Without Action Scheduler installed, the filter is called a second time when a booking is cancelled, in order to clear the pending reminders, and it is called with an empty $row. A callback that derives the list from $row returns something different there and leaves orphan reminders scheduled.
  • Return an empty array to turn booking reminders off completely.
  • This only covers appointment bookings. Event reminders are scheduled from the reminder_hours_before general setting, which has no filter.

happenboard_email_locale

Filter Free Since 1.0 src/Service/Notifications/AttendeeNotifier.php

Chooses the language a notification is rendered in. HappenBoard wraps the subject line and the template body in switch_to_locale() using whatever this filter returns, so one recipient can get French while the site runs in English.

Signature

apply_filters( 'happenboard_email_locale', string $locale, string $template_id, WP_Post $event, array<string,mixed> $attendee )
apply_filters( 'happenboard_email_locale', string $locale, int $booking_id )

Parameters

$locale string
The site locale, from get_locale(). Passed by both call shapes.
$template_id string
For attendee emails: confirmation or reminder. The booking calls put the booking id, an integer, in this position instead.
$event WP_Post
The source event. Attendee emails only, the booking calls do not pass it.
$attendee array<string,mixed>
The full attendee row, including the custom_fields blob. Attendee emails only.

Return value

string A WordPress locale such as fr_FR. Return $locale unchanged to keep the site language.

Example

add_filter(
    'happenboard_email_locale',
    function ( string $locale, $context = '', $event = null, $attendee = array() ): string {
        // The booking notifier passes only two arguments, so every
        // parameter after $locale needs a default value.
        if ( ! is_array( $attendee ) || empty( $attendee['email'] ) ) {
            return $locale;
        }

        return str_ends_with( (string) $attendee['email'], '.fr' ) ? 'fr_FR' : $locale;
    },
    10,
    4
);

Notes

  • The two call sites disagree. src/Service/Notifications/AttendeeNotifier.php passes four arguments, while src/Service/Booking/Notifications/BookingNotifier.php passes only two, and its second argument is the booking id (an integer), not a template id. Give every parameter after $locale a default value in your callback, otherwise the booking calls reach it with missing arguments and PHP raises an ArgumentCountError.
  • Register with 10, 4. With the default of one argument you never see $attendee and cannot tell one recipient from another.
  • The two paths also treat an empty return differently. The attendee notifier checks for an empty string and skips the switch, so returning '' is safe there. The booking notifier feeds the return value straight into switch_to_locale() with no check, so always return a real locale.
  • HappenBoard Pro registers Service\I18n\LocaleResolver on this filter at priority 10 with four accepted arguments. It reads the attendee’s language custom field (key language by default) and returns the incoming value when it finds nothing. Hook later than 10 if you want the last word.
  • Switching only changes strings that have translation files installed on the site. A locale with no language pack produces an email in the original English.

happenboard_mp_checked_in

Action Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

The MailerPress workflow trigger fired when an attendee is checked in at the door. It carries a ready-made context of contact identity and event merge fields.

Signature

do_action( 'happenboard_mp_checked_in', array<string,mixed> $ctx )

Parameters

$ctx array<string,mixed>
Resolved context: user_id, email, first_name, last_name, event_id, event_title, event_url, event_date, venue, quantity.

Example

add_action(
    'happenboard_mp_checked_in',
    function ( array $ctx ): void {
        error_log(
            sprintf(
                '%s checked in at %s.',
                (string) ( $ctx['email'] ?? '' ),
                (string) ( $ctx['event_title'] ?? '' )
            )
        );
    }
);

Notes

  • One argument only, so a plain add_action() is enough.
  • This is an outbound trigger. HappenBoard fires it into MailerPress and sends nothing itself. The operator builds the email, the tags and the delays in the MailerPress automation interface, where this trigger appears as “Event, Attendee checked in”.
  • It is registered only when MailerPress is installed (the bridge tests for add_mailerpress_contact()) and the integration is switched on in the HappenBoard Pro settings. Otherwise the action never fires at all.
  • It rides happenboard_pro_attendee_checked_in at priority 20, so it fires on re-scans too. The check-in row id is dropped by the bridge and is not in $ctx; hook the underlying action if you need it.
  • The bridge returns early when the attendee row cannot be read or has no email, so this action never fires with an empty context.

happenboard_mp_post_event

Action Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

The MailerPress workflow trigger fired the day after an event ends, once per attendee who was going. Use it for a thank-you, a survey or a follow-up offer.

Signature

do_action( 'happenboard_mp_post_event', array<string,mixed> $ctx )

Parameters

$ctx array<string,mixed>
Resolved context: user_id, email, first_name, last_name, event_id, event_title, event_url, event_date, venue, quantity.

Example

add_action(
    'happenboard_mp_post_event',
    function ( array $ctx ): void {
        my_plugin_queue_survey(
            (string) ( $ctx['email'] ?? '' ),
            (int) ( $ctx['event_id'] ?? 0 )
        );
    }
);

Notes

  • One argument only. It is the bridge translation of happenboard_mp_post_event_tick, which a daily cron fires per attendee.
  • Recurring series are skipped. The daily scan excludes any event that has a non-empty _happenboard_rrule, because a series has no single end date. Attendees of a recurring event never get this trigger.
  • Only attendees whose status is going are included. Cancelled and waitlisted rows are filtered out before the tick fires.
  • It fires at most once per event: the scan writes a _happenboard_mp_post_event_fired flag on the event before looping over attendees, so a failure part way through loses the remaining attendees rather than replaying the whole event tomorrow.
  • Timing depends on WP-Cron. The scan looks at events whose end falls between 26 hours and 1 hour ago and processes at most 100 events per run, so a site with no cron traffic can miss the window entirely.

happenboard_mp_post_event_tick

Action Pro Since 1.0 src/Service/Integrations/MailerPress/PostEventCron.php

The raw per-attendee tick emitted by the daily post-event scan. The MailerPress bridge listens to it and turns it into happenboard_mp_post_event. Fire it yourself to replay a follow-up for one attendee.

Signature

do_action( 'happenboard_mp_post_event_tick', int $attendee_id, int $event_id )

Parameters

$attendee_id int
Primary key of the attendee row.
$event_id int
The event that just ended.

Example

add_action(
    'happenboard_mp_post_event_tick',
    function ( int $attendee_id, int $event_id ): void {
        error_log(
            sprintf(
                'Post-event follow-up due for attendee %d of event %d.',
                $attendee_id,
                $event_id
            )
        );
    },
    10,
    2
);

Notes

  • Two arguments are passed, so register with 10, 2. The MailerPress bridge itself listens with 10, 2.
  • It comes from the daily WP-Cron job happenboard_pro_mp_post_event_scan, which self-gates on the MailerPress integration being enabled. The scan does nothing when the setting is off, so nothing fires.
  • The scan window is events whose _happenboard_end falls between 26 hours and 1 hour ago, capped at 100 events per run and 1000 attendees per event. Recurring series are excluded and only attendees with status going produce a tick.
  • Unlike the other happenboard_mp_* hooks, this one carries plain ids, not the resolved context array. The bridge resolves the context afterwards.
  • Calling do_action( 'happenboard_mp_post_event_tick', $attendee_id, $event_id ) yourself is the supported way to re-send a follow-up, since the per-event flag stops the scan from doing it twice.

happenboard_mp_reminder

Action Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

The MailerPress workflow trigger fired at the reminder time before an event, 24 hours before the start by default. Use it for the “see you tomorrow” message anchored to the event date.

Signature

do_action( 'happenboard_mp_reminder', array<string,mixed> $ctx )

Parameters

$ctx array<string,mixed>
Resolved context: user_id, email, first_name, last_name, event_id, event_title, event_url, event_date, venue, quantity.

Example

add_action(
    'happenboard_mp_reminder',
    function ( array $ctx ): void {
        error_log(
            sprintf(
                'Reminder due for %s, event on %s.',
                (string) ( $ctx['email'] ?? '' ),
                (string) ( $ctx['event_date'] ?? '' )
            )
        );
    }
);

Notes

  • One argument only. It rides happenboard_cron_reminder at priority 20, the one-off cron event HappenBoard schedules when a sign-up is recorded.
  • If “send reminder email” is off in the HappenBoard settings, no reminder is ever scheduled and this trigger never fires, whatever MailerPress is configured to do.
  • The offset comes from the reminder_hours_before general setting, 24 by default, and is applied at sign-up time. Changing the setting later does not move reminders that are already scheduled.
  • The bridge does not re-check the attendee status, unlike HappenBoard’s own notifier at priority 10. An attendee who cancelled after signing up still produces this trigger. Test $ctx against the current row if that matters.
  • A sign-up recorded less than the offset before the start schedules nothing at all, because the reminder time is already in the past.

happenboard_mp_rsvp_cancelled

Action Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

The MailerPress workflow trigger fired when a registration is cancelled. Use it for a win-back sequence or to ask what went wrong.

Signature

do_action( 'happenboard_mp_rsvp_cancelled', array<string,mixed> $ctx )

Parameters

$ctx array<string,mixed>
Resolved context: user_id, email, first_name, last_name, event_id, event_title, event_url, event_date, venue, quantity.

Example

add_action(
    'happenboard_mp_rsvp_cancelled',
    function ( array $ctx ): void {
        my_plugin_tag_contact(
            (string) ( $ctx['email'] ?? '' ),
            'cancelled-' . (int) ( $ctx['event_id'] ?? 0 )
        );
    }
);

Notes

  • One argument only. It rides happenboard_rsvp_cancelled at priority 20.
  • It therefore covers far more than a visitor clicking the cancel link: a Stripe refund and a reversed WooCommerce order also fire the underlying action, once per attendee row. $ctx gives you no way to tell those apart, so listen to happenboard_rsvp_cancelled directly if you need the distinction.
  • No contact is created or updated on this path. The bridge only upserts the MailerPress contact on the sign-up trigger, so a cancellation for someone who was never synced resolves a context but has no contact behind it.
  • The context is read from the attendee row after the cancellation, so $ctx describes a row whose status is already cancelled.
  • An administrator setting the status to cancelled in the attendees list does not reach this trigger.

happenboard_mp_rsvp_created

Action Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

The MailerPress workflow trigger fired when someone registers for an event. Use it to welcome the attendee, deliver practical details or start a pre-event sequence.

Signature

do_action( 'happenboard_mp_rsvp_created', array<string,mixed> $ctx )

Parameters

$ctx array<string,mixed>
Resolved context: user_id, email, first_name, last_name, event_id, event_title, event_url, event_date, venue, quantity.

Example

add_action(
    'happenboard_mp_rsvp_created',
    function ( array $ctx ): void {
        error_log(
            sprintf(
                '%s registered for %s (%d seats).',
                (string) ( $ctx['email'] ?? '' ),
                (string) ( $ctx['event_title'] ?? '' ),
                (int) ( $ctx['quantity'] ?? 1 )
            )
        );
    }
);

Notes

  • One argument only. It rides happenboard_rsvp_submitted at priority 20, so it runs after HappenBoard’s own confirmation email at priority 10.
  • The MailerPress contact is upserted before the action fires, when attendee sync is enabled. That ordering is deliberate: the contact exists by the time MailerPress resolves the trigger.
  • Paid orders fire the underlying action once per ticket line, so one Stripe checkout or WooCommerce order can produce several triggers for the same buyer.
  • first_name and last_name come from splitting the display name on its first space. A single-word name leaves last_name empty.
  • event_date is a formatted, human-readable string built with the site date and time formats, not a machine-readable date. Use event_id when you need the real value.

happenboard_pro_mailerpress_contact

Filter Pro Since 1.0 src/Service/Integrations/MailerPress/Bridge.php

Filters the MailerPress contact record just before HappenBoard creates or updates it, so you can attach extra custom fields, extra lists or your own opt-in source.

Signature

apply_filters( 'happenboard_pro_mailerpress_contact', array<string,mixed> $payload, array<string,mixed> $ctx )

Parameters

$payload array<string,mixed>
The arguments for add_mailerpress_contact(): email, firstName, lastName, subscription_status, lists, custom_fields and opt_in_source.
$ctx array<string,mixed>
The same resolved context the happenboard_mp_* triggers receive.

Return value

array<string,mixed> The arguments handed to add_mailerpress_contact(). Return the array you were given, extended.

Example

add_filter(
    'happenboard_pro_mailerpress_contact',
    function ( array $payload, array $ctx ): array {
        $payload['custom_fields']['hp_seats'] = (string) ( $ctx['quantity'] ?? 1 );

        return $payload;
    },
    10,
    2
);

Notes

  • Two arguments are passed, so register with 10, 2.
  • It only runs on the sign-up path, and only when attendee sync is enabled in the HappenBoard Pro MailerPress settings. Cancellations, check-ins, reminders and post-event ticks never reach it.
  • The result is cast with (array) and passed straight to add_mailerpress_contact(). Keep the existing keys, email above all: an empty address would create a useless contact.
  • Add to custom_fields, do not replace it. The hp_event_title, hp_event_date, hp_event_url and hp_event_venue entries are the merge tags the HappenBoard triggers expose in the automation interface.
  • subscription_status follows the operator’s opt-in setting and is either subscribed or pending. Forcing subscribed overrides a deliberate double opt-in choice. lists holds MailerPress list ids and is an empty array when no sync list is configured.

happenboard_notifications_{$template_id}

Filter Free Since 1.0 src/Service/Notifications/AttendeeNotifier.php

Rewrites an attendee email just before it is handed to wp_mail(), or cancels it. The hook name is built at runtime by appending the template id, so you hook the finished name, for example happenboard_notifications_confirmation.

Signature

apply_filters( "happenboard_notifications_{$template_id}", array<string,mixed> $payload, WP_Post $event, array<string,mixed> $attendee )

Parameters

$payload array<string,mixed>
to, subject, body (HTML), text (plain-text alternative), headers (array of raw header lines) and attachments (absolute file paths).
$event WP_Post
The event the email is about.
$attendee array<string,mixed>
The full attendee row, including attendee_id, created_utc, quantity and custom_fields.

Return value

array<string,mixed> The email payload to send. Add 'send' => false to cancel the send entirely.

Example

add_filter(
    'happenboard_notifications_confirmation',
    function ( array $payload, $event = null, $attendee = array() ): array {
        $payload['subject'] = '[' . get_bloginfo( 'name' ) . '] ' . $payload['subject'];
        $payload['body']   .= '<p>' . esc_html__( 'See you there.', 'my-plugin' ) . '</p>';

        // Return array( 'send' => false ) + $payload to cancel the send.
        return $payload;
    },
    40,
    3
);

Notes

  • The name is assembled by concatenation, so only the templates the code actually dispatches produce a hook. The attendee notifier dispatches two: happenboard_notifications_confirmation and happenboard_notifications_reminder. The docblock in the source also lists happenboard_notifications_cancellation, but nothing dispatches a cancellation template, so that name never fires.
  • Three arguments are passed, so register with 10, 3 at least. Give $event and $attendee default values so the callback stays safe if it is ever reused elsewhere.
  • Returning a payload that contains 'send' => false cancels the send. This is the way to hand a message over to your own transport without HappenBoard sending a duplicate.
  • HappenBoard Pro already filters happenboard_notifications_confirmation: the check-in QR block at priority 20 and the PDF ticket attachment at priority 30. Append to body and attachments instead of replacing them, and hook after 30 if you want to see the final content.
  • Any Content-Type: header you add is stripped straight after the filter and replaced with text/html. Appointment bookings use a separate family, happenboard_notifications_booking_{$template_id} in src/Service/Booking/Notifications/BookingNotifier.php, with a different signature: array $payload, int $booking_id, array $row, and three dispatched ids (confirmation, cancellation, reminder).