Skip to content

Invisible CAPTCHA (Beta)

Invisible CAPTCHA provides seamless bot protection without interrupting the user experience. The CAPTCHA challenge runs in the background and only presents a visible challenge if additional verification is needed.

Invisible CAPTCHA works by:

  1. Running verification challenges in the background
  2. Analyzing user behavior and browser characteristics
  3. Only showing a visible challenge if the user appears suspicious
  4. Providing a smooth experience for legitimate users

data-size="invisible" is what makes the widget invisible. Put it on the element that submits the form:

<button
class="procaptcha"
data-sitekey="your_site_key"
data-size="invisible"
data-callback="onCaptchaSuccess">
Submit
</button>

Which challenge runs behind it is a property of the site key, chosen in the Prosopo portal — see CAPTCHA Types. On a frictionless site key most visitors complete an invisible Proof of Work and never see anything; a visitor who scores as risky is shown a puzzle or an image challenge at the point of submission.

Add the procaptcha class and data attributes directly to your form elements:

<!DOCTYPE html>
<html>
<head>
<script type="module" src="https://js.prosopo.io/js/procaptcha.bundle.js" async defer></script>
<script nomodule src="https://js.prosopo.io/js/procaptcha.bundle.iife.js" async defer></script>
</head>
<body>
<form>
<input type="email" name="email" required>
<button
type="button"
class="procaptcha"
data-sitekey="your_site_key"
data-size="invisible"
data-callback="handleSubmit"
data-failed-callback="handleFailure">
Submit Form
</button>
</form>
<script>
function handleSubmit(token) {
console.log('CAPTCHA verified:', token);
// Submit your form with the token
}
function handleFailure() {
console.log('CAPTCHA verification failed');
// Handle failure case
}
</script>
</body>
</html>

For more control over the CAPTCHA lifecycle:

<!DOCTYPE html>
<html>
<head>
<script type="module" src="https://js.prosopo.io/js/procaptcha.bundle.js" async defer></script>
<script nomodule src="https://js.prosopo.io/js/procaptcha.bundle.iife.js" async defer></script>
</head>
<body>
<form id="demo-form">
<input type="email" name="email" required>
<div id="procaptcha"></div>
<button type="submit">Submit Form</button>
</form>
<script type="module">
import { render, execute } from "https://js.prosopo.io/js/procaptcha.bundle.js"
let widgetId;
function handleCaptchaSuccess(token) {
console.log('CAPTCHA verified:', token);
// Process form submission with token
}
function handleCaptchaFailure() {
console.log('CAPTCHA verification failed');
// Handle failure
}
document.addEventListener('DOMContentLoaded', async function() {
// Render invisible CAPTCHA. render() is async, so await it before
// using the id it resolves to.
widgetId = await render(document.getElementById('procaptcha'), {
siteKey: 'your_site_key',
size: 'invisible',
callback: handleCaptchaSuccess,
'failed-callback': handleCaptchaFailure
});
// Handle form submission
document.getElementById('demo-form').addEventListener('submit', function(e) {
e.preventDefault();
// Trigger CAPTCHA verification. Pass the widget id to trigger only
// this widget; with no argument every widget on the page runs.
execute(widgetId);
});
});
</script>
</body>
</html>
AttributeValueDescription
data-size"invisible"Required. Enables invisible mode
data-callbackFunction nameCalled when CAPTCHA is successfully verified
data-failed-callbackFunction nameCalled when CAPTCHA verification fails
data-sitekeyYour site keyRequired. Your Procaptcha site key
data-start-mode"auto", "manual"When the background work starts. auto (default) starts on page load. manual waits until you call window.procaptcha.start() or execute(). See Controlling when the widget starts.
data-bindCSS selectorA button on your page that triggers this widget, instead of your code calling execute(). The button’s default action is prevented. See Binding a button to the widget.
data-placement"popup"Accepted for consistency with visible widgets, but an invisible widget has no checkbox to anchor a float challenge to, so its challenge always opens as a centred popup.

Set this on the site key in the portal, not in your markup:

  • Frictionless: best for most use cases — it adapts per visitor, and most of them see nothing at all
  • PoW: when you want every visitor to get the same invisible challenge and never a visible one
  • Puzzle or Image: when every visitor should get the same visible challenge on submission

Always implement a failed callback to handle verification failures:

function onCaptchaFailed() {
// Show user-friendly error message
alert('Verification failed. Please try again.');
// Reset form or provide alternative
}

Since verification happens in the background, consider showing loading states:

function onCaptchaStart() {
document.getElementById('submit-btn').disabled = true;
document.getElementById('submit-btn').textContent = 'Verifying...';
}
function onCaptchaComplete(token) {
document.getElementById('submit-btn').disabled = false;
document.getElementById('submit-btn').textContent = 'Submit';
// Process with token
}

Always verify the Procaptcha response on your server:

// Node.js example
const response = await fetch('https://api.prosopo.io/siteverify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
secret: 'your_secret_key',
token: token, // Token from Procaptcha callback
ip: userIP // Optional
})
});
const result = await response.json();
if (result.verified) {
// Procaptcha verified successfully
console.log('Verification successful');
} else {
// Verification failed
console.log('Verification failed:', result.status);
}
  • Ensure data-size="invisible" is set
  • Check that your site key is correct and active
  • Verify your domain is registered in the portal
  • Confirm you’re on Pro or Enterprise tier

This is normal behavior when:

  • User behavior appears suspicious
  • Browser characteristics suggest automation
  • Additional verification is needed for security
  • Make sure the Procaptcha script loads before your code runs
  • Check browser console for error messages
  • Verify callback functions are defined globally
  • Test with different CAPTCHA types to isolate issues

Migration from Visible CAPTCHA

Section titled Migration from Visible CAPTCHA

To convert existing visible CAPTCHA implementations:

  1. Add data-size="invisible" to existing configurations
  2. Update UI to remove CAPTCHA container (for implicit rendering)
  3. Adjust form styling since no visible widget will appear
  4. Test user flows to ensure smooth experience
  5. Update any size-dependent CSS or JavaScript