---
title: "Elementor Forms Webhook: Send Submissions Anywhere"
description: "Elementor forms webhook setup, two ways: the built-in Webhook action in Elementor Pro, and the elementor_pro/forms/new_record hook when you need real control."
url: "https://wpwebhooks.org/blog/elementor-forms-webhook/"
date: "2026-07-24"
---

# Elementor Forms Webhook: Send Submissions Anywhere

**TL;DR:** Elementor can send form submissions to a webhook two ways, and both need Elementor Pro — the Form widget is a Pro feature.

-   **No code:** add _Webhook_ under _Actions After Submit_ in the Form widget and paste your URL.
-   **With code:** hook `elementor_pro/forms/new_record`, which fires after the form actions have run and hands you the record.
-   The code path is what you need when the endpoint expects a specific payload shape or an authentication header.
-   Either way, a submission sent inline blocks the visitor's request and is lost if the endpoint is down.

/ Overview

## Can Elementor send form submissions to a **webhook**?

Yes — a Webhook action is built into the Form widget, no code required. It is one of the default [form actions](https://developers.elementor.com/docs/form-actions/) Elementor Pro ships, alongside Email, Redirect, Collect Submissions, Slack, Discord and a set of CRM integrations.

The prerequisite catches people out: the Form widget itself is part of Elementor Pro and is not available in the free plugin. If you are on free Elementor there is no Webhook action to configure, because there is no Elementor form to attach it to. Everything below assumes Pro.

FIG 01 — Two paths from an Elementor form to your endpoint

/ Built-in action

## Where is the built-in **Webhook action**?

In the Form widget's _Content_ tab, under _Actions After Submit_. Add _Webhook_ to the action list and a Webhook panel appears below, where you paste the destination URL. On submission Elementor posts the form data to that URL.

By default each field is sent using its label as the field name, which is convenient in a no-code tool and fragile everywhere else — renaming a label in the editor renames the key in your payload and quietly breaks the consumer. Give each field an explicit Custom ID in the field's _Advanced_ settings and use that as the stable key instead.

That is the whole configuration surface, and its limits are the reason the code path exists. You get one URL and Elementor's payload shape. There is no place to add an `Authorization` header, no way to rename or nest fields to match an API contract, no signature, and no visibility into whether the request succeeded.

/ The hook

## How does **elementor\_pro/forms/new\_record** work?

It is an action that fires _after_ the configured form actions have run, and it receives the submitted record plus the AJAX handler. [Elementor's developer documentation](https://developers.elementor.com/docs/hooks/forms/) gives the signature as two parameters:

| Parameter | Type | What it holds |
| --- | --- | --- |
| `$record` | `Form_Record` | The submitted form: fields, settings, uploaded files and meta. |
| `$ajax_handler` | `Ajax_Handler` | The response handler — used to add success or error messages back to the visitor. |

Because it runs after the actions, anything the built-in Webhook action was going to send has already gone out by the time your callback runs. Use `new_record` when you want to send your _own_ request; use it alongside the built-in action only if you genuinely want two deliveries.

Two related hooks are worth knowing. `elementor_pro/forms/validation` runs before processing and is where you reject a submission. `elementor_pro/forms/process` fires after fields are validated and processed. Both take the same `$record` and `$ajax_handler` pair, and both have per-field-type variants — `elementor_pro/forms/validation/{$field_type}` and `elementor_pro/forms/process/{$field_type}` — for targeting a single kind of field.

> elementor\_pro/forms/new\_record fires after the form actions have run — so it is a delivery hook, not a validation hook. — Elementor developer documentation

/ Implementation

## How do you **post a submission** to your own endpoint?

Read the form name to make sure you are handling the right form, pull the fields off the record, and send them. The form-name guard is not optional — without it your callback fires for every Elementor form on the site.

PHP — send one Elementor form to a custom endpoint

```
add_action( 'elementor_pro/forms/new_record', function( $record, $ajax_handler ) {

    // Only handle the form named "Contact" in the editor.
    if ( 'Contact' !== $record->get_form_settings( 'form_name' ) ) {
        return;
    }

    // Raw fields, keyed by each field's Custom ID.
    $raw = $record->get( 'fields' );

    $payload = [];
    foreach ( $raw as $id => $field ) {
        $payload[ $id ] = $field['value'];
    }

    wp_remote_post( 'https://example.com/hooks/elementor', [
        'timeout' => 5,
        'headers' => [
            'Content-Type'  => 'application/json',
            'Authorization' => 'Bearer ' . get_option( 'my_endpoint_token' ),
        ],
        'body'    => wp_json_encode( [
            'source'    => 'elementor',
            'form'      => $record->get_form_settings( 'form_name' ),
            'submitted' => gmdate( 'c' ),
            'fields'    => $payload,
        ] ),
    ] );

}, 10, 2 );
```

Each entry in `$record->get('fields')` is an array, not a bare string — `value` holds what the visitor typed, and `title` holds the field label. Reading `['value']` explicitly is what keeps the payload clean. Note the header block: this is the capability the built-in action does not have, and usually the reason people move to code in the first place.

Uploaded files arrive as URLs rather than binary content, so a file field's `value` is a link into the uploads directory. If your endpoint needs the file itself, fetch it from that URL — and remember the URL is public unless you have restricted upload access.

/ Reliability

## Why does a **synchronous request** hurt your form?

Because it runs inside the visitor's submission request. [`wp_remote_post()`](https://developer.wordpress.org/reference/functions/wp_remote_post/) is blocking: with the five-second timeout above, a slow endpoint adds five seconds to every submission before the visitor sees a confirmation. Drop the timeout and you cut off legitimate slow responses; raise it and a degraded endpoint stalls your form.

Worse is what happens on failure. The visitor's submission succeeded, Elementor showed a success message, and your delivery threw a connection error into the void. Nothing retries it. Nobody is told. This is the standard failure mode for form webhooks and it is [almost always silent](https://wpwebhooks.org/blog/why-wordpress-webhooks-silently-fail-in-production/).

| Concern | wp\_remote\_post inside the hook | Queued delivery |
| --- | --- | --- |
| Submission speed | Visitor waits for your endpoint | Returns immediately — job runs after |
| Endpoint down | Delivery lost, no record | Job stays queued and retries |
| Slow endpoint | Adds latency to every submission | No effect on the form |
| Visibility | Failure is invisible unless you log it | Per-attempt status is inspectable |
| Traffic spikes | Each submission opens its own request | Work is batched by the queue runner |

The fix is to enqueue rather than send. Capture the payload in the hook, hand it to a background job, and let a queue runner deliver it with retries and [exponential backoff](https://wpwebhooks.org/blog/webhook-retry-policy-exponential-backoff/). [Action Scheduler](https://actionscheduler.org/) is the usual choice on WordPress because it gives each delivery its own database row and retry state — the same pattern described in the [WordPress job queue guide](https://wpwebhooks.org/blog/wordpress-job-queue/).

/ Choosing

## Which approach should you **use**?

Start with the built-in action, and move to the hook when you hit one of its walls. Concretely:

1.  **Use the built-in Webhook action** when the destination accepts whatever you send it — an automation platform's catch-hook node, an internal script, a test endpoint. Set Custom IDs on your fields first.
2.  **Use `elementor_pro/forms/new_record`** when the endpoint needs an authentication header, a specific JSON shape, nested objects, or fields renamed to match an API contract.
3.  **Queue the delivery** as soon as the submission matters commercially. A lost contact form entry is an annoyance; a lost order or lead is revenue.

The same trade-off shows up in every form plugin — the built-in option is fastest to set up, the hook gives control, and neither gives you retries on its own. For the equivalent hooks in other form plugins, see the [gform\_after\_submission reference](https://wpwebhooks.org/blog/gravity-forms-gform-after-submission-webhook/) and the [wpcf7\_mail\_sent guide](https://wpwebhooks.org/blog/wordpress-wpcf7-mail-sent-hook/).

## Structured data

```json
{"@context":"https://schema.org","@type":"Article","headline":"Elementor Forms Webhook: Send Submissions Anywhere","description":"Elementor forms webhook setup, two ways: the built-in Webhook action in Elementor Pro, and the elementor_pro/forms/new_record hook when you need real control.","datePublished":"2026-07-24","dateModified":"2026-07-24","author":{"@type":"Person","name":"Mateusz Skorupa","url":"https://wpwebhooks.org/about/"},"publisher":{"@type":"Organization","name":"WP Webhooks","url":"https://wpwebhooks.org"},"url":"https://wpwebhooks.org/blog/elementor-forms-webhook/","image":{"@type":"ImageObject","url":"https://wpwebhooks.org/og_image.jpg","width":1200,"height":630,"caption":"Elementor Forms Webhook: Send Submissions Anywhere"},"keywords":["elementor forms webhook","elementor webhook","elementor pro forms","elementor form submission hook","elementor pro forms new record","elementor actions after submit","wordpress form webhook","elementor form api"]}

{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"WP Webhooks","item":"https://wpwebhooks.org/"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://wpwebhooks.org/blog/"},{"@type":"ListItem","position":3,"name":"Elementor Forms Webhook: Send Submissions Anywhere","item":"https://wpwebhooks.org/blog/elementor-forms-webhook/"}]}

{"@context":"https://schema.org","@type":"FAQPage","mainEntity":[{"@type":"Question","name":"Does Elementor have a webhook for forms?","acceptedAnswer":{"@type":"Answer","text":"Yes. The Form widget ships a Webhook action under Actions After Submit — add it in the Content tab and paste the destination URL. It requires Elementor Pro, because the Form widget itself is a Pro feature and is not available in free Elementor."}},{"@type":"Question","name":"What data does the Elementor webhook action send?","acceptedAnswer":{"@type":"Answer","text":"The submitted form fields, keyed by each field name. By default the field label is used as the key, which breaks your consumer whenever a label is edited, so set an explicit Custom ID on each field in its Advanced settings and rely on that instead."}},{"@type":"Question","name":"What is elementor_pro/forms/new_record?","acceptedAnswer":{"@type":"Answer","text":"It is the Elementor Pro action hook that fires after the form actions have run. It receives two parameters: a Form_Record object holding the fields, settings and uploaded files, and an Ajax_Handler used to add success or error messages back to the visitor."}},{"@type":"Question","name":"How do I send Elementor form data to a custom API with headers?","acceptedAnswer":{"@type":"Answer","text":"Use elementor_pro/forms/new_record rather than the built-in action. Guard on the form name via $record->get_form_settings('form_name'), read the fields with $record->get('fields'), build your own JSON body, and pass an Authorization header to wp_remote_post. The built-in action has no place to set headers."}},{"@type":"Question","name":"Why should Elementor webhook deliveries be queued?","acceptedAnswer":{"@type":"Answer","text":"Because wp_remote_post inside the hook is blocking and unretried: a slow endpoint adds its latency to every submission, and a failed request is lost silently while the visitor still sees a success message. Enqueueing the delivery lets a background worker retry with exponential backoff without affecting the form."}}]}
```
