Troubleshooting
Symptoms in the browser and on your server, what each one means and what to change.
In the browser
| Symptom | Cause | What to do |
|---|---|---|
| "otcaptcha is not defined" | Your code ran before the loader. A plain script before the loader tag, or a module script, runs first. | Put the loader tag first and give both tags defer, or run your code on the loader tag's load event. |
| onError with code 60100, no ready | The site key was refused when the challenge was requested. It is unknown, or not 32 lowercase hex characters. | Use the site key exactly as issued. It is the same value your server passes as captcha_id. |
| onFail then onError with code 60500 | The service refused the verification: the challenge was older than ten minutes, or the solution did not check out. | Call init closer to the submit, or arm again when the form has been open a long time, as the quickstart does. If it repeats on fresh widgets, write to us with the lot_number from the fail payload. |
| ready takes about five seconds, then success with a lot_number starting with "offline" | The browser could not reach the API host: a content security policy without our host in connect-src, an ad blocker, a corporate proxy, or a CORS refusal because the page origin is not allowed for the site key. | Check the console for a policy or CORS message. Add https://api.otcaptcha.com to connect-src, or tell us the page origins your site uses. |
| The box appears with no styling | The page policy refuses inline styles, so the stylesheet the box carries is dropped. | Add 'unsafe-inline' to style-src. See Content security policy. |
| The solve is slow, the console shows a worker or WebAssembly policy violation | Workers or WebAssembly are refused by the page policy, so the loader fell back to the main thread or to JavaScript. | Allow the loader's origin in worker-src and add 'wasm-unsafe-eval' to script-src. |
| Nothing is ever shown; every attempt passes silently | That is the normal case. Most attempts pass with no box. | To see the box, raise an attempt's score as described on the Testing page, or open the demo page. |
| The box opens, but Tab leaves it or Escape does nothing | Another script on the page is handling keyboard events on the document before the box sees them. | Let keyboard events reach the box while it is open, or stop the other handler while a dialog is open. |
| showBox() does nothing the second time | A widget runs its check once. After success, close or error it is finished. | Call destroy() and init again for a new attempt. |
On your server
| Answer | Cause | What to do |
|---|---|---|
| -50002 unauthorized encryption | sign_token does not match: the wrong secret, the wrong field signed, or uppercase hex. | Sign lot_number only, with the secret as the HMAC key, SHA-256, and send lowercase hex. |
| -50003 repeated verification | The pass was already checked: a double submit, a retry more than 20 seconds after the first call, or a replay. | Confirm each pass once. Retries are safe within 20 seconds. |
| -50102 invalid captcha ID | The captcha_id in the query is not a site key we know. | Use the site key as issued, and pass the one from the pass itself. |
| -50306 invalid process token, lot_number starts with "offline" | The visitor's browser could not reach us and made an offline pass, which we refuse when we are reachable. If this rises across many visitors, something on the visitors' side blocks our host. | See the browser rows above. Count these separately so a policy fault on your page is noticed. |
| -50306 invalid process token, normal lot_number | One of the five strings was altered in transit, or the pass is presented under a different site key than it was issued for. | Send the pass exactly as getValidate() returned it, and use its own captcha_id. |
| -50307 invalid payload parameters | A field is missing or empty, gen_time is not a whole number, or the body is not a form: JSON bodies and query-string fields are not read. | Send all five fields in an application/x-www-form-urlencoded body. |
| -50308 expired payload | gen_time is more than five minutes old, or more than a minute in the future. | Confirm the pass as soon as it reaches your server, and check your server clock. |
| HTTP 403 | The endpoint has an address allow-list and your server is not on it. | Tell us your egress addresses. Do not treat it as an outage to fail open on. |
| HTTP 405 | The request was not a POST. | POST, with the site key in the query string and the fields in the body. |
| HTTP 503, timeout or connection error | No decision could be made: our store, our capacity or the path between us. | Your outage policy applies. Count it and alert on it. |
Writing to us
Include the lot_number of an affected attempt, the loader version from otcaptcha.version, the page origin, and the time with its zone. The lot number lets us find the attempt's record without any personal data; records are kept for 90 days. Never send us your secret.