The recommended flow
How we suggest a site builds the whole thing, end to end: the sequence, a diagram, the decisions you make and the things not to do.
The sequence
Independent of framework or platform, every integration is the same eight steps. The overview has the code for each; the Browser API and Server check pages have the detail.
- Your pageLoads the script early, with the page, before the visitor can submit anything. It is small and deferred, so it never delays the page.
- Your page scriptInitialises with the site key as soon as the form is on screen. The challenge is fetched and solved in the background while the visitor is still typing.
- The visitorFills the form. Nothing is shown; the check is already running.
- Your page scriptOn submit, asks the widget for the pass. Almost always it is there already; when the attempt looked risky, a box asks for a short hold or a slide first.
- Your page scriptSends the five pass fields together with the form fields to your own server, in the same request.
- Your serverSigns the lot number with the secret and posts the pass to the verify endpoint. The secret never leaves your server.
- Your serverActs on the answer: success lets the form through, fail refuses it, no answer follows your outage policy.
- Your pageShows the visitor the result, and arms a fresh check if the form stays open for another attempt.
The same thing as a picture
Decisions you make
| Decision | Our recommendation | Why |
|---|---|---|
| Load at page load or at submit | Page load, with init when the form is shown. | Started at submit, the pass needs three round trips in a row before anything else can happen: the script download, the challenge request and the verification, each bounded at a few seconds on a bad connection, plus the background work itself. Started with the page, all of that runs while the visitor types, and the submit is usually instant. |
| What to do when the verification fails | Treat it as a failed submit: show the message, keep the form filled, arm a fresh check. | A fail at the browser means the challenge expired or the solution did not check out; a fail at your server means the pass was not ours, was reused or was too old. Neither is recoverable with the same pass, and the visitor should not lose their input. |
| Fail open or fail closed when we cannot be reached | Fail open, with a logged counter and an alert, for sign-up and login forms. Fail closed for high-value actions: payouts, password or e-mail changes, account deletion. | A refused sign-up costs you a customer with certainty; a bot that gets through during an outage costs you what it can do afterwards. Where that is a lot, close. The outage policy page has the pattern and the counter. |
| One pass per form submit | One pass, confirmed once, then a fresh widget for the next attempt. | A pass is spent by its first confirmation. Sending the same pass with a second submit, or confirming it twice, answers -50003. |
| Server-side timeout | About 5 seconds for the whole call, then your outage policy. | A decision normally takes well under a second. A longer wait holds a visitor on a spinner for the same answer you get from the policy; a much shorter one turns ordinary network variance into outages. |
Do not
- Never confirm a pass from the browser. The verify endpoint takes the secret, and anything the browser can do, a bot can do. The browser gets a pass; only your server can turn it into a decision.
- Never reuse a pass. One pass, one submit, one confirmation. A second use is refused, and a form that depends on it fails for a real visitor.
- Never put the secret in client code. Not in a script, a bundle, a mobile app or a configuration file served to browsers. The site key is public; the secret is what makes your server's word good.
- Never block the form while the script is loading. The visitor can type while the loader arrives; the submit handler waits for the pass, not the page. A form that is disabled until a third-party script runs is a form that fails when an ad blocker or a slow network delays it.