Recipe: Custom RSVP Field
Goal: Collect an extra piece of data during RSVP, persist it against the RSVP record, and expose it in the attendee list and REST API, using only the real seams that ship today.
How custom RSVP data actually works. Eventonomy has no
evnm_register_metaand noevnm_registration_fieldshook; neither exists in the code. There are two real paths, and this recipe covers both:
- Built-in custom questions (site-owner bank + per-event selection). When the
custom_questionsfeature is on, the site owner defines the question bank in Settings → Questions, and each event selects which of those questions it asks. The RSVP block renders the event's selection, and submitted values arrive as ananswersmap that Free stores as one meta key:rsvp_answers.- Your own add-on data - persist any value you like against an RSVP from
evnm_after_create_rsvpusing the public Meta API.
Seams Used
| Seam | Type | Purpose |
|---|---|---|
custom_questions feature |
Feature flag | Gates the built-in per-attendee questions. Configured in Settings; readable via evnm_feature_enabled( 'custom_questions' ). The flag is only the first gate - see evnm_event_registration_questions() below. |
evnm_event_registration_questions( $event ) |
Function ( array $event ): array |
The only correct answer to "which questions does this event ask". Resolves the feature flag, the site-wide bank and the event's own settings.question_ids into the final question list, in bank order. |
evnm_after_create_rsvp |
Action ( $rsvp, $context ) |
Fires after an RSVP is created. Persist or forward custom data here. |
evnm_rest_prepare_rsvp |
Filter ( $data, $row, $request ) |
Add a computed field to each RSVP in REST output. |
evnm_get_meta / evnm_update_meta |
Functions | Read/write against evnm_meta (public Meta API). |
Path 1: Read the Built-in Custom-Question Answers
The site owner turns on the Custom questions feature card on the Settings → Questions tab, then adds questions in the builder on that same tab. Each question is normalised to { id, label, type, required, options }, where type ∈ text | textarea | select | checkbox.
No code is needed to render them - but rendering is gated twice, and the second gate is the one that catches people out:
- The
custom_questionsfeature must be on. - The event must ask the question. Each event stores its selection in
settings.question_ids. Never read that key directly - callevnm_event_registration_questions( $event ), which encodes the three states:
| Event state | What it asks |
|---|---|
No question_ids key at all (predates the picker) |
The whole bank |
question_ids present and non-empty |
Exactly those questions, in bank order |
question_ids present and empty |
Nothing |
Free uses that one resolver on every surface - the RSVP form, the REST write path, the admin attendee table and the CSV export - so your code stays consistent with all of them by calling it too. Answers submitted for a question the event does not ask are dropped on write, so a stale form or a crafted request cannot attach data no export will surface.
When a visitor submits the RSVP, Free sanitizes the answers (only while custom_questions is enabled) and stores them under one meta key, rsvp_answers, keyed by question id. Read them back anywhere:
<?php
// my-addon/my-addon.php
// React to a submitted answer - e.g. forward the attendee's answers to a CRM.
add_action( 'evnm_after_create_rsvp', function ( $rsvp, array $context ): void {
if ( ! is_array( $rsvp ) || empty( $rsvp['id'] ) ) {
return; // $rsvp is null if the insert failed.
}
// 'rsvp_answers' is a [ question_id => value ] map, or [] when none/feature off.
$answers = evnm_get_meta( 'rsvp', (int) $rsvp['id'], 'rsvp_answers', [] );
if ( ! empty( $answers['tshirt_size'] ) ) {
my_addon_push_to_crm( (int) $rsvp['id'], (string) $answers['tshirt_size'] );
}
}, 10, 2 );
// Surface one answer as a top-level field in the attendee REST response.
add_filter( 'evnm_rest_prepare_rsvp', function ( array $data, array $row, $request ): array {
$answers = evnm_get_meta( 'rsvp', (int) $row['id'], 'rsvp_answers', [] );
$data['tshirt_size'] = isset( $answers['tshirt_size'] ) ? (string) $answers['tshirt_size'] : '';
return $data;
}, 10, 3 );
Path 2: Persist Your Own Add-on Data on an RSVP
If your value does not come from the custom-questions form (for example you derive it server-side, or read it from a cookie/session your add-on controls), write it directly with the Meta API from evnm_after_create_rsvp. $type for the Meta API is rsvp.
add_action( 'evnm_after_create_rsvp', function ( $rsvp, array $context ): void {
if ( ! is_array( $rsvp ) || empty( $rsvp['id'] ) ) {
return;
}
// Never read $_POST here - derive the value yourself, then persist it.
$referral = my_addon_current_referral_code();
if ( '' !== $referral ) {
evnm_update_meta( 'rsvp', (int) $rsvp['id'], 'referral_code', sanitize_text_field( $referral ) );
}
}, 10, 2 );
Read it back the same way: evnm_get_meta( 'rsvp', $rsvp_id, 'referral_code', '' ), and expose it through evnm_rest_prepare_rsvp exactly as in Path 1.
Verify It Worked
- Enable Settings → Features → Custom questions, then add a
selectquestion labelled "T-shirt size" (idtshirt_size). - Visit a published event's single-event page and submit an RSVP; the T-shirt size field appears in the form.
- After submission, read the stored answers in WP-CLI:
wp eval 'print_r( evnm_get_meta( "rsvp", RSVP_ID, "rsvp_answers", [] ) );' - As an organizer, request the attendee list and confirm your computed field is present:
curl -s -u admin:password \ "https://yoursite.com/wp-json/eventonomy/v1/events/EVENT_ID/attendees" \ | jq '.items[].tshirt_size'
Related
- Hooks & Filters -
evnm_after_create_rsvpandevnm_rest_prepare_rsvpsignatures. docs/EXTENDING.md§8 - Meta API reference (evnm_get_meta/evnm_update_meta/evnm_delete_meta).- Recipes Index