Forms Auto Submit

Submit exact pre-filled form answers from a short-lived, server-signed link.

Forms Auto Submit adds a second, intentionally narrow way into Forms: opening a signed link submits the exact answers embedded in that link. It is useful for one-click actions such as Yes, I'll attend or Confirm my booking. The accepted entry and its background deliveries use the normal Forms pipeline.

This feature requires a server-side integration

The form editor does not generate a generic mail-merge URL. A template such as ?email=[EMAIL] cannot be signed safely because changing the placeholder after signing would also change the authorized submission. Your application or mailing system must resolve every answer first, then ask WordPress to build one concrete signed URL for that recipient.

Turn it on

Edit the form, open the Auto Submit panel in the document sidebar and switch Enable on.

Update the form. A copied form starts with Auto Submit disabled.

Have a server-side integration call XPACGroup\Plugin\FormsAutoSubmit\Addon::buildSignedUrl() after it knows the exact values for one recipient. Send the returned URL unchanged.

For example:

use XPACGroup\Plugin\FormsAutoSubmit\Addon;

$url = Addon::buildSignedUrl(
	128,
	[
		'email' => 'jane@example.com',
		'rsvp'  => 'yes',
	],
	30 * MINUTE_IN_SECONDS
);

if (is_wp_error($url)) {
	// Do not send a link; log or handle $url->get_error_code().
}

The third argument is an optional lifetime in seconds. When omitted, links last one hour. Lifetimes are clamped to a minimum of five minutes and a maximum of seven days. The xpac_forms_autosubmit_link_ttl filter can change the default inside the same bounds.

buildSignedUrl() returns string|WP_Error. It refuses an unavailable form, unknown field names, nested or associative values, File Upload, Consent and Password values, and URLs longer than 8,192 bytes. Values may be strings/scalars or flat indexed lists for multi-value fields. The builder percent-encodes them; do not edit or append query arguments afterwards.

A pretty-permalink link resembles:

https://example.com/submit_form/128/?email=jane%40example.com&rsvp=yes&xpac_as_id=…&xpac_as_exp=…&xpac_as=…

Plain-permalink sites use /?xpac_forms_action=autosubmit&form_id=128 instead. The xpac_as_exp argument is the required Unix expiry, xpac_as_id is a random 20-character request instance, and xpac_as is an HMAC covering the form id, instance, expiry and canonical exact answer values. Altering, adding, removing or reordering a multi-value answer invalidates the link. Duplicate keys, scalar/array ambiguity and nested query values fail closed before PHP's query parser can hide them.

Answers are still visible in the URL. They can appear in email-security reports, browser history, proxy/CDN and server logs, analytics and referrer data. Prefer opaque identifiers and never put passwords, secrets, consent or file contents in a link.

What happens when it opens

The route accepts only GET, a published and accessible form, an exact valid signature and a non-expired bounded expiry. Forms' per-IP/per-form throttle then runs. Normal validation, entry storage, idempotency and background delivery queuing follow. The instance marker, safe retry payload and named delivery manifest are committed atomically with the entry. Replaying the same URL reports that it was already received and resumes an interrupted named-delivery discovery/scheduling phase without creating another entry or duplicate job. A completed irreversible-mutation recovery also skips the link's throttle and pre-submit extension hook because those phases already accepted. Google reCAPTCHA and Cloudflare Turnstile cannot run on this link-only path; Akismet and the honeypot still run.

On a validation failure the visitor sees a short summary. A disabled link returns HTTP 410 with a clear unavailable message and creates no entry or delivery. A configured safe redirect is followed; an empty redirect is not converted into a silent trip to the home URL.

Opening the link is the write

The link is single-effect, not proof that its intended recipient opened it. A mail scanner, chat preview or browser prefetcher can consume it first; the recipient then sees “already received.” Do not use Auto Submit for payments, stock, scarce reservations or another action where the identity or intent of the first opener matters.

File, Consent and write-only fields are not supported

File, Consent, Password and Confirmation fields are refused even when optional. A legacy text field mapped to a UserAuth password is refused as well. A required one therefore makes the form unsuitable for Auto Submit. Use the ordinary form when the visitor must upload, actively consent or enter or repeat a secret.

Lower-level signing API

An integration that must assemble the URL itself can call:

$signature = Addon::signLink($formId, $exactValues, $expiry, $instanceId);

It returns string|WP_Error and applies the same form, value, instance and expiry checks. $instanceId must be a newly generated 20-character lowercase alphanumeric value and the URL must carry it unchanged in xpac_as_id, alongside the exact values, positive expiry in xpac_as_exp, and signature in xpac_as. buildSignedUrl() is safer because it generates the instance and cannot accidentally encode a different value shape.

Historical signatures covering only form_id|expiry and never-expiring placeholder links are rejected by default. A site may temporarily opt into that unsafe migration path with xpac_forms_autosubmit_allow_legacy_form_signature, but doing so makes every answer mutable again, permits expiry zero and lacks the signed instance that guarantees one effect. Reissue concrete links instead. The old xpac_forms_autosubmit_require_signature bypass is no longer honored.

Settings remain in Forms' form_settings post meta under { autosubmit: { enable: boolean } }. xpac_forms_autosubmit_unsafe_fields can add to the refused field-name list, and xpac_forms_autosubmit_pre_submit fires after all route gates and before Submission::submit(). The shared xpac_forms_submit_response filter receives the same normalized response keys as the REST submit route; malformed or throwing callbacks cannot turn a durably stored entry into a visible failure.

On this page