Documentation

Quickstart

Five minutes from signup to first verification. Drop-in compatible with reCAPTCHA, hCaptcha, and Turnstile — your existing form code keeps working.

1 · Sign up

Create a free account at trustedcaptcha.com/signup. Verify your email. The free plan covers 1,000 verifications per day with no credit card.

2 · Add a site

In the dashboard, click Add a site. Provide:

  • A name (anything human-readable, just for your records)
  • Your production hostnames, comma-separated. Wildcards like *.example.com work. Add localhost for local development.
  • Default mode — leave on auto unless you have a specific reason. The risk engine picks the right challenge type per request.

You'll be given a sitekey (public, goes on your page) and a secret (private, lives on your server). The secret is shown once. Copy it now into a secure place — your server's environment, a secret manager, your .env file. We don't store it in plaintext on our side and can't show it again.

Sitekey shape: 0x9a7f4b2c8e1d3f5a6b7c8d9e0f1a2b3c4d (36 chars). Secret shape: 0x + 64 hex chars (66 total). Both prefixed with 0x so you can spot them in logs.

3 · Add the widget to your page

Include the loader script once, anywhere in your HTML. It's about 12 KB gzipped and auto-renders any element with the .trustedcaptcha class.

<!doctype html>
<html>
<head>
  <script src="https://cdn.trustedcaptcha.com/widget/v1/api.js"
          async defer></script>
</head>
<body>

  <form action="/signup" method="POST">
    <input name="email" type="email" required>
    <input name="password" type="password" required>

    <div class="trustedcaptcha"
         data-sitekey="0x9a7f4b2c..."
         data-theme="auto"></div>

    <button type="submit">Sign up</button>
  </form>

</body>
</html>

The widget renders as a checkbox initially (the same size as reCAPTCHA v2 — 304×78 px), validates passively, and only shows a challenge if the risk engine flags the request.

Available data-* attributes

AttributeValuesDefault
data-sitekeyYour sitekeyrequired
data-themeauto, light, darkauto
data-sizenormal, compact, invisiblenormal
data-modeauto, smart, image, math, logic, audio, invisibleauto
data-languageauto, en, de, fr, esauto
data-callbackName of a global function called with the token
data-expired-callbackCalled when the token expires (after 180s unused)
data-error-callbackCalled on any widget error

4 · Verify on your server

When the form submits, your form's POST body contains a hidden field trustedcaptcha-response with a 40-byte token. On your server, POST it to the verify endpoint with your secret to validate.

# Endpoint
POST https://challenges.trustedcaptcha.com/api/v1/siteverify

# Form-encoded body
secret=YOUR_SECRET
response=USER_TOKEN
remoteip=USER_IP    # optional but recommended

# Response (reCAPTCHA-compatible JSON)
{
  "success":      true,
  "score":        0.92,
  "mode":         "smart",
  "hostname":     "example.com",
  "challenge_ts": "2026-05-11T09:00:00+00:00",
  "action":       null,
  "error_codes":  []
}

PHP example

$response = file_get_contents(
  'https://challenges.trustedcaptcha.com/api/v1/siteverify'
  . '?secret=' . urlencode($SECRET)
  . '&response=' . urlencode($_POST['trustedcaptcha-response'])
  . '&remoteip=' . urlencode($_SERVER['REMOTE_ADDR'])
);
$result = json_decode($response, true);

if (!$result['success']) {
    http_response_code(400);
    exit('Captcha verification failed');
}

// $result['score'] is 0.0 (bot) to 1.0 (human). Threshold per your risk tolerance.
if ($result['score'] < 0.3) {
    // Add to manual review queue, request 2FA, etc.
}

Examples for Node.js, Python, Ruby, Go, and curl are in the developers guide.

5 · Compatibility with existing reCAPTCHA / hCaptcha / Turnstile code

The widget fills four hidden inputs simultaneously — trustedcaptcha-response plus g-recaptcha-response, h-captcha-response, and cf-turnstile-response. If your server already reads any of these from $_POST, no server-side changes are needed.

The /api/v1/siteverify endpoint accepts the same parameters as reCAPTCHA's siteverify (secret, response, remoteip) and returns the same JSON shape (success, score, challenge_ts, hostname, error_codes). The only field that's different is the endpoint URL.

Migrating from reCAPTCHA? See the migration guide for a one-line drop-in path. Most apps need only two changes: the script URL and the verify endpoint URL.

Error codes

If success is false, the error_codes array tells you why:

CodeMeaning
missing-input-secretThe secret field is empty.
invalid-input-secretThe secret doesn't match any site.
missing-input-responseThe token field is empty.
invalid-input-responseThe token doesn't exist or is malformed.
timeout-or-duplicateToken expired (over 180s old) or already verified.
hostname-mismatchThe parent page's hostname isn't in this site's allowlist.
rate-limitedToo many verify calls for this secret. Back off.

Next steps

  • Read the widget reference for programmatic control (reset(), execute(), callbacks).
  • See API reference for the full /challenge and /answer endpoints (you typically don't need them — the widget handles both — but they're documented for SDK authors).
  • Configure site settings to enable accessibility-only mode, change the default challenge type, or adjust the score threshold.