DEVELOPER DOCUMENTATION

Migrate from reCAPTCHA

Replace Google reCAPTCHA (v2 or v3) with MANDATE bot detection.

Start in Observe on one route. Use the deployment URL and installed package version supplied for your workspace. Historical host examples and source capabilities do not establish a live service or published release.

Warning

Use the unpublished reviewed kit candidate for these server examples, not npm

0.3.0. Normal protected browser actions require hosted live Gate authority.

Legacy CAPTCHA shims preserve callback/form compatibility only during bounded

migration; they are not production browser authority. Hidden response fields

alone cannot replace cookie/request proof. Complete the first-party hosted

rollout before removing the previous production control.

Migration Approaches

There are two ways to migrate from reCAPTCHA to MANDATE:

ApproachEffortDescription
Clean migrationMediumRemove reCAPTCHA entirely, integrate MANDATE directly
Shim migrationLowDrop-in compatibility layer that maps reCAPTCHA APIs to MANDATE

Clean Migration

Remove the reCAPTCHA Script

Remove the existing reCAPTCHA script tag:

html
<!-- Remove this -->
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>

Add the MANDATE Script

html
<!-- Add this -->
<script async src="https://gate.x-lock.dev/g.js?k=YOUR_SITE_KEY"></script>

Update Frontend Token Retrieval

Replace grecaptcha.execute() calls with the MANDATE token function:

javascript Before (reCAPTCHA v3)
grecaptcha.ready(function() {
  grecaptcha.execute("SITE_KEY", { action: "submit" }).then(function(token) {
    document.getElementById("token-field").value = token;
  });
});
javascript After (MANDATE)
await fetch("/api/action", { method: "POST" });
// Production same-origin protected requests carry Mndt-Sess automatically.

See Frontend Integration for the same-origin request-boundary contract.

Update Backend Verification

Replace the reCAPTCHA siteverify API call with local MANDATE SDK verification:

javascript Before (reCAPTCHA)
// Network call to Google
const response = await fetch(
  "https://www.google.com/recaptcha/api/siteverify",
  {
    method: "POST",
    body: new URLSearchParams({
      secret: RECAPTCHA_SECRET,
      response: token,
    }),
  }
);
const data = await response.json();
if (!data.success || data.score < 0.5) {
  // Block request
}
javascript After (MANDATE)
import { validateHostedRequest } from "@mandate-security/node";

const result = await validateHostedRequest(req, {
  siteSecret: process.env.MANDATE_SITE_SECRET,
  siteKey: process.env.MANDATE_SITE_KEY,
  apiKey: process.env.MANDATE_API_KEY,
  gateUrl: process.env.MANDATE_GATE_URL,
  credentialMode: "cookie", credentialCookieName: "Mndt-Sess",
  highValue: true, origin: "https://app.example.com",
});
if (!result.success) {
  // Keep the protected action denied. Prefer a framework adapter so
  // authenticated cookie/companion/native updates are applied too.
}

Remove reCAPTCHA Dependencies

  • Delete the reCAPTCHA secret key from environment variables
  • Remove any reCAPTCHA-specific CSS (e.g., hiding the badge)
  • Remove grecaptcha type definitions if using TypeScript

Shim Migration

The shim provides a compatibility layer that maps reCAPTCHA API calls to MANDATE. Use this when you want minimal code changes or need to migrate incrementally.

Swap Script Tags

Replace the reCAPTCHA script with the MANDATE script and shim:

html
<!-- Remove this -->
<script src="https://www.google.com/recaptcha/api.js?render=YOUR_SITE_KEY"></script>

<!-- Add these (order matters) -->
<script async src="https://gate.x-lock.dev/g.js?k=YOUR_SITE_KEY"></script>
<script src="https://gate.x-lock.dev/compat/mandate-shim-core.js"></script>
<script src="https://gate.x-lock.dev/compat/recaptcha-shim.js"></script>

Info

Shims are served from the gate at /compat/*. Load order: g.js → mandate-shim-core.js → provider shim. The core reads the legacy compatibility bridge and never calls polymorphic g.js globals. Enable MANDATE_TOKEN_BRIDGE=legacy only in non-production shim migration tests; production rejects page-visible token bridges.

Verify Existing Code Works

The shim provides the following grecaptcha API surface. Your existing frontend code should work without changes:

reCAPTCHA APIShim Behavior
grecaptcha.ready(callback)Calls callback when MANDATE is ready
grecaptcha.execute(siteKey, options)Returns MANDATE token via Promise
grecaptcha.getResponse()Returns current MANDATE token
grecaptcha.render(container, options)No-op (MANDATE is invisible)
grecaptcha.reset()Triggers token refresh

Update Backend Verification

The shim only covers the frontend. You must still replace the backend siteverify call:

javascript
// Replace Google siteverify with the MANDATE SDK
import { validateHostedRequest } from "@mandate-security/node";

const result = await validateHostedRequest(req, {
  siteSecret: process.env.MANDATE_SITE_SECRET,
  siteKey: process.env.MANDATE_SITE_KEY,
  apiKey: process.env.MANDATE_API_KEY,
  gateUrl: process.env.MANDATE_GATE_URL,
  credentialMode: "cookie", credentialCookieName: "Mndt-Sess",
  highValue: true, origin: "https://app.example.com",
});
if (!result.success) {
  // Keep the protected action denied. Prefer a framework adapter so
  // authenticated cookie/companion/native updates are applied too.
}

Info

The shim also populates hidden form fields named g-recaptcha-response for compatibility with forms that submit tokens via POST body. You can read the token from either location during migration.

Remove the Shim (Optional)

Once migration is confirmed stable, remove the shim scripts, disable MANDATE_TOKEN_BRIDGE=legacy, and move production browser routes to first-party cookie delivery and hosted validation (see Frontend Integration).

Key Differences

FeaturereCAPTCHAMANDATE
User interactionSometimes (v2 checkbox/puzzle)Never
Backend verificationAPI call to GoogleLocal cryptographic check
Token deliveryHidden form field or JS callbackHttpOnly Mndt-Sess in production; X-Mndt only for compatibility
LatencyNetwork round-tripNo MANDATE round trip for compatible local verification
PrivacyData sent to GoogleNo third-party data sharing
Score range0.0 - 1.00.0 - 1.0

Warning

reCAPTCHA Enterprise customers: the shim does not support Enterprise-specific features like grecaptcha.enterprise.execute(). Use the clean migration approach instead.