Captcha
Keep bots out of your forms with one click for people and no tracking cookies.
Create a site key
- Open Captcha in the dashboard sidebar and choose New site key.
- Name it, list the hostnames it runs on (example.com, www.example.com or *.example.com for every subdomain), and pick Checkbox or Invisible.
- Copy the site key for your page and the secret for your server. The secret is shown only once, so save it right away. If it is lost, roll a new one from the key's page.
Plan allowances
Captcha is on every plan, Free included. A check is one visitor passing the captcha. Checks are counted for your whole workspace each calendar month (UTC), so deleting a site key doesn't take its checks off the count, and test keys never count.
Checks past your plan are paid from your points with pay as you go, at $1 per 10,000, taken in blocks of 1,000. Going a little over doesn't break your forms: with pay as you go off or no points, checks keep working up to twice your plan's checks, then new challenges stop until the month resets, you add points or you upgrade. Keys you made on a higher plan keep working after a downgrade; you just can't add more until you are under the new limit.
| Plan | Site keys | Checks a month |
|---|---|---|
| Free | 1 | 10,000 |
| Launch | 5 | 100,000 |
| Pro | Unlimited | 1,000,000 |
| Scale | Unlimited | 10,000,000 |
Add it to your form
Load the script once and put a box where the check should appear. When a visitor passes, the form gets a hidden field called rimbra-captcha-response.
<script src="https://api.rimbra.app/captcha/v1/api.js" async defer></script>
<form action="/signup" method="post">
<input name="email" type="email" required>
<div class="rimbra-captcha" data-sitekey="YOUR_SITE_KEY"></div>
<button type="submit">Sign up</button>
</form>Options
| Attribute | What it does |
|---|---|
| data-sitekey | Your site key. Required. |
| data-mode="invisible" | No box. The check runs when the form is sent, then the form goes through. |
| data-theme | auto (default), light or dark. |
| data-size | normal (default) or compact. |
| data-callback | Name of a function that gets the token when the check passes. |
| data-expired-callback | Called when a token expires after 5 minutes unused. |
| data-error-callback | Called with an error code if the check can’t run. |
Difficulty
Each site key has a difficulty, set on its Settings tab. Nothing changes in your code when you switch.
| Difficulty | What visitors do |
|---|---|
| Light | One click. The lightest background check, for low-risk forms. |
| Automatic (default) | One click. The background check gets harder for anyone asking often. |
| Strong | One click, then one short puzzle: slide a piece into the gap in a picture. Arrow keys and Enter work too. Two misses bring a new picture. |
JavaScript API
Boxes render on their own. For pages that build forms later, load the script with ?render=explicit and call render yourself.
const id = rimbraCaptcha.render("#captcha", { sitekey: "YOUR_SITE_KEY", callback: (token) => {} })
rimbraCaptcha.execute(id) // run the check now (invisible keys); returns a promise for the token
rimbraCaptcha.getResponse(id) // the current token, or ""
rimbraCaptcha.reset(id) // start over
rimbraCaptcha.remove(id) // take the box off the pageCheck the token on your server
Before trusting the form, post the token and your secret to siteverify. Form or JSON bodies both work. A token works once and lasts 5 minutes.
const res = await fetch("https://api.rimbra.app/captcha/v1/siteverify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
secret: process.env.RIMBRA_CAPTCHA_SECRET,
response: req.body["rimbra-captcha-response"],
}),
})
const result = await res.json()
// { success: true, challenge_ts: "2026-11-09T10:00:00Z", hostname: "example.com", "error-codes": [] }
if (!result.success) return res.status(400).send("Please confirm you're human.")Error codes
| Code | Meaning |
|---|---|
| missing-input-secret | No secret was sent. |
| invalid-input-secret | The secret doesn’t belong to any site key. |
| missing-input-response | No token was sent. The visitor may not have done the check. |
| invalid-or-already-seen-response | The token is not valid, or it belongs to another site key. |
| timeout-or-duplicate | The token is older than 5 minutes or was already used. |
| hostname-mismatch | The page’s hostname is no longer on the key. |
Test keys
Use these in development and CI. They run on any hostname and never count toward your stats.
| Behaviour | Site key | Secret |
|---|---|---|
| Always passes | rck_test_always_passes | rcs_test_always_passes |
| Always fails | rck_test_always_fails | rcs_test_always_fails |
Privacy and how it decides
- No cookies and no tracking. Rimbra keeps no profile of the people who pass.
- The browser solves a small proof of work, usually under a second. Visitors asking unusually often get more work.
- Browsers that say they are automated, and headless browsers, are refused.
- Counts per site key (shown, passed, confirmed, refused) appear in the dashboard for the last 7 days.
