Bot Protection
ResponseDock protects public surveys from automated submissions in two layers: heuristics that flag suspicious responses on every survey, and an optional invisible Cloudflare Turnstile challenge that blocks bots before a response starts.
Invisible Challenge (Cloudflare Turnstile)
Open Survey settings → Integrations & bot protection and set Bot protection to Invisible challenge (Cloudflare Turnstile). The default is Off.
When it is on, the survey runs an invisible Turnstile challenge before a response starts, and /responses/start only accepts the request with a valid Turnstile token, which the API verifies with Cloudflare. Respondents normally see nothing.
Only the web survey renderer can solve the challenge. Responses to a survey with bot protection turned on that come from the mobile app or directly through the API, without a token, are rejected. Leave bot protection off for surveys answered in the mobile app or submitted from your own backend.
Honeypot and Completion Time
Every survey, with or without Turnstile, also checks each completed response for two bot signals:
| Signal | Meaning |
|---|---|
honeypot | A hidden field that people never see was filled in. |
too_fast | The response was completed faster than a person could read and answer it. |
The minimum completion time is the smaller of 3 seconds and 700 ms per answered question, so short surveys are not penalized.
Suspicious responses are not rejected. They are saved and flagged, and show a Possible bot chip in the responses list, so you can review them and hide or delete them if needed. Over the API, flagged responses carry the signals in their context:
"context": {
"suspectedBot": true,
"botSignals": ["too_fast"]
}Rate Limiting
Starting a response is rate-limited per IP address and survey (20 starts per minute by default), on top of the general limit for public survey requests. Eligibility checks (dry runs) that do not create a response do not count.
Self-hosting
Turnstile needs a site key and a secret key from the Cloudflare dashboard:
| Variable | Where | Description |
|---|---|---|
TURNSTILE_SECRET_KEY | API | Turnstile secret key used to verify tokens. Without it, surveys cannot use the invisible challenge. |
VITE_TURNSTILE_SITE_KEY | web-survey, at build time | Turnstile site key the survey renderer uses to run the challenge. |
TURNSTILE_VERIFY_TIMEOUT_MS | API | Timeout for the verification call to Cloudflare. Default 5000. |
TURNSTILE_FAIL_OPEN | API | When Cloudflare cannot be reached or times out, starts fail closed (are rejected) unless this is true. Default false. |
PUBLIC_RESPONSE_START_PER_SURVEY_MAX | API | Response starts allowed per IP and survey in each window. Default 20. |
PUBLIC_RESPONSE_START_PER_SURVEY_WINDOW_MS | API | Window for the limit above. Default 60000. |
BOT_MIN_COMPLETION_MS | API | Upper bound of the minimum completion time. The threshold is the smaller of this and 700 ms per answered question. Default 3000. |
If you serve the survey renderer with your own Content Security Policy, allow https://challenges.cloudflare.com in script-src, frame-src and connect-src. The bundled nginx.conf already does.