wpforms_process_complete: Hook Reference & Code Examples
Full parameter reference for the wpforms_process_complete action hook -- $fields, $form_data, $entry_id with copy-ready code examples and workflow patterns.
wpforms_process_complete Hook Reference: Parameters, Examples & Workflow Patterns
The wpforms_process_complete hook is where most custom WPForms integrations begin — yet the official docs provide only a stub reference with no data structures or working examples. This wpforms_process_complete hook guide covers the full parameter reference, three production-ready workflow patterns, and debugging techniques.
What wpforms_process_complete Does (and When It Fires)
wpforms_process_complete is a WordPress action hook that fires at the end of successful form processing. By the time your callback runs, WPForms has already validated fields, saved the entry, and dispatched built-in notifications.
The hook does not fire when:
- Field validation fails
- reCAPTCHA or hCaptcha verification fails
- The anti-spam check fails (token validation or honeypot)
- The form ID is not found
A form-specific variant — wpforms_process_complete_{$form_id} — fires immediately after the global hook. Use it to scope your callback to a single form without conditional checks.
wpforms_process_complete in the Hook Firing Sequence
Understanding where wpforms_process_complete sits in the pipeline helps you choose the right hook:
wpforms_process_before_form_data— Filter (pre-processing data normalization)wpforms_process_before— Action (pre-validation)wpforms_process_validate_{$type}— Action (per-field validation)wpforms_process_format_{$type}— Action (per-field formatting)wpforms_process_filter— Filter (post-validation)wpforms_process— Action (main processing; can halt submission)wpforms_process_entry_save— Action (after database save) Built-in email notifications dispatched between steps 7 and 8wpforms_process_complete— Action (your hook fires here)wpforms_process_redirect— Action (before redirect)
Key distinction: wpforms_process (step 6) can halt submission and runs before the entry is saved. wpforms_process_complete (step 8) runs after everything succeeds — use it for post-submission side effects like API calls, custom post creation, or notification routing.
Complete Parameter Reference
The callback signature accepts four parameters:
add_action( 'wpforms_process_complete_5', 'my_callback', 10, 4 );
function my_callback( $fields, $entry, $form_data, $entry_id ) {
// Your logic here.
}
| Parameter | Type | Contains | Key Gotcha |
|---|---|---|---|
$fields | array | Sanitized field values, keyed by field ID | Keys may vary for specialty fields (see note below) |
$entry | array | Raw $_POST superglobal | Not a WPForms entry object — use $fields for field values |
$form_data | array | Form configuration: id, fields, settings, notifications, confirmations | Read-only snapshot of the form builder config |
$entry_id | int | Database entry ID | Returns 0 in WPForms Lite or when entry storage is disabled |
The $fields Array Structure
Each element in $fields is keyed by the field ID and contains these standard keys:
$fields[1] = array(
'id' => 1,
'type' => 'email',
'name' => 'Email Address',
'value' => '[email protected]',
'value_raw' => '[email protected]',
);
The keys id, type, name, value, and value_raw are standard across common field types (text, email, name, textarea, select). Specialty fields such as checkboxes, file uploads, and payment fields may include additional or different keys. Inspect the structure with error_log( print_r( $fields, true ) ); when working with non-standard fields.
Usage Examples
All three patterns below use the form-specific variant (wpforms_process_complete_{$form_id}) and are ready to add to your theme’s functions.php or a code snippets plugin.
1. Create a Custom Post from Submission
Converts form submissions into testimonial posts for moderation. Pairs well with form-to-database workflow patterns.
add_action( 'wpforms_process_complete_5', 'create_testimonial_post', 10, 4 );
function create_testimonial_post( $fields, $entry, $form_data, $entry_id ) {
wp_insert_post( array(
'post_title' => sanitize_text_field( $fields[1]['value'] ),
'post_content' => sanitize_textarea_field( $fields[3]['value'] ),
'post_status' => 'pending',
'post_type' => 'testimonial',
'meta_input' => array(
'submitter_email' => sanitize_email( $fields[2]['value'] ),
'wpforms_entry' => $entry_id,
),
) );
}
2. Route Notifications by Department
Use dropdown field values to send targeted emails. For advanced routing, see self-hosted CRM integration patterns.
add_action( 'wpforms_process_complete_12', 'route_department_email', 10, 4 );
function route_department_email( $fields, $entry, $form_data, $entry_id ) {
$department = sanitize_text_field( $fields[4]['value'] );
$recipients = array(
'Sales' => '[email protected]',
'Support' => '[email protected]',
);
if ( ! isset( $recipients[ $department ] ) ) {
return; // Unknown department -- skip.
}
wp_mail(
$recipients[ $department ],
sprintf( 'New %s inquiry from %s', $department, $fields[1]['value'] ),
sprintf( "Name: %s\nEmail: %s\nMessage: %s",
$fields[1]['value'], $fields[2]['value'], $fields[5]['value']
)
);
}
3. Dispatch to an External API
Sends form data to an external service using wp_safe_remote_post(). The API key is stored in wp_options via get_option() — never hardcode credentials. For outbound patterns, see native WordPress webhook dispatch.
add_action( 'wpforms_process_complete_8', 'sync_to_external_api', 10, 4 );
function sync_to_external_api( $fields, $entry, $form_data, $entry_id ) {
$api_key = get_option( 'my_service_api_key' );
if ( empty( $api_key ) ) {
error_log( 'WPForms sync: API key not configured.' );
return;
}
$response = wp_safe_remote_post( 'https://api.example.com/leads', array(
'body' => wp_json_encode( array(
'name' => $fields[1]['value'],
'email' => $fields[2]['value'],
'form_id' => $form_data['id'],
'entry_id' => $entry_id,
) ),
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
),
'timeout' => 15,
) );
if ( is_wp_error( $response ) ) {
error_log( 'WPForms sync failed: ' . $response->get_error_message() );
}
}
For long-running operations that risk timeout, offload work to Action Scheduler background processing patterns rather than running synchronously inside the callback. For input handling techniques, review WordPress data validation and sanitization best practices.
Debugging wpforms_process_complete: Common Failures
Hook not firing? Walk through this checklist:
- Validation errors — The hook only fires on successful submissions. Check your error log for validation messages.
- Wrong form ID — If using
wpforms_process_complete_{$form_id}, confirm the ID matches (WPForms > All Forms). - Missing parameter count — Pass
4as the final argument toadd_action(). Omitting it means your callback only receives$fields. - Silent failures — Add
error_log( 'Hook fired for form ' . $form_data['id'] );as the first line to confirm execution.
Redirect anti-pattern: wp_redirect() is unreliable inside this hook. With AJAX enabled (the default), the response is JSON-based and server-side redirects have no effect. Even with AJAX disabled, headers may already be sent. Use the wpforms_process_redirect_url filter instead.
FAQ
What parameters does wpforms_process_complete pass?
Four parameters: $fields (array of sanitized field values, keyed by field ID), $entry (raw $_POST superglobal — not a WPForms object), $form_data (the complete form configuration array including settings, notifications, and confirmations), and $entry_id (integer database entry ID, or 0 when entry storage is disabled or using WPForms Lite).
How is wpforms_process_complete different from wpforms_process?
wpforms_process fires during processing (step 6) and can halt submission. wpforms_process_complete fires after everything succeeds (step 8) and cannot halt — use it for post-submission side effects.
Does the hook fire if entry storage is disabled?
Yes. The hook fires regardless of entry storage settings. All four parameters are still passed to your callback, but $entry_id will be 0 rather than a valid database integer. Test for this with if ( $entry_id > 0 ) before running any logic that depends on the entry record.
Why is my callback not receiving all four parameters?
You likely omitted the parameter count. Use add_action( 'wpforms_process_complete', 'callback', 10, 4 ); — the 4 is required.
Can I use echo or var_dump inside this hook?
Not reliably. WPForms processes submissions via AJAX by default, so browser output is suppressed. Use error_log() to inspect values during development.
Building custom WordPress automation workflows? Subscribe to the Summix newsletter for developer-focused tutorials on hooks, integrations, and workflow patterns delivered weekly.