This page lists the main PHP actions and filters available in Elzo Forms. Use these hooks when you need to validate submissions, detect spam, customize saved data, modify email settings, change AJAX responses, register modules, customize templates, or extend PRO automations.
This is a developer reference. If you are looking for task-based examples, see the related developer guides for custom fields, modules, template overrides, JavaScript events, and custom automation actions.
How Elzo Forms processes a submission
Most submission-related hooks run in this order:
- The form submission request is received.
- Nonce, form ID, form object, timing, and IP checks are validated.
elzo_forms_validate_submissionruns for custom server-side validation.elzo_forms_detect_spamruns for custom spam detection.- Submitted fields are matched against visible form fields.
elzo_forms_is_field_value_emptyruns for required-field empty checks.- Field classes validate and sanitize field values.
- The submission is saved.
elzo_forms_after_submission_savedruns for non-spam submissions.- Enabled modules handle the submission.
elzo_forms_submission_email_settingsruns before the built-in email notification is sent.elzo_forms_submission_responseruns before the final AJAX success response is returned.
Important: use the earliest hook that matches your goal. For example, use elzo_forms_validate_submission to block a submission, elzo_forms_after_submission_saved to run logic after saving, and elzo_forms_submission_response to customize the frontend success response.
Quick reference
| Hook | Type | Scope | Use it to |
|---|---|---|---|
elzo_forms_validate_submission |
Filter | Free | Run custom server-side validation before field processing continues. |
elzo_forms_detect_spam |
Filter | Free | Mark a submission as spam using custom rules. |
elzo_forms_is_field_value_empty |
Filter | Free | Customize how Elzo Forms decides whether a required field is empty. |
elzo_forms_after_submission_saved |
Action | Free | Run custom logic after a valid non-spam submission is saved. |
elzo_forms_submission_email_settings |
Filter | Free | Change built-in email notification settings at runtime. |
elzo_forms_submission_response |
Filter | Free | Customize the AJAX success response returned to the frontend. |
elzo_forms_field_types |
Filter | Free | Register or modify available field types. |
elzo_forms_field |
Filter | Free | Modify field data before rendering. |
elzo_forms_field_type_variants |
Filter | Free | Give a field type variants, stored as base:variant. |
elzo_forms_field_picker_items |
Filter | Free | Set the group, icon and search terms of field types in the builder. |
elzo_forms_is_text_field_value_valid |
Filter | Free | Validate the format of a Text variant. |
elzo_forms_block_form_options |
Filter | Free | Change which forms the Elzo Form block offers. |
elzo_forms_content_search_query_args |
Filter | Free | Limit the content offered by the admin page pickers. |
elzo_forms_form_data_attributes |
Filter | Free | Add custom HTML data attributes to the rendered form element. |
elzo_forms/templates/* |
Action / Filter | Free | Customize template loading, template arguments, and before/after template output. |
elzo_forms/modules/register |
Action | Free | Register a custom module with the module manager. |
elzo_forms_pro_register_automation_extensions |
Action | PRO | Register custom automation actions, condition operators, controls, and data providers. |
elzo_forms_pro_automation_before_action |
Action | Pro | Run logic immediately before an automation action executes. |
elzo_forms_pro_automation_after_action |
Action | Pro | Inspect or react to an automation action result. |
Submission hooks
elzo_forms_validate_submission
Type: Filter
Scope: Free
Runs: after the form object and timing checks are prepared, before submitted fields are processed.
Use this to: block a submission with custom server-side validation.
Signature:
add_filter( 'elzo_forms_validate_submission', function( $result, $form, $context ) {
return $result;
}, 10, 3 );
Parameters:
$result— default validation result. Usuallytrue.$form— current\ElzoForms\Form\Forminstance.$context— array with request timing data, includingsubmission_timeandform_load_time.
Return: return the current result to continue, or return a WP_Error to stop the submission and show an error message.
Example: block submissions outside business hours.
<?php
add_filter( 'elzo_forms_validate_submission', function( $result, $form, $context ) {
$hour = (int) current_time( 'G' );
if ( $hour < 9 || $hour >= 18 ) {
return new WP_Error(
'elzo_forms_closed',
__( 'This form is available only during business hours.', 'my-plugin' )
);
}
return $result;
}, 10, 3 );
Note: do not return false expecting the submission to stop. To block processing, return a WP_Error.
elzo_forms_detect_spam
Type: Filter
Scope: Free
Runs: before submitted fields are processed and before the submission is saved.
Use this to: mark a submission as spam based on custom rules.
Signature:
add_filter( 'elzo_forms_detect_spam', function( $is_spam, $form, $context ) {
return $is_spam;
}, 10, 3 );
Parameters:
$is_spam— current spam status as boolean.$form— current\ElzoForms\Form\Forminstance.$context— array with timing data.
Return: boolean. Return true to mark the submission as spam.
Example: mark submissions from a suspicious query string as spam.
<?php
add_filter( 'elzo_forms_detect_spam', function( $is_spam, $form, $context ) {
if ( isset( $_GET['traffic_source'] ) && $_GET['traffic_source'] === 'bad-campaign' ) {
return true;
}
return $is_spam;
}, 10, 3 );
elzo_forms_is_field_value_empty
Type: Filter
Scope: Free
Runs: when Elzo Forms checks whether a visible required field is empty.
Use this to: customize empty-value detection for special field values.
Signature:
add_filter( 'elzo_forms_is_field_value_empty', function( $is_empty, $value, $field, $form ) {
return $is_empty;
}, 10, 4 );
Parameters:
$is_empty— current empty check result.$value— submitted field value.$field— current field object.$form— current form object.
Return: boolean. Return true when the value should be treated as empty.
Example: treat a placeholder option value as empty.
<?php
add_filter( 'elzo_forms_is_field_value_empty', function( $is_empty, $value, $field, $form ) {
if ( $value === 'please-select' ) {
return true;
}
return $is_empty;
}, 10, 4 );
elzo_forms_after_submission_saved
Type: Action
Scope: Free
Runs: after a valid non-spam submission is saved, before built-in email settings are applied and before the final response is sent.
Use this to: send data to external services, create posts, update metadata, or trigger custom business logic after a successful submission.
Signature:
add_action( 'elzo_forms_after_submission_saved', function( $form, $submission, $submission_post_id ) {
// Your logic here.
}, 10, 3 );
Parameters:
$form— current\ElzoForms\Form\Forminstance.$submission— current\ElzoForms\Submission\Submissioninstance.$submission_post_id— saved submission post ID.
Example: store a custom meta flag on every saved submission.
<?php
add_action( 'elzo_forms_after_submission_saved', function( $form, $submission, $submission_post_id ) {
update_post_meta( $submission_post_id, '_sent_to_custom_integration', 'no' );
}, 10, 3 );
Example: read submitted fields.
<?php
add_action( 'elzo_forms_after_submission_saved', function( $form, $submission, $submission_post_id ) {
$fields = $submission->get_fields();
foreach ( $fields as $field ) {
$field_id = $field['id'] ?? '';
$value = $field['value'] ?? '';
// Use $field_id and $value here.
}
}, 10, 3 );
elzo_forms_submission_email_settings
Type: Filter
Scope: Free
Runs: before the built-in email notification is sent.
Use this to: enable, disable, or change built-in notification recipients at runtime.
Signature:
add_filter( 'elzo_forms_submission_email_settings', function( $email_settings, $form, $submission, $submission_post_id ) {
return $email_settings;
}, 10, 4 );
Parameters:
$email_settings— array of email notification settings.$form— current form object.$submission— current submission object.$submission_post_id— saved submission post ID.
Return: modified email settings array.
Common keys:
email_notifications—yesorno.email_notification_recipients— comma-separated string, newline-separated string, or array of email addresses.
Example: send notifications for a specific form to a different recipient.
<?php
add_filter( 'elzo_forms_submission_email_settings', function( $email_settings, $form, $submission, $submission_post_id ) {
if ( method_exists( $form, 'get_slug' ) && $form->get_slug() === 'partner-request' ) {
$email_settings['email_notifications'] = 'yes';
$email_settings['email_notification_recipients'] = 'partners@example.com';
}
return $email_settings;
}, 10, 4 );
elzo_forms_submission_response
Type: Filter
Scope: Free
Runs: after submission processing, before the AJAX success response is returned.
Use this to: add custom response data, adjust redirects, or pass custom data to frontend scripts.
Signature:
add_filter( 'elzo_forms_submission_response', function( $response, $submission, $form ) {
return $response;
}, 10, 3 );
Parameters:
$response— response array.$submission— current submission object.$form— current form object.
Default response keys:
messageredirect_urlredirect_delaysubmission_post_id
Example: add a custom tracking value to the AJAX response.
<?php
add_filter( 'elzo_forms_submission_response', function( $response, $submission, $form ) {
$response['tracking_event'] = 'elzo_form_submission';
$response['form_id'] = method_exists( $form, 'get_id' ) ? $form->get_id() : null;
return $response;
}, 10, 3 );
Field and form rendering hooks
elzo_forms_field_types
Type: Filter
Scope: Free
Runs: when Elzo Forms builds the list of available field types.
Use this to: add or modify field type definitions.
Signature:
add_filter( 'elzo_forms_field_types', function( $types ) {
return $types;
} );
Return: array of field type definitions. Each field type should include a human-readable label and a PHP class name in class.
Example: register a custom field type.
<?php
add_filter( 'elzo_forms_field_types', function( $types ) {
$types['rating'] = [
'label' => __( 'Rating', 'my-plugin' ),
'class' => \MyPlugin\ElzoForms\Fields\Rating_Field::class,
];
return $types;
} );
elzo_forms_field_text_subtypes
Type: Filter
Scope: Free
Use this to: add or modify the variants of the Text field. The defaults are text, date, time, number, email, url, tel, and password. A variant is stored in the field type as text:{key}, gets its own entry in the builder’s field type list, and its key becomes the type attribute of the rendered input. A variant you add has no format check until you give it one with elzo_forms_is_text_field_value_valid.
<?php
add_filter( 'elzo_forms_field_text_subtypes', function( $subtypes ) {
$subtypes['color'] = __( 'Color', 'my-plugin' );
return $subtypes;
} );
elzo_forms_field_type_variants
Type: Filter
Scope: Free
Runs: whenever Elzo Forms resolves the variants of a base field type.
Arguments: $variants — labels keyed by variant; $base_type — the base field type. Text’s variants arrive here from elzo_forms_field_text_subtypes.
Use this to: give any field type variants. A variant is stored as {base type}:{variant}. The variant keyed like the base type, or else the first one, is the default and is stored as the bare base type. Keys may contain only letters, digits, _ and -; the field class reads its variant with get_subtype().
<?php
add_filter( 'elzo_forms_field_type_variants', function( $variants, $base_type ) {
if ( 'star_rating' === $base_type ) {
$variants = [
'star_rating' => __( 'Star rating', 'my-plugin' ),
'hearts' => __( 'Heart rating', 'my-plugin' ),
];
}
return $variants;
}, 10, 2 );
Return: array of labels keyed by variant.
elzo_forms_field_picker_items
Type: Filter
Scope: Free
Runs: when the builder prepares the field type list opened by Add Field and by each field’s Type button.
Arguments: $items — a list of items with the keys type, label, category, icon and keywords. Types without built-in presentation arrive with type only.
Use this to: set the group, icon, label and search terms of a field type. Items naming a type that is not registered are dropped, so the list can never offer a type the builder cannot create. The accepted categories and icons are listed in Field Type Filters.
<?php
add_filter( 'elzo_forms_field_picker_items', function( $items ) {
foreach ( $items as &$item ) {
if ( 'star_rating' === $item['type'] ) {
$item['category'] = 'choice';
$item['icon'] = 'range';
$item['keywords'] = [ 'stars', 'score', 'review' ];
}
}
unset( $item );
return $items;
} );
elzo_forms_is_text_field_value_valid
Type: Filter
Scope: Free
Runs: during submission validation, for every non-empty value of every Text variant — first on the submitted value, then on the sanitized one.
Arguments: $is_valid — the built-in verdict; $value — the value without surrounding whitespace; $subtype — the variant, such as email or tel; $field — the field data.
Use this to: give a custom Text variant a format, or tighten a built-in one. Returning false rejects the submission with the variant’s error message.
<?php
add_filter( 'elzo_forms_is_text_field_value_valid', function( $is_valid, $value, $subtype, $field ) {
// Require international phone numbers.
if ( 'tel' === $subtype && 0 !== strpos( $value, '+' ) ) {
return false;
}
return $is_valid;
}, 10, 4 );
Return: true to accept the value, false to reject it.
elzo_forms_field_widths
Type: Filter
Scope: Free
Use this to: add or modify field width options in the admin UI.
<?php
add_filter( 'elzo_forms_field_widths', function( $widths ) {
$widths['2/3'] = __( 'Two thirds (2/3)', 'my-plugin' );
return $widths;
} );
elzo_forms_field
Type: Filter
Scope: Free
Runs: when field data is prepared from a field object.
Use this to: modify field data before rendering.
Signature:
add_filter( 'elzo_forms_field', function( $field_data, $field ) {
return $field_data;
}, 10, 2 );
Example: add a custom class to a specific field key.
<?php
add_filter( 'elzo_forms_field', function( $field_data, $field ) {
if ( isset( $field_data['field_key'] ) && $field_data['field_key'] === 'company_email' ) {
$field_data['custom_class'] = trim( ( $field_data['custom_class'] ?? '' ) . ' is-company-email' );
}
return $field_data;
}, 10, 2 );
elzo_forms_form_data_attributes
Type: Filter
Scope: Free
Runs: when the form HTML attributes are prepared.
Use this to: add custom data-* attributes to the form wrapper.
Signature:
add_filter( 'elzo_forms_form_data_attributes', function( $attributes, $form_id ) {
return $attributes;
}, 10, 2 );
Example: expose a custom integration flag to frontend scripts.
<?php
add_filter( 'elzo_forms_form_data_attributes', function( $attributes, $form_id ) {
$attributes['data-my-integration-enabled'] = '1';
return $attributes;
}, 10, 2 );
elzo_forms_handle_phone_field
Type: Filter
Scope: Free
Runs: when a telephone text field is sanitized.
Use this to: normalize telephone values before they are stored.
<?php
add_filter( 'elzo_forms_handle_phone_field', function( $phone, $field_data ) {
return preg_replace( '/[^\d+]/', '', $phone );
}, 10, 2 );
Block editor and conditional logic hooks
elzo_forms_block_form_options
Type: Filter
Scope: Free
Runs: when the block editor loads, for users who can edit posts.
Arguments: $options — the forms offered by the Elzo Form block, each with id, title, status and statusLabel.
Use this to: remove forms from the block’s form selector. Do not add form settings or content to the entries: the list is printed into the editor page.
<?php
add_filter( 'elzo_forms_block_form_options', function( $options ) {
return array_values( array_filter( $options, function( $option ) {
return strpos( $option['title'], '[internal]' ) === false;
} ) );
} );
elzo_forms_condition_types
Type: Filter
Scope: Free
Runs: when the condition types of a context are built. Field visibility uses the context field.
Arguments: $types — labels keyed by condition type; $usage_context.
Use this to: remove a condition type from the builder. Rules already saved with that type keep working. Do not add types here: Elzo Forms evaluates only its own condition types, and a rule of any other type never matches.
<?php
add_filter( 'elzo_forms_condition_types', function( $types, $usage_context ) {
if ( 'field' === $usage_context ) {
unset( $types['page'] );
}
return $types;
}, 10, 2 );
elzo_forms_condition_operators_map
Type: Filter
Scope: Free
Arguments: $operators — lists of operator keys, keyed by condition type; $usage_context.
Use this to: remove operators a condition type offers in the builder. The same rule as for types applies: an operator Elzo Forms does not implement never matches.
elzo_forms_condition_operator_labels
Type: Filter
Scope: Free
Arguments: $labels — labels keyed by operator; $usage_context.
Use this to: rename operators in the condition builder.
elzo_forms_condition_type_picker_items
Type: Filter
Scope: Free
Arguments: $items — the entries of the condition type list, with type, label, icon and keywords; $registered — labels of the registered condition types.
Use this to: change how condition types are presented: label, icon and search terms. It changes presentation only — an entry can be chosen only when its type is registered.
elzo_forms_content_post_types
Type: Filter
Scope: Free
Arguments: $post_types — post type slugs. The default is every public post type except attachments.
Use this to: choose the post types the admin content pickers offer and search — the page and post type values of Page conditions, and in PRO the page picker of automation conditions. An empty result falls back to pages.
<?php
add_filter( 'elzo_forms_content_post_types', function( $post_types ) {
return [ 'page', 'product' ];
} );
elzo_forms_content_search_query_args
Type: Filter
Scope: Free
Arguments: $query_args — the WP_Query arguments; $context — search, include, post_types, limit and page.
Use this to: keep content out of the admin content pickers with ordinary query arguments such as post__not_in or tax_query. Entries that are not published in a public post type are offered only to users who can edit them, whatever the query returns.
<?php
add_filter( 'elzo_forms_content_search_query_args', function( $query_args, $context ) {
$query_args['post__not_in'] = [ (int) get_option( 'page_on_front' ) ];
return $query_args;
}, 10, 2 );
Settings and saved form data hooks
elzo_forms_default_settings
Type: Filter
Scope: Free
Use this to: add or modify default form settings.
<?php
add_filter( 'elzo_forms_default_settings', function( $settings ) {
$settings['my_custom_setting'] = '';
return $settings;
} );
elzo_forms_form_settings
Type: Filter
Scope: Free
Runs: after global, default, and form-specific settings are merged.
Use this to: modify runtime form settings.
<?php
add_filter( 'elzo_forms_form_settings', function( $settings, $form_post ) {
if ( $form_post instanceof WP_Post && $form_post->post_name === 'contact-us' ) {
$settings['min_submission_delay'] = 3;
}
return $settings;
}, 10, 2 );
elzo_forms_form_styles
Type: Filter
Scope: Free
Use this to: modify runtime form style settings.
<?php
add_filter( 'elzo_forms_form_styles', function( $styles, $form_post ) {
$styles['primary_color'] = '#1d4ed8';
return $styles;
}, 10, 2 );
elzo_forms_form_texts
Type: Filter
Scope: Free
Use this to: modify runtime form text values.
<?php
add_filter( 'elzo_forms_form_texts', function( $texts, $form_post ) {
$texts['success_submit_message'] = __( 'Thank you. We received your message.', 'my-plugin' );
return $texts;
}, 10, 2 );
elzo_forms_form_data_before_save
Type: Filter
Scope: Free / Pro
Runs: before the form builder JSON data is saved to the form post content.
Use this to: add extension data to the saved form JSON payload.
Signature:
add_filter( 'elzo_forms_form_data_before_save', function( $data, $post_id, $raw_post ) {
return $data;
}, 10, 3 );
Example: persist custom builder data.
<?php
add_filter( 'elzo_forms_form_data_before_save', function( $data, $post_id, $raw_post ) {
if ( isset( $raw_post['my_plugin_form_meta'] ) ) {
$data['my_plugin'] = [
'meta' => sanitize_text_field( wp_unslash( $raw_post['my_plugin_form_meta'] ) ),
];
}
return $data;
}, 10, 3 );
Important: only store data you own under a unique top-level key to avoid conflicts with Elzo Forms core data.
Admin save filters
These filters are useful when extending the form builder or normalizing saved form configuration.
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
elzo_forms_field_before_save |
Filter | $field |
Modify a field array before it is stored. |
elzo_forms_step_before_save |
Filter | $step |
Modify a step array before it is stored. |
elzo_forms_form_settings_value_before_save |
Filter | $value, $key |
Modify any form setting value before saving. |
elzo_forms_form_settings_{$key}_before_save |
Filter | $value |
Modify one specific form setting before saving. |
elzo_forms_form_texts_value_before_save |
Filter | $value, $key |
Modify any form text value before saving. |
elzo_forms_form_texts_{$key}_before_save |
Filter | $value |
Modify one specific form text before saving. |
elzo_forms_json_encoding_error |
Action | $exception, $post_id |
Handle JSON encoding failures while saving a form. |
Template hooks
Elzo Forms supports WooCommerce-style template overrides and template-loading hooks.
elzo_forms/templates/theme_dir
Type: Filter
Scope: Free
Use this to: change the theme directory where Elzo Forms looks for overridden templates. The value is a folder name relative to the theme root; the default is elzo-forms. Register the filter anywhere that runs before a form is rendered, including a theme’s functions.php.
<?php
add_filter( 'elzo_forms/templates/theme_dir', function( $dir ) {
return 'my-theme-forms';
} );
elzo_forms/templates/locate_template
Type: Filter
Scope: Free
Use this to: override the located template path programmatically.
<?php
add_filter( 'elzo_forms/templates/locate_template', function( $template, $template_name, $template_path, $args ) {
if ( $template_name === 'form.php' ) {
$custom_template = plugin_dir_path( __FILE__ ) . 'templates/elzo-form.php';
if ( file_exists( $custom_template ) ) {
return $custom_template;
}
}
return $template;
}, 10, 4 );
elzo_forms/templates/template_args
Type: Filter
Scope: Free
Use this to: add or modify variables available inside frontend templates.
<?php
add_filter( 'elzo_forms/templates/template_args', function( $args, $template_name, $template ) {
if ( $template_name === 'form.php' ) {
$args['my_custom_value'] = 'Example value';
}
return $args;
}, 10, 3 );
elzo_forms/templates/before_template and elzo_forms/templates/after_template
Type: Action
Scope: Free
Use this to: output or run logic before or after a frontend template is loaded.
<?php
add_action( 'elzo_forms/templates/before_template', function( $template_name, $template_path, $template, $args ) {
if ( $template_name === 'form.php' ) {
echo '<div class="my-elzo-form-wrapper">';
}
}, 10, 4 );
add_action( 'elzo_forms/templates/after_template', function( $template_name, $template_path, $template, $args ) {
if ( $template_name === 'form.php' ) {
echo '</div>';
}
}, 10, 4 );
Module hooks
elzo_forms/modules/register
Type: Action
Scope: Free
Runs: after the core module manager is created and the core modules are registered.
Use this to: register custom modules.
Signature:
add_action( 'elzo_forms/modules/register', function( $manager ) {
// $manager->register( new Your_Module() );
} );
Example:
<?php
add_action( 'elzo_forms/modules/register', function( $manager ) {
if ( class_exists( \MyPlugin\ElzoForms\Modules\My_Module::class ) ) {
$manager->register( new \MyPlugin\ElzoForms\Modules\My_Module() );
}
} );
elzo_forms/modules/enabled
Type: Filter
Scope: Free
Use this to: modify the list of enabled modules for a specific form.
<?php
add_filter( 'elzo_forms/modules/enabled', function( $enabled, $form_id ) {
if ( (int) $form_id === 123 ) {
$enabled[] = 'my_module';
}
return array_values( array_unique( $enabled ) );
}, 10, 2 );
elzo_forms/module/{$id}/settings
Type: Filter
Scope: Free
Use this to: modify a specific module settings array for one form.
<?php
add_filter( 'elzo_forms/module/my_module/settings', function( $settings, $form_id ) {
$settings['enabled_for_custom_flow'] = true;
return $settings;
}, 10, 2 );
JSON storage hooks
Use these hooks when integrating with JSON-based form sync, deployment workflows, or custom import/export tools.
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
elzo_forms/json_storage/paths |
Filter | $paths |
Change the directories scanned for form JSON files. |
elzo_forms/load_form_data |
Filter | $data, $post |
Modify loaded form data, including JSON-file-backed data. |
elzo_forms_form_compatibility_warning |
Action | $warning, $result, $form_id |
React to a JSON form that requires a newer plugin version. |
elzo_forms/json_storage/import_form_payload |
Filter | $payload, $form_data, $filepath |
Sanitize or modify imported form payload data. |
Example: strip an environment-specific value from an imported form.
<?php
add_filter( 'elzo_forms/json_storage/import_form_payload', function( $payload, $form_data, $filepath ) {
if ( isset( $payload['data']['my_plugin']['local_secret'] ) ) {
unset( $payload['data']['my_plugin']['local_secret'] );
}
return $payload;
}, 10, 3 );
Submission admin table hooks
These hooks customize the WordPress admin submission list table.
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
elzo_forms_submission_table_field_labels |
Filter | $field_labels, $submission_fields |
Modify dynamic field column labels. |
elzo_forms_submission_table_field |
Filter | $field, $column, $submission_data |
Modify the value used for a submission table field column. |
elzo_forms_submission_table_form_title |
Filter | $form_title, $form_id |
Modify the displayed form title in the submissions table. |
elzo_forms_submission_table_form_edit_url |
Filter | $form_edit_url, $form_id |
Modify the form edit URL in the submissions table. |
elzo_forms_submission_form_dropdown_options |
Filter | $forms |
Modify form options in the submission filter dropdown. |
Email template hooks
elzo_forms_admin_email_field
Type: Filter
Scope: Free
Runs: while preparing fields for the built-in admin email template.
Use this to: modify how a submitted field appears in the admin notification email.
<?php
add_filter( 'elzo_forms_admin_email_field', function( $field ) {
if ( isset( $field['value'] ) && is_array( $field['value'] ) ) {
$field['value'] = implode( ', ', $field['value'] );
}
return $field;
} );
File upload hooks
elzo_forms_dangerous_extensions
Type: Filter
Scope: Free
Runs: during upload validation.
Use this to: add more extensions to the blocked file extension list.
Default blocked extensions include: php, php3, php4, php5, phtml, and phar.
<?php
add_filter( 'elzo_forms_dangerous_extensions', function( $extensions ) {
$extensions[] = 'sh';
$extensions[] = 'exe';
return array_values( array_unique( $extensions ) );
} );
Security note: do not remove PHP-related extensions from this list on public websites.
PRO automation hooks
These hooks exist only when Elzo Forms PRO is active. Automations are described in Automations Overview.
elzo_forms_pro_register_automation_extensions
Type: Action
Runs: once, when the automation extension registry boots.
Arguments: $registrar — an \ElzoFormsPro\Automation\Extensions\AutomationExtensionRegistrar.
Use this to: register custom actions, condition operators, controls, codecs, categories, and workflow data providers.
This is the single entry point for extending automations. There is no filter that accepts a list of class names — you register instances (or factories) on the registrar:
<?php
add_action( 'elzo_forms_pro_register_automation_extensions', function ( $registrar ) {
$registrar->register_action( new \MyPlugin\Automation\CreateLeadAction() );
} );
The registrar exposes:
| Method | Registers |
|---|---|
register_action( ActionInterface $action ) |
An automation action. |
register_action_factory( string $type, callable $factory ) |
An action constructed on first use. |
register_condition_operator( ConditionOperatorInterface $operator ) |
A condition operator. |
register_condition_operator_factory( string $key, callable $factory ) |
An operator constructed on first use. |
register_control( ActionControlInterface $control ) |
A settings control for the action editor. |
register_codec( ActionCodecInterface $codec ) |
A codec that converts editor input to stored settings. |
register_category( AutomationCategoryDefinition $category ) |
A category in the action library. |
register_data_provider( WorkflowDataProviderInterface $provider ) |
A workflow data provider. |
Registries are validated and frozen immediately after this action completes. Registering later throws a LogicException, so hook this action rather than deferring to init.
If registration throws, the whole extension registry fails to boot, elzo_forms_pro_automation_extensions_boot_failed fires with the exception, and no automation runs for that request.
The action contract
An action implements \ElzoFormsPro\Automation\Contracts\ActionInterface, which has exactly two methods:
<?php
namespace MyPlugin\Automation;
use ElzoFormsPro\Automation\Contracts\ActionInterface;
use ElzoFormsPro\Automation\Runtime\ExecutionContext;
use ElzoFormsPro\Automation\ValueObjects\ActionDefinition;
use ElzoFormsPro\Automation\ValueObjects\ActionResult;
class CreateLeadAction implements ActionInterface {
public function get_definition(): ActionDefinition {
return new ActionDefinition(
'my_create_lead',
__( 'Create lead', 'my-plugin' ),
__( 'Send the submission to the CRM.', 'my-plugin' ),
'http',
'elzo-icon-link',
[
'type' => 'object',
'properties' => [
'list_id' => [
'type' => 'string',
'label' => __( 'List ID', 'my-plugin' ),
'allow_dynamic' => true,
'ui' => [ 'control' => 'text' ],
],
],
'required' => [ 'list_id' ],
'additional_properties' => false,
],
[
'type' => 'object',
'properties' => [
'lead_id' => [ 'type' => 'integer', 'label' => __( 'Lead ID', 'my-plugin' ) ],
],
'required' => [ 'lead_id' ],
'additional_properties' => false,
],
[ 'has_side_effects' => true, 'supports_test' => true ]
);
}
public function execute( array $settings, ExecutionContext $context ): ActionResult {
$lead_id = my_plugin_create_lead( $settings['list_id'] );
if ( ! $lead_id ) {
return ActionResult::failed( 'my_create_lead_failed', __( 'The CRM rejected the lead.', 'my-plugin' ) );
}
return ActionResult::success( [ 'lead_id' => $lead_id ], __( 'Lead created.', 'my-plugin' ) );
}
}
ActionDefinition takes the type, label, description, category, icon, settings schema, outputs schema, and flags. Settings are validated against the schema before execute() runs, and outputs are validated against the outputs schema after it returns — an output the schema does not declare is rejected.
Declaring 'supports_test' => true together with 'has_side_effects' => true also requires implementing SimulatableActionInterface, or registration fails validation.
ActionResult
Return one of three factory methods:
| Factory | Signature |
|---|---|
ActionResult::success() |
$outputs, $message, $context_patch, $response_patch, $control, $meta |
ActionResult::failed() |
$error_code, $error_message, $error_data, $outputs, $control, $meta |
ActionResult::skipped() |
$message, $meta |
$control accepts ActionResult::CONTROL_CONTINUE, CONTROL_STOP_AUTOMATION, or CONTROL_STOP_ALL. The action’s configured On error setting applies on failure; returning an explicit control value overrides it.
$context_patch writes values back into workflow state for later actions. $response_patch modifies the response returned to the browser. Both are size-limited by the runtime and validated before they are applied.
ExecutionContext
Actions receive an ExecutionContext object, not an array. Read workflow data through it by path:
$email = $context->get( 'fields.by_key.email', '' );
$form = $context->get_form(); // \ElzoForms\Form\Form
$entry = $context->get_submission(); // \ElzoForms\Submission\Submission
$user = $context->get_user(); // \WP_User
$is_test = $context->is_test();
| Method | Returns |
|---|---|
get( string $path, $default = null ) |
A workflow value, such as fields.by_key.email or variables.total. |
has( string $path ) |
Whether the path resolves. |
resolve( $value ) |
A ValueResolutionResult for a value containing {{ }} references. |
get_form(), get_submission(), get_user() |
The form, submission, and submitting user. |
is_live(), is_test(), get_mode() |
Whether this is a real run or a test run. |
get_http_client() |
The guarded HTTP client. Use it instead of wp_remote_request() so runtime limits apply. |
get_remaining_deadline_seconds() |
Time left in the run’s budget. |
Check is_test() before performing any side effect that a configuration test should not cause.
Lifecycle actions
| Hook | Arguments | Runs |
|---|---|---|
elzo_forms_pro_automation_before_automation |
$automation, $context |
Before an automation’s conditions are evaluated. |
elzo_forms_pro_automation_after_automation |
$automation, $context, $automation_result |
After the automation finishes. |
elzo_forms_pro_automation_before_action |
$automation, $action_payload, $context |
Before an action executes. |
elzo_forms_pro_automation_after_action |
$automation, $action_payload, $context, $action_result |
After an action executes. |
elzo_forms_pro_automation_after_run |
$context, $results, $response_patch, $trigger |
After every automation for the submission has run. |
elzo_forms_pro_automation_before_evaluate_conditions |
$tree, $state, $scope, $scope_id |
Before a condition tree is evaluated. |
elzo_forms_pro_automation_after_evaluate_conditions |
$result, $tree, $state, $scope, $scope_id |
After a condition tree is evaluated. |
These are notification hooks. They cannot change the automation or its result.
Throwing from a before_ hook stops the automation and records a diagnostic. Throwing from an after_ hook is caught and recorded, and the run continues. Keep the work inside them small, since they execute during the visitor’s request and count against the runtime deadline.
<?php
add_action( 'elzo_forms_pro_automation_after_action', function ( $automation, $action_payload, $context, $action_result ) {
if ( $action_result->get_status() !== 'failed' ) {
return;
}
error_log( sprintf(
'Elzo Forms automation action failed: %s (%s)',
$action_payload['type'] ?? '',
$action_result->get_error()['code'] ?? ''
) );
}, 10, 4 );
Runtime and security filters
| Hook | Type | Arguments | Use it to |
|---|---|---|---|
elzo_forms_pro_automation_runtime_limits |
Filter | $defaults, $form, $source |
Adjust runtime limits. Values above the hard ceilings are clamped. |
elzo_forms_pro_automation_http_allowed_ports |
Filter | $ports |
Allow ports other than 80 and 443 for outgoing requests. |
elzo_forms_pro_automation_http_allow_cross_origin_redirect |
Filter | $allowed, $source_origin, $target_origin, $source |
Permit a redirect that leaves the original origin. Defaults to false. |
elzo_forms_pro_automation_trace_sensitive_keys |
Filter | $additional_keys, $default_keys |
Add keys that must be redacted from execution traces. |
elzo_forms_pro_automation_diagnostic_catalog_entries |
Filter | $entries |
Add titles, messages, and recommendations for custom error codes. |
See Webhook Integrations for Developers for the values these limits take.
Advanced admin hooks
These hooks are mostly useful for admin integrations, custom dashboards, or site-specific tooling.
| Hook | Type | Use it to |
|---|---|---|
elzo_forms_settings_sanitization_rules |
Filter | Modify sanitization rules for an option. |
elzo_forms_settings_sanitization_rules_{$option_name} |
Filter | Modify sanitization rules for one specific option. |
elzo_forms_settings_default_sanitization_rule |
Filter | Change the fallback sanitization rule. |
elzo_forms_get_update_metadata |
Filter | Provide update metadata in custom update flows. |
elzo_forms_logic_label_max_length |
Filter | Change the maximum displayed logic label length in the admin UI. |
elzo_forms_custom_form_label |
Filter | Display a label for custom or non-numeric form references. |
elzo_forms_custom_form_edit_url |
Filter | Display an edit URL for custom or non-numeric form references. |
elzo_forms_admin_submission_field |
Filter | Modify fields shown in the admin submission view. |
elzo_forms_admin_field |
Filter | Modify admin field data before rendering in the builder. |
elzo_forms_admin_field_tabs |
Filter | Add or modify field settings tabs in the admin UI. |
elzo_forms_alert_types |
Filter | Add or modify available alert types. |
Security guidelines
When using Elzo Forms hooks, follow standard WordPress security practices:
- Sanitize all data coming from
$_POST,$_GET,$_REQUEST, cookies, headers, and external APIs. - Escape all output with the correct escaping function, such as
esc_html(),esc_attr(), oresc_url(). - Use
current_user_can()before changing admin-only data or privileged settings. - Do not store API keys, passwords, access tokens, or secrets inside public form data.
- Do not remove dangerous upload extensions unless you fully control the upload environment.
- Use
WP_Errorfor validation failures that should be shown to the user. - Keep custom automation logs free of sensitive data.
Choosing the right hook
| Goal | Recommended hook |
|---|---|
| Block a submission with a custom error message | elzo_forms_validate_submission |
| Mark a submission as spam | elzo_forms_detect_spam |
| Run logic after a valid submission is saved | elzo_forms_after_submission_saved |
| Change notification recipients dynamically | elzo_forms_submission_email_settings |
| Change the frontend success response | elzo_forms_submission_response |
| Add a custom field type | elzo_forms_field_types |
| Override template arguments | elzo_forms/templates/template_args |
| Register a custom module | elzo_forms/modules/register |
| Register a custom PRO automation action or condition operator | elzo_forms_pro_register_automation_extensions |
Next steps
- Build a Custom Field — a worked example using the field filters.
- Template Overrides — the template hooks in context.
- JavaScript Hooks and Events API — the front-end counterpart to these hooks.
- Webhook Integrations for Developers — automation runtime limits and endpoint guidance.