Skip to main content

Troubleshooting: TURNSTILE_REQUIRED (422) on public form endpoints

Two public, unauthenticated endpoints carry the Cloudflare Turnstile bot-protection gate — the marketing-lead capture used by Devotel-property contact forms, and the self-service data-subject-request portal (POST /compliance/public/dsar/begin). When the gate is armed and the submitted token fails, the request is rejected at the bot-protection step — before rate limits, before payload validation completes — with:
The 422 is fail-closed by design: a request without a challenge the platform can verify reads as a bot submission, and the gate rejects it rather than let it in unchallenged. An absent, expired, or forged token never succeeds while the gate is armed.
error.details.error_codes carries the raw codes Cloudflare returned from siteverify when it available — the most common entries are missing-input-response (no token arrived at all), invalid-input-response (token already used, expired, malformed, or from the wrong site), and timeout-or-duplicate (the token was presented before, or too late). The field is omitted when the verification itself was unreachable, in which case the rejection is an availability fault on the platform side, not a visitor error.

Cause table

Fix it at the form

The gate is armed server-side; the fix is always on the form side:
  1. Render the widget. Add the Cloudflare Turnstile script and render the widget with the public site key in your form markup. The widget solves the challenge and hands you a token via its callback.
  2. Attach the token to the submission. For the marketing-lead capture endpoint this is the turnstile_token body field; on a raw HTML form it is the cf-turnstile-response field. Whichever name, it is the same token.
  3. Reset after every attempt. Reset the widget after each submission — success included — so every attempt carries a fresh token. A reused token is the single most common cause of this 422.

Retry posture

Retry only after solving a fresh token — never blind-retry. A 422 TURNSTILE_REQUIRED is a deterministic gate: replaying the identical body rejects identically, and the rate limit behind the gate makes a tight retry loop expensive. Re-render or reset the widget, get a new token, and submit once. If a fresh token still rejects with a site-key mismatch or with no error_codes in the response, work the cause table above before trying again.

What NOT to do

  • Do not retry with the same token. Single-use means single-use; a replayed token is the most rejected class and always fails.
  • Do not cache tokens client-side or server-side “to save solves.” A cached token is already dead by the time you use it.
  • Do not strip the field to see if the gate is off. The gate is fail-closed — an absent token rejects identically to a bad one. The absence test tells you nothing.
  • Do not open the form wider to route around the gate. A public endpoint with no abuse defense fills your lead pipeline with synthetic submissions; the gate exists because it is the cheaper failure of the two.

Escalation bundle

Work the cause table first — the four causes above account for the rejection almost always. If a fresh, in-window token with the correct site-key pairing still rejects, open a ticket with:
  • The request id from the response envelope’s meta.request_id.
  • The public site key the widget was rendered with (the public key, never the secret).
  • The full error envelope, including error.details.error_codes.
  • The endpoint you hit (marketing-lead capture, or the DSA-request portal’s begin) and one confirmation that a fresh token was used.

See also