These hooks cover the Forms module (app/Modules/Forms/) added in Notifal 3.0.0. Use them to validate, block, mutate, or react to public form submissions without editing core services.
Index: Hook Reference Hub.
User guides: Notifal Forms Overview.
Prefer PHP constants from Notifal\Infrastructure\WordPress\Hooks\ActionHooks and FilterHooks. Scoped hooks are fired through FormsHookDispatcher with sprintf().
Conventions
| Topic | Detail |
|---|---|
| Global hooks | Run for every form (notifal/forms/...) |
| Form-scoped hooks | Append CPT ID: sprintf(FilterHooks::FORMS_SUBMIT_FIELDS_FORM, $form_id) → notifal/forms/submit/fields/42 |
| Field-scoped hooks | Append field machine name, or form ID + field name |
| Mutating data | Use filters. Actions (FORMS_SUBMIT_BEFORE) are for side effects only |
| Secrets | Never add webhook keys, Mailchimp secrets, or tokens to FORMS_SUBMIT_RESPONSE |
| Security gates | Core spam and integrity checks run before extension hooks. Do not rely on hooks to replace those checks |
Submit and validation pipeline (order)
After core security checks succeed, extension hooks run in this order:
FORMS_SUBMIT_ALLOW(+ form-scoped)FORMS_SUBMIT_FIELDS(+ form-scoped)FORMS_SUBMIT_BEFORE(+ form-scoped)FORMS_VALIDATE_BEFORE(+ form-scoped)- Per field:
FORMS_VALIDATE_FIELD(+ field / form+field scoped) FORMS_VALIDATE_RESULT(+ form-scoped)FORMS_VALIDATE_AFTER(+ form-scoped)- Unique-value check (
FORMS_UNIQUE_VALUE_DUPLICATE_MESSAGEon collision) FORMS_SUBMISSION_DATA(+ form-scoped) then DB insert- Emails and integrations
FORMS_SUBMIT_AFTER(+ form-scoped)FORMS_SUBMIT_RESPONSE(+ form-scoped)
On failure after a verified request: FORMS_SUBMIT_ERROR (+ form-scoped) and FORMS_SUBMIT_FAILED (+ form-scoped).
Submit filters
| Hook | Parameters | Description | |
|---|---|---|---|
notifal/forms/submit/allow | `bool\ | array $allowed, array $schema, array $fields` | Return true to continue. Return false or ['message' => '...', 'code' => '...', 'field' => '...'] to block |
notifal/forms/submit/allow/%d | same | Form CPT scoped | |
notifal/forms/submit/fields | array $fields, array $schema | Mutate raw submitted fields before validation | |
notifal/forms/submit/fields/%d | same | Form CPT scoped | |
notifal/forms/submission/data | array $data, array $schema, array $fields | Submission row before DB insert | |
notifal/forms/submission/data/%d | same | Form CPT scoped | |
notifal/forms/submit_response | array $response, int $submission_id | Public AJAX success payload (no secrets) | |
notifal/forms/submit_response/%d | same | Form CPT scoped | |
notifal/forms/submit/error | array $error, array $schema, array $context | Public AJAX error payload | |
notifal/forms/submit/error/%d | same | Form CPT scoped | |
notifal/forms/unique_value_duplicate_message | string $message, string $field_name, string $label, int $form_id, array $field | Duplicate unique-value message |
Submit and validation actions
| Hook | Parameters | Description |
|---|---|---|
notifal/forms/submit/before | array $schema, array $fields | Side effects before validation |
notifal/forms/submit/before/%d | same | Form CPT scoped |
notifal/forms/validate/before | array $schema, array $fields | Before the validation field loop |
notifal/forms/validate/before/%d | same | Form CPT scoped |
notifal/forms/validate/after | array $result, array $schema, array $fields | After validation completes |
notifal/forms/validate/after/%d | same | Form CPT scoped |
notifal/forms/submit/after | int $submission_id, array $schema, array $fields | After save, emails, and integrations |
notifal/forms/submit/after/%d | same | Form CPT scoped |
notifal/forms/submit/failed | array $error, array $schema, array $context | Submit failed after a verified request |
notifal/forms/submit/failed/%d | same | Form CPT scoped |
Validation filters
| Hook | Parameters | Description |
|---|---|---|
notifal/forms/validate/field | string[] $field_errors, array $field, mixed $value, array $input, array $schema | Per-field error slugs (required, type, regex, or custom). Non-empty list fails the field |
notifal/forms/validate/field/%s | same | Field machine name scoped |
notifal/forms/validate/field/%d/%s | same | Form CPT + field machine name scoped |
notifal/forms/validate/result | array $result, array $schema, array $input | Full result: valid, fields, errors |
notifal/forms/validate/result/%d | same | Form CPT scoped |
Schema, fields, emails, integrations
| Hook | Type | Description |
|---|---|---|
notifal/forms/schema_parsed | filter | Parsed schema DTO after HTML parse ($schema, $html) |
notifal/forms/field_definitions | filter | Sanitized field definitions before CPT persistence |
notifal/forms/fields/synced | action | After fields sync ($form_id, $fields, $source) |
notifal/forms/email_system_variables | filter | System merge tags in email UI |
notifal/forms/email_field_variables | filter | Field merge tags in email UI |
notifal/forms/email_context | filter | Merge tag values before rendering ($context, $form_id, $submission_id, $fields) |
notifal/forms/email_template_rendered | filter | Subject/body after merge tag replacement |
notifal/forms/email_admin_default_body | filter | Default admin body ($template, $format) |
notifal/forms/email_visitor_default_body | filter | Default visitor body ($template, $format) |
notifal/forms/integration_adapters | filter | Adapter class map (webhook, mailchimp, custom) |
notifal/forms/integrations/dispatched | action | After all adapters run ($form_id, $submission_id, $fields) |
notifal/forms/services | filter | Forms module service class list |
Admin lifecycle actions
| Hook | Parameters |
|---|---|
notifal/forms/form/saved | int $form_id, array $data |
notifal/forms/form/duplicated | int $source_id, int $new_id |
notifal/forms/form/trashed | int $form_id, string $status |
notifal/forms/form/deleted | int $form_id, string $status |
notifal/forms/trash/emptied | int $deleted_count |
notifal/forms/submission/status_updated | int $submission_id, string $status |
notifal/forms/submission/note_updated | int $submission_id, string $note |
notifal/forms/submission/deleted | int $submission_id |
notifal/forms/submissions/bulk_marked_read | int[] $ids, int $updated |
notifal/forms/submissions/bulk_deleted | int[] $ids, int $deleted |
Admin list filters: notifal/forms/submission_list/columns, notifal/forms/submission_list/bulk_actions.
Frontend JavaScript events
Events bubble from the form root. Each fires twice: notifal:form:{name} and notifal:form:{name}:{formId}.
| Event | Cancelable | When | Detail |
|---|---|---|---|
init | no | After form is bound | { formId } |
validate | yes | Before submit / next step | { valid, fields } (set detail.valid = false or preventDefault()) |
before-submit | yes | Before AJAX starts | { fields } (mutable) |
submit | no | When AJAX request starts | { fields } |
success | no | After successful AJAX | { message, redirect, behavior } |
error | no | Client or server failure | { message, code, field } |
step-change | no | Multi-step navigation | { from, to } |
OnPage notifications listen for notifal:form:success for close-on-submit behavior.
What to read next
Hooks series
- Hook Reference Hub
- Hooks: Forms (you are here)
- Hooks: Templates and Rendering
- Hooks: Pro Integration Bridge