These hooks change the emails HappenBoard sends: to attendees who sign up for an event, to customers who book an appointment, and to the host and the site admin about each booking. Use them to rewrite or cancel a message, choose its language, change when reminders go out or decide who hears about a booking.
For the settings side, see automatic emails and emails in the attendee’s language. New to hooks? Start with Extending HappenBoard with hooks.
Which emails HappenBoard sends
Each email has a template id. The id ends the name of the filter that can rewrite the message, and it is passed to happenboard_email_locale.
Sign-up emails go through happenboard_notifications_{$template_id}:
| Template id | Sent when |
|---|---|
confirmation | Someone signs up, or gets a place from the waitlist. Needs Send confirmation email on RSVP / ticket purchase. |
reminder | Their date comes within the reminder lead time. Needs Send reminder email before each event. |
cancellation | Their registration is cancelled: by them, by a refund or by an administrator. |
date_changed | Their date moves: the event was edited, or an administrator moved them to another date. |
date_cancelled | Their date is cancelled, or no longer part of the series. |
Booking emails go through happenboard_notifications_booking_{$template_id}:
| Template id | Sent to | When |
|---|---|---|
request | Customer | A booking that needs approval is received. |
confirmation | Customer | The booking is confirmed: at once, on approval or once paid. |
reminder | Customer | Before the appointment, 24 hours and 1 hour by default. |
cancellation | Customer | The booking is cancelled. |
declined | Customer | A request is declined. |
payment_failed | Customer | The card held for a request could not be charged. |
rescheduled, request_rescheduled | Customer | A booking, or a request, moves to another time. |
operator_request, operator_confirmed, operator_cancelled, operator_rescheduled | Host and site admin | A request is waiting, or a booking is confirmed, cancelled or moved. Needs Email the host and the site admin about bookings. |
These settings are under HappenBoard → Settings → Email. See approving booking requests for the request flow.
How reminders go out
Sign-up reminders come from one WP-Cron event, happenboard_rsvp_reminder_sweep, which runs every 15 minutes. Each run finds the dates that now start within the lead time (Send reminder hours before start, 24 by default). It then fires happenboard_cron_reminder once for each attendee still going.
HappenBoard’s reminder email listens there, and so do HappenBoard Pro’s WhatsApp and MailerPress reminders. Before version 0.2.0, each sign-up scheduled its own cron event; the first sweep after the update takes those over.
Booking reminders work differently. Each confirmed booking schedules its own reminders, with Action Scheduler when it is installed and WP-Cron otherwise, at the hours set by happenboard_booking_reminder_offsets.
Which language an email uses
- Sign-up emails: the language of the event translation the attendee signed up on (WPML or Polylang), else the site language.
- Booking emails to the customer: the language of the page they booked on, else their profile language when they have an account, else the site language.
- Booking emails to the host and the site admin: each person’s profile language.
happenboard_email_localedoes not run for these.
happenboard_email_locale can change the first two. HappenBoard Pro uses it to read a language field from the registration form.
MailerPress triggers (Pro)
The happenboard_mp_* actions exist only in HappenBoard Pro, and only fire when MailerPress is active and the MailerPress integration is turned on. HappenBoard sends nothing on them: they are the triggers you build automations on in MailerPress. See MailerPress automations.
happenboard_email_locale
Filters the language an email is written in. HappenBoard switches to the returned locale while it builds the subject and the body, so one recipient can read French on an English site.
Signature
apply_filters( 'happenboard_email_locale', string $locale, string $template_id, WP_Post|null $event, array<string,mixed> $row )
Parameters
-
$localestring - The default. Sign-up emails: the locale of the event translation the attendee signed up on (WPML or Polylang), else the site locale. Booking emails: the locale of the page the booking was made on, else the customer’s profile locale when they have an account, else the site locale.
-
$template_idstring - The email being sent. Sign-up emails:
confirmation,reminder,cancellation,date_changed,date_cancelled. Booking emails:request,confirmation,reminder,cancellation,declined,payment_failed,rescheduled,request_rescheduled. -
$eventWP_Post|null - The event, for sign-up emails. Always
nullfor booking emails. -
$rowarray<string,mixed> - The attendee row for sign-up emails, the booking row without its
cancel_tokenfor booking emails. Both carrycustom_fields, where a language field is stored.
Return value
string
A WordPress locale such as fr_FR. Return $locale unchanged to keep the default.
Example
add_filter(
'happenboard_email_locale',
function ( $locale, $template_id, $event, $row ) {
// Attendee rows have "email", booking rows "customer_email".
$email = (string) ( $row['email'] ?? $row['customer_email'] ?? '' );
return str_ends_with( $email, '.fr' ) ? 'fr_FR' : $locale;
},
20,
4
);
Notes
- Four arguments: register with
10, 4or a later priority, or you never see the row. Fired fromsrc/Service/Notifications/AttendeeNotifier.phpandsrc/Service/Booking/Notifications/BookingNotifier.php. - Do not type-hint
$eventasWP_Post: it isnullfor booking emails. - The emails sent to the host and the site admin about a booking do not go through this filter. Each one is written in that person’s profile language.
- HappenBoard Pro hooks it at priority 10 and returns the language stored in the attendee’s or customer’s language field (key
languageby default) when there is one. Switching only changes strings that have a translation installed on the site.
happenboard_notifications_{$template_id}
Filters a sign-up email just before it is handed to wp_mail(), or cancels it. The name ends with the template id, so you hook the full 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
-
$payloadarray<string,mixed> to,subject,body(HTML),text(plain-text version),headers(array of header lines) andattachments(file paths: the calendar file of the date onconfirmationanddate_changed).-
$eventWP_Post - The event the email is about.
-
$attendeearray<string,mixed> - The attendee row, including
attendee_id,status,occurrence_seq(their date in a series),quantityandcustom_fields.
Return value
array<string,mixed>
The email to send. Add 'send' => false to cancel it.
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 email.
return $payload;
},
40,
3
);
Notes
- Five names fire:
happenboard_notifications_confirmation(a sign-up, or a place from the waitlist),happenboard_notifications_reminder,happenboard_notifications_cancellation,happenboard_notifications_date_changedandhappenboard_notifications_date_cancelled. The table at the top of this page says when each is sent. - Three arguments: register with
10, 3. A payload with'send' => falsecancels the send, for example when your own email service delivers the message. - HappenBoard Pro adds the check-in QR code at priority 20 and the PDF ticket at priority 30, on
confirmationanddate_changed. Append tobodyandattachmentsrather than replacing them, and hook after 30 to see the final content. - Keep each
headersentry a string: one that is not can end in a PHP fatal error. Removingheadersis safe. AContent-Typeheader is replaced withtext/html. The calendar file is temporary and deleted right after sending.
happenboard_cron_reminder
Fires when an attendee’s reminder is due: once per attendee and date, when the date comes within the reminder lead time. HappenBoard’s reminder email goes out on it at priority 10, and HappenBoard Pro’s WhatsApp and MailerPress reminders follow.
Signature
do_action( 'happenboard_cron_reminder', int $attendee_id )
Parameters
-
$attendee_idint - The attendee to remind.
Example
add_action(
'happenboard_cron_reminder',
function ( int $attendee_id ): void {
// A channel of your own, next to the email.
my_send_sms_reminder( $attendee_id );
},
50
);
Notes
- One argument. It comes from the reminder sweep, a WP-Cron event (
happenboard_rsvp_reminder_sweep) that runs every 15 minutes. A reminder can therefore go out up to 15 minutes after the exact lead time, and later on a site whose WP-Cron only runs on visits. - It fires only while “Send reminder email before each event” is on, and only for an attendee still going to a published event that is not cancelled or postponed. It never fires twice for the same attendee and date, and someone who signs up after the reminder time gets no reminder.
- Before HappenBoard 0.2.0, each sign-up scheduled its own one-off cron event on this hook. The first sweep after the update takes those over and removes them. Calling
do_action()on it yourself sends the email only if the reminder is due now and has not gone out. - To stop the email but keep Pro’s WhatsApp and MailerPress reminders, cancel it on
happenboard_notifications_reminder. Removing HappenBoard’s own callback stops those too: they only go out in the run that sends the email.
happenboard_booking_email_tokens
Filters the values a booking email is filled with, subject line included. Use it to add a detail to every email about a booking, such as a room, a meeting link or a dress code.
Signature
apply_filters( 'happenboard_booking_email_tokens', array<string,string> $tokens, int $booking_id, array<string,mixed> $row )
Parameters
-
$tokensarray<string,string> - Customer emails:
display_name,event_title(the booking type) andstart_local(date and time in the customer’s time zone, with the host’s name). Thenevent_url,cancel_url(the customer’s private link to cancel or move the booking),price,payment_line(what was paid and what is due) andpersons(“2 × Adult, 1 × Child”). Emails to the host and the site admin replacecancel_urlwithcustomer_email,host_nameandprevious_local. Their time is in the site’s time zone, andevent_urlopens the Bookings screen. -
$booking_idint - The booking ID.
-
$rowarray<string,mixed> - The booking row, without its
cancel_token.
Return value
array<string,string>
The values. The templates only print the keys listed above, so change values rather than adding keys.
Example
add_filter(
'happenboard_booking_email_tokens',
function ( $tokens, $booking_id, $row ) {
// The meeting link saved on the booking type.
$link = (string) get_post_meta( (int) $row['booking_type_id'], 'acme_meeting_link', true );
if ( '' !== $link && '' !== $tokens['start_local'] ) {
$tokens['start_local'] .= ' · ' . $link;
}
return $tokens;
},
10,
3
);
Notes
- Three arguments: register with
10, 3. It runs for every booking email, to the customer and to the team, in the recipient’s language, so strings you translate come out in it. - To change only the team’s emails, test
isset( $tokens['customer_email'] ): customer emails do not have that key. - Pass
cancel_urlthrough untouched: it is the customer’s private link. Values are escaped when the email is built, so return plain text. - HappenBoard Pro adds the shared resource a booking holds (a room, a table) to
start_localat priority 10.
happenboard_notifications_booking_{$template_id}
Filters a booking email just before it is handed to wp_mail(), or cancels it. The name ends with the template id, for example happenboard_notifications_booking_confirmation.
Signature
apply_filters( "happenboard_notifications_booking_{$template_id}", array<string,mixed> $payload, int $booking_id, array<string,mixed> $row )
Parameters
-
$payloadarray<string,mixed> to,subject,body(HTML),text(plain-text version),headersandattachments(file paths: the calendar invite onconfirmationandrescheduled, the invite that removes the booking from the calendar oncancellation).-
$booking_idint - The booking ID.
-
$rowarray<string,mixed> - The booking row, without its
cancel_token.
Return value
array<string,mixed>
The email to send. Add 'send' => false to cancel it.
Example
add_filter(
'happenboard_notifications_booking_confirmation',
function ( array $payload, int $booking_id, array $row ): array {
// Copy the front desk on every booking confirmation.
$payload['headers'][] = 'Bcc: [email protected]';
return $payload;
},
10,
3
);
Notes
- Twelve template ids. To the customer:
request,confirmation,reminder,cancellation,declined,payment_failed,rescheduledandrequest_rescheduled. To the host and the site admin:operator_request,operator_confirmed,operator_cancelledandoperator_rescheduled. - Three arguments: register with
10, 3. A payload with'send' => falsecancels the send. - A team email fires once per recipient, with
toset to that person and aReply-Toheader set to the customer. - A
Content-Typeheader is replaced withtext/html, and headers or attachments that are not strings are dropped. Invites are temporary files, deleted after sending. Mail to an address erased under a privacy request is dropped before this filter.
happenboard_booking_operator_recipients
Filters who is emailed on the team side of a booking: by default the booking’s host and the site admin address. Use it to add a front desk, leave out the admin or keep only some events.
Signature
apply_filters( 'happenboard_booking_operator_recipients', array<int,string> $recipients, string $event, int $booking_id, array<string,mixed> $row )
Parameters
-
$recipientsarray<int,string> - Email addresses: the host’s account email and the
admin_emailoption, less the person making the change. Empty when “Email the host and the site admin about bookings” is off. -
$eventstring request(waiting for approval),confirmed,cancelledorrescheduled.-
$booking_idint - The booking ID.
-
$rowarray<string,mixed> - The booking row, without its
cancel_token.
Return value
array<int,string>
The addresses to email. Invalid and duplicate addresses, and the customer’s own address, are removed afterwards. Return an empty array to send nothing.
Example
add_filter(
'happenboard_booking_operator_recipients',
function ( array $recipients, string $event, int $booking_id, array $row ): array {
// The front desk hears about every request waiting for approval.
if ( 'request' === $event ) {
$recipients[] = '[email protected]';
}
return $recipients;
},
10,
4
);
Notes
- Four arguments: register with
10, 4. The setting is under HappenBoard → Settings → Email and is on by default. - The filter runs even when the setting is off, with an empty list, so a callback can still add addresses.
- Nobody is emailed about a change they made: the logged-in user’s address is removed before the filter, but an address you add back is emailed. A declined request sends no team email.
- Each recipient gets their own email, in their profile language when the address belongs to a user account, through
happenboard_notifications_booking_operator_{$event}.
happenboard_booking_reminder_offsets
Sets how many hours 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
-
$offsetsarray<int,int> - Hours before the start. Defaults to
array( 24, 1 ). -
$booking_idint - The booking ID.
-
$rowarray<string,mixed> - The booking row, without its
cancel_token. An empty array when reminders are being cleared, see the notes.
Return value
array<int,int>
Whole hours before the booking start. An empty array turns booking reminders off.
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: register with
10, 3. Zero, negative and duplicate values are dropped, and a reminder due less than a minute from now is skipped. - Reminders are scheduled with the confirmation: when the booking is made, approved or paid. A reschedule clears and schedules them again. They use Action Scheduler when it is installed, WP-Cron otherwise, and go out only while the booking is confirmed or awaiting payment.
- Return the same list every time. Without Action Scheduler, the filter runs again with an empty
$rowwhen reminders are cleared (on cancellation and reschedule): a list built from$rowwould leave reminders behind. - The subject reads “Starting soon” for offsets under 12 hours. This filter only covers appointments: sign-up reminders follow “Send reminder hours before start” and fire
happenboard_cron_reminder.
happenboard_mp_rsvp_created
The MailerPress trigger fired when someone signs up for an event. Use it to welcome the attendee, send practical details or start a sequence before the event.
Signature
do_action( 'happenboard_mp_rsvp_created', array<string,mixed> $ctx )
Parameters
-
$ctxarray<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 {
// Log each sign-up sent to MailerPress.
error_log(
sprintf(
'%s registered for %s (%d seats).',
(string) ( $ctx['email'] ?? '' ),
(string) ( $ctx['event_title'] ?? '' ),
(int) ( $ctx['quantity'] ?? 1 )
)
);
}
);
Notes
- One argument. It follows
happenboard_rsvp_submittedat priority 20, after HappenBoard’s confirmation email at priority 10. A sign-up put on the waitlist fires nothing until it is moved to going, throughhappenboard_attendee_status_changed. - When attendee sync is on, the MailerPress contact is created or updated before the action fires, so it exists when MailerPress runs the trigger.
- Paid orders fire the underlying action once per ticket line, so one Stripe checkout or WooCommerce order can fire several triggers for the same buyer.
event_dateis the attendee’s date (their date in a series), as a readable string in the site’s date and time formats.first_nameandlast_namecome from splitting the name at its first space.
happenboard_mp_rsvp_cancelled
The MailerPress 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
-
$ctxarray<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 {
// Tag the contact in your own system.
my_plugin_tag_contact(
(string) ( $ctx['email'] ?? '' ),
'cancelled-' . (int) ( $ctx['event_id'] ?? 0 )
);
}
);
Notes
- One argument. It follows
happenboard_rsvp_cancelledat priority 20: the attendee’s cancel link, but also a Stripe refund or a reversed WooCommerce order, once per attendee row. Listen tohappenboard_rsvp_cancelleddirectly if you need to tell them apart. - An administrator setting the status to
cancelledin the attendee list does not fire this trigger. - No contact is created or updated here: only the sign-up trigger syncs contacts.
- The context is read after the cancellation, so
$ctxdescribes a row whose status is alreadycancelled.
happenboard_mp_checked_in
The MailerPress trigger fired when an attendee is checked in at the door.
Signature
do_action( 'happenboard_mp_checked_in', array<string,mixed> $ctx )
Parameters
-
$ctxarray<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 {
// Log each check-in sent to MailerPress.
error_log(
sprintf(
'%s checked in at %s.',
(string) ( $ctx['email'] ?? '' ),
(string) ( $ctx['event_title'] ?? '' )
)
);
}
);
Notes
- One argument. It follows
happenboard_pro_attendee_checked_inat priority 20, so it also fires when a ticket is scanned again. - The check-in row ID is not in
$ctx. Hookhappenboard_pro_attendee_checked_inif you need it. - It never fires with an empty context: an attendee row that cannot be read, or has no email, fires nothing.
happenboard_mp_reminder
The MailerPress trigger fired when an attendee’s reminder is due, 24 hours before their date by default. Use it for the “see you tomorrow” message.
Signature
do_action( 'happenboard_mp_reminder', array<string,mixed> $ctx )
Parameters
-
$ctxarray<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 {
// Log each reminder sent to MailerPress.
error_log(
sprintf(
'Reminder due for %s (event %d).',
(string) ( $ctx['email'] ?? '' ),
(int) ( $ctx['event_id'] ?? 0 )
)
);
}
);
Notes
- One argument. It follows
happenboard_cron_reminderat priority 20 and fires only in the run that sends HappenBoard’s own reminder, so once per attendee and date. - No reminder, no trigger. Nothing fires while “Send reminder email before each event” is off, for a sign-up made after the reminder time, or for an event cancelled, postponed or unpublished.
- Timing follows the reminder sweep: within 15 minutes after the lead time set in “Send reminder hours before start”.
- Cancelling the email on
happenboard_notifications_reminderkeeps this trigger.
happenboard_mp_post_event_tick
Fires once per attendee from the daily after-event scan. The MailerPress bridge turns it into happenboard_mp_post_event. Fire it yourself to send a follow-up to one attendee again.
Signature
do_action( 'happenboard_mp_post_event_tick', int $attendee_id, int $event_id )
Parameters
-
$attendee_idint - The attendee row ID.
-
$event_idint - The event whose date just ended.
Example
add_action(
'happenboard_mp_post_event_tick',
function ( int $attendee_id, int $event_id ): void {
// Log each follow-up the scan finds.
error_log(
sprintf(
'Follow-up due for attendee %d of event %d.',
$attendee_id,
$event_id
)
);
},
10,
2
);
Notes
- Two arguments: register with
10, 2. Unlike the otherhappenboard_mp_*hooks, it carries IDs, not the resolved context. - It comes from the daily WP-Cron job
happenboard_pro_mp_post_event_scan, which does nothing while the MailerPress integration is off. - The scan takes the dates that ended between 26 hours and 1 hour ago, at most 500 per run and 1,000 attendees per event. Each date of a recurring event counts, and so does an event saved without an end. Only attendees still going produce a tick, and on a series only those of the date that ended.
- Each date is recorded in the event’s
_happenboard_mp_post_event_firedmeta before its attendees are processed, so the scan never handles it twice. Callingdo_action( 'happenboard_mp_post_event_tick', $attendee_id, $event_id )yourself is the way to send a follow-up again.
happenboard_mp_post_event
The MailerPress trigger fired the day after an event’s date ends, once per attendee of that date 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
-
$ctxarray<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 {
// Queue a satisfaction survey.
my_plugin_queue_survey(
(string) ( $ctx['email'] ?? '' ),
(int) ( $ctx['event_id'] ?? 0 )
);
}
);
Notes
- One argument. It is the bridge’s version of
happenboard_mp_post_event_tick, so the same rules apply: each date of a recurring event counts, for its own attendees, and only attendees still going are included. - Timing depends on WP-Cron: a site with too few visits to run its daily jobs can miss the 26-hour window.
happenboard_pro_mailerpress_contact
Filters the MailerPress contact just before HappenBoard creates or updates it, so you can add custom fields, lists or your own opt-in source.
Signature
apply_filters( 'happenboard_pro_mailerpress_contact', array<string,mixed> $payload, array<string,mixed> $ctx )
Parameters
-
$payloadarray<string,mixed> - The arguments for
add_mailerpress_contact():email,firstName,lastName,subscription_status,lists,custom_fieldsandopt_in_source. -
$ctxarray<string,mixed> - The same resolved context the
happenboard_mp_*triggers receive.
Return value
array<string,mixed>
The arguments passed to add_mailerpress_contact(). Return the array you were given, extended.
Example
add_filter(
'happenboard_pro_mailerpress_contact',
function ( array $payload, array $ctx ): array {
// Store the number of seats on the contact.
$payload['custom_fields']['hp_seats'] = (string) ( $ctx['quantity'] ?? 1 );
return $payload;
},
10,
2
);
Notes
- Two arguments: register with
10, 2. - It only runs on sign-up, and only when attendee sync is on in the MailerPress settings of HappenBoard Pro.
- Keep the existing keys,
emailabove all. Add tocustom_fieldsrather than replacing it:hp_event_title,hp_event_date,hp_event_urlandhp_event_venueare the merge tags the HappenBoard triggers offer in MailerPress. subscription_statusfollows the opt-in setting (subscribedorpending). Forcingsubscribedoverrides a double opt-in choice.
Next steps
- Appointment booking hooks: the booking actions that send these emails.
- Attendees and sign-up hooks: the sign-up and cancellation actions behind the attendee emails.
- Customizing email branding: colors, logo and footer of every email.