Webhooks

Queue selected form answers for delivery to an HTTP endpoint.

Webhooks is an add-on for Forms. It adds a Webhooks panel to each form. Every enabled item sends one JSON or URL-encoded request to the endpoint you configure.

The submission is stored before any request begins. Delivery runs in the background and its result appears under the submission's Deliveries tab, so the visitor does not wait for the remote service.

Add a webhook

Edit a form, open Webhooks, and choose Add webhook.

Give it an administrative title and an http:// or https:// endpoint. Tokenised URLs with query strings are supported.

Choose POST, GET, PUT, PATCH or DELETE and JSON or URL Encoded. GET always uses query parameters.

Add headers if the receiver requires them. For every value, choose whether its source is a Form field or a Fixed value.

Choose All Fields or Select Fields. A selected row has an outgoing key, an explicit source type, and either a form field or fixed value.

Save the modal, then update the form. Submit a signed-out test and confirm the delivery and received body before going live.

Payload safety

Password and confirmation controls are write-only. They are never included by All Fields, are absent from editor choices, and an explicitly written mapping to either type fails without making a request.

UserAuth also marks the source fields mapped as a new password or current-password proof as sensitive. Webhooks removes those values at the server boundary even if the source happens to be an ordinary text control in a legacy or tampered form.

Legacy rows did not say whether their value was a field name or fixed text. A row which still names a real field continues to work. Any other legacy value could be a renamed field or a literal, so delivery fails visibly until you open the webhook, choose its source, and save. An explicit field mapping also fails if that field is later removed or renamed; it never turns into a constant silently.

Files are opt-in

Forward public file URLs is off by default, including with All Fields. When enabled, each submitted attachment is converted to a URL the receiver must fetch. Every attachment must resolve or the whole delivery fails; the plugin never reports success after silently dropping one file.

A forwarded public file URL does not expire

Anyone who receives the URL can open it while the file remains public. Do not enable this for material you would not send to every operator of the remote service.

Forms uploads stored privately cannot be forwarded as webhook links: their route requires a signed-in submissions administrator. If an opted-in field contains a private upload, delivery fails with a clear message instead of sending that unusable authenticated URL.

Optional delivery identity

Include submission metadata adds this stable object under _xpac:

{
	"form_id": 42,
	"entry_id": 7301,
	"site_url": "https://example.com/"
}

The receiver can use the site and entry IDs as a deduplication key. _xpac is reserved while this option is enabled.

Headers and credentials

Header field sources accept ordinary non-file answers. Password, confirmation and file fields are refused; put opted-in file URLs in the body. Submitted header values have line breaks/tags removed and are capped at 1,024 characters. Literal values keep their exact credential bytes except CR, LF and NUL, which are removed to prevent header injection. Invalid header names fail the delivery.

Tokens stored as fixed header values live in the form's form_settings meta and travel in a form export. Anyone allowed to edit the form can read or replace them. Use a revocable, endpoint-specific token and rotate it if an export is disclosed. While a delivery can still retry, Forms also holds the accepted form settings in an authenticated-encrypted entry-meta snapshot; the fixed token is not written as readable retry metadata.

URL and request behavior

The endpoint must have an explicit HTTP or HTTPS scheme and a host. Credentials in user:pass@host form are refused; put them in a header. Literal loopback, link-local, private and reserved addresses are refused. At send time WordPress also resolves the hostname and applies its safe-URL and safe-port checks, which protects against a public name resolving to an internal address.

xpac_webhooks_allow_unsafe_url can bypass those protections for a deliberately internal integration. Treat that filter as privileged server configuration and allow only exact destinations.

GET puts the payload in the URL. Proxies and access logs commonly retain query strings, so use POST for personal or secret data.

Delivery, failures and retries

Each request has a 10-second timeout, bounded to 1–30 seconds by the timeout filter. HTTP 2xx is accepted. Configuration errors and HTTP 400, 401, 403, 404, 410 and 422 are permanent failures. Transport failures, rate limits and other statuses are retried by Forms, up to five attempts with waits of 1, 5, 30 and 120 minutes.

When several webhooks share one form, an accepted item receives its own durable completion marker. A sibling can retry without resending that accepted item. If a temporary and permanent endpoint fail together, the temporary one still gets its retry opportunity.

The endpoint, headers, mappings and trusted request-derived conditions are frozen when Forms accepts the submission. Editing or removing the webhook affects future submissions only; an old retry never redirects saved answers to the replacement. If the accepted webhook configuration itself was wrong, abandon that delivery and submit a new test after correcting the form.

An ambiguous timeout can still produce a duplicate

There is no universal HTTP-webhook idempotency standard. The receiver may have accepted a request before its reply was lost, in which case a retry sends it again. Enable submission metadata and make the receiver deduplicate by site and entry ID when duplicate side effects matter.

Delivery history remains visible after the retry payload expires, but Forms keeps the answers needed for retry for only 30 days. Repair and retry before that privacy deadline.

Stored shape

Settings live below webhooks.items in the form's form_settings post meta. A complete item looks like this:

{
	"status": true,
	"title": "CRM",
	"request": {
		"url": "https://hooks.example.com/inbound?token=id",
		"method": "POST",
		"contentType": "json"
	},
	"headers": [
		{
			"name": "Authorization",
			"value": "Bearer …",
			"kind": "literal"
		}
	],
	"body": {
		"type": "selected",
		"includeFiles": false,
		"includeMetadata": true,
		"fields": [
			{
				"key": "email",
				"field": "email",
				"kind": "field"
			}
		]
	}
}

kind is field or literal. status: false disables the item. Conditional Logic may add its own condition property.

Filters

xpac_forms_webhooks_field_value receives one resolved submitted value, field type, all submitted data, the field-block map, source field name, outgoing key and Submission. File values reach it only after every attachment resolves to a public URL array.

xpac_forms_webhooks_request_body receives the complete array, body settings and Submission immediately before encoding. It must return an array or a WP_Error; an error becomes a visible permanent failure without sending.

xpac_webhooks_request_timeout changes the request timeout before the 1–30 second bound. xpac_webhooks_allow_unsafe_url controls the explicit safe-URL bypass.

See the generated Webhooks hook reference for signatures.

On this page