Eventonomy

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_meta and no evnm_registration_fields hook; neither exists in the code. There are two real paths, and this recipe covers both:

  1. Built-in custom questions (site-owner bank + per-event selection). When the custom_questions feature 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 an answers map that Free stores as one meta key: rsvp_answers.
  2. Your own add-on data - persist any value you like against an RSVP from evnm_after_create_rsvp using 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:

  1. The custom_questions feature must be on.
  2. The event must ask the question. Each event stores its selection in settings.question_ids. Never read that key directly - call evnm_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

  1. Enable Settings → Features → Custom questions, then add a select question labelled "T-shirt size" (id tshirt_size).
  2. Visit a published event's single-event page and submit an RSVP; the T-shirt size field appears in the form.
  3. After submission, read the stored answers in WP-CLI:
    wp eval 'print_r( evnm_get_meta( "rsvp", RSVP_ID, "rsvp_answers", [] ) );'
    
  4. 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_rsvp and evnm_rest_prepare_rsvp signatures.
  • docs/EXTENDING.md §8 - Meta API reference (evnm_get_meta / evnm_update_meta / evnm_delete_meta).
  • Recipes Index