Webhooks
Send each XPAC Forms submission to any URL as JSON or form data.
Webhooks adds a Webhooks panel to each form in Forms. Each webhook sends the submission to a URL once Forms has saved it. Webhooks are sent in the background, so a slow receiver never slows the form.
It needs XPAC Forms. Without it, admins see a notice asking for it.
Add it to a form
There is no account to connect. Open the form's Webhooks panel, click Add webhook and paste the URL the receiving service gives you.
- Title: shown in the list and in delivery errors. An untitled webhook is named by its host.
- Request URL: starts with
https://orhttp://. A token in the query string works. A login in the URL doesn't: put it in an Authorization header instead. - Method: POST, GET, PUT, PATCH or DELETE. GET sends the fields in the URL, keys exactly as typed. Servers often log URLs, so use POST for personal data.
- Content type: JSON or Form (URL-encoded).
- Request headers: each has a Name (Authorization, Accept, User-Agent, X-API-Key or a custom one), a Source (Form field or Fixed value) and its value.
- Fields to send: All fields sends every field under its Name. Blank or hidden fields are sent empty (a file field as an empty list), so the keys never change. Selected fields sends only your rows, each with a Key, a Source and a value. Blank answers are left out.
- Forward public file URLs: sends a public link for each uploaded file. Off by default. Links never expire. A file kept private by Keep uploaded files private can't be sent, and the delivery fails.
- Include submission metadata: adds
_xpac, shown below. - Include the visitor's IP address: shows once metadata is on. Off for new webhooks. Webhooks saved before it existed keep sending it.
Password and confirmation fields are never sent. Only people who can view form submissions can send webhooks to a new URL.
{
"_xpac": {
"form_id": 42,
"entry_id": 7301,
"site_url": "https://example.com/",
"delivery_id": "9f2c41d07b6e4a8d93e1c5f0a2b7d864",
"timestamp": "2026-09-28T10:15:00+00:00",
"ip_address": "",
"user_agent": "Mozilla/5.0 …"
}
}timestamp is when the submission came in, in UTC. delivery_id stays the same on
every retry and differs for each webhook. Every request also sends it in an
Idempotency-Key header, so a receiver that must not act twice can skip one it has
already seen.
A few safety rules apply:
- A fixed header value, such as a token, is stored with the form. Anyone who can edit the form can read it, and it is included in a form export. Use a token you can revoke.
- If the URL redirects to another host, your headers aren't sent there. Use the final URL.
- Addresses on this server or a private network, and ports other than 80, 443 and 8080, are refused.
When a send fails
All webhooks on a form share one Webhooks delivery. Each result is on XPAC → Forms → Deliveries and in the submission's own Deliveries row:
- Waiting: queued, or waiting for its next try.
- Delivered: every webhook that applied was accepted.
- Failed: not sent. The error names each webhook that failed and says why.
- Skipped: nothing was sent, and the error says why.
- Dismissed: you dismissed it, or the submission is in spam or the trash.
The receiver has 10 seconds to reply, and only a 2xx reply counts. Timeouts, rate limits and server errors are tried again after 1, 5, 30 and 120 minutes, five tries in all. Other errors fail at once. Each webhook that went through is marked done, so a retry sends only the ones that failed. When one fails for good while another went through, the delivery is Failed and doesn't count toward a pause.
Retry now sends a failed or dismissed delivery again with the form's current settings. So fix the webhook, update the form, then retry. New submissions use the fix too. Retry works for 30 days after the submission.
After 5 failed submissions in a row on one form, every webhook on that form pauses. Rate limits and errors about one submission's data don't count. While paused, new submissions are saved but show as Failed, XPAC still tries one an hour, and the first one that goes through ends the pause. The site admin gets one email when it pauses, and Deliveries shows a Resume and retry button that resumes and sends the failed submissions again.
For developers
xpac_forms_webhooks_field_value: filters each answered field. File fields arrive as arrays of URLs.xpac_forms_webhooks_request_body: filters the whole body. Return aWP_Errorto fail without sending.xpac_webhooks_signing_secret: return a secret to sign each request withX-XPAC-TimestampandX-XPAC-Signature, which issha256=and the HMAC-SHA256 of the timestamp, a dot and the raw body. A GET signs the timestamp and a dot.xpac_webhooks_request_timeout: seconds to wait. Default 10, from 1 to 30.xpac_webhooks_request_redirection: redirects to follow. Default 5, from 0 to 10.xpac_webhooks_send_unanswered_fields: return false to leave blank fields out of All fields.xpac_webhooks_send_ip_address: return false to send the IP address empty.xpac_webhooks_allow_unsafe_url: return true to send to this server or a private network. It turns off WordPress's protection for that request.xpac_webhooks_response_log_entries: replies kept per webhook on the submission. Default 0, so none, up to 20.xpac_webhooks_response(action): runs after each reply, with the reply, the URL and the submission.
Signatures are in the Webhooks hook reference.