Installation

Load the embed script and mark a container. Everything else is automatic.

<script src="https://privcaptcha.com/1/api.js" async defer></script>
<div class="privcaptcha" data-sitekey="pk_your_sitekey"></div>

The script is cached for 10 minutes, so a widget fix reaches your visitors the same day without you redeploying anything.

Container attributes

Attribute Required Meaning
class="privcaptcha" yes, for automatic rendering marks the container the loader scans for
data-sitekey yes your public sitekey
data-callback no name of a global function called with the token on success
<div class="privcaptcha"
     data-sitekey="pk_your_sitekey"
     data-callback="onCaptchaSolved"></div>

<script>
  function onCaptchaSolved(token) {
    document.querySelector('#submit').disabled = false;
  }
</script>

The response field

On success the token is written into a hidden input named privcaptcha-response inside the container. A normal form submit carries it; you do not need any JavaScript.

Read it server-side as you would any form field, then verify it. If you submit with fetch() instead, read the token with privcaptcha.getResponse().

Explicit rendering

Skip the privcaptcha class and render when you are ready — useful for dynamically inserted forms, modals or single-page apps.

<div id="captcha-slot"></div>
<script>
  var id = privcaptcha.render('captcha-slot', {
    sitekey: 'pk_your_sitekey',
    callback: function (token) { console.log(token); },
    'expired-callback': function () { console.log('token expired'); },
    'error-callback': function (err) { console.error(err); },
  });
</script>

render() accepts an element or an element id and returns a widget id you can pass to the other JavaScript API methods. It returns null if no sitekey was given.

Layout

The badge is a 302×76 iframe. The puzzle opens in a centred overlay above the page (z-index: 2147483647), dismissible with Escape or a click outside. Nothing is injected into your page's styles, and the iframes carry a frame-ancestors policy scoped to your site's own domains, so no one else can frame your widget.

The iframe endpoints

The loader builds these itself; you never link to them directly, but they show up in a CSP report or a network tab, so they are listed here.

Path What it is
/widget/badge the 302×76 checkbox iframe
/widget/challenge the puzzle iframe inside the overlay

Both take the sitekey as a query parameter and answer with a Content-Security-Policy: frame-ancestors header scoped to that site's domains, so only your own pages can frame them. A site with an empty domain list gets *, so a first integration works before the list is configured; an unknown sitekey gets 'none'. Both are noindex.

Lifetime

A token is valid for 120 seconds server-side. The widget clears it after 110 seconds, resets itself and fires expired-callback, so a slowly-filled form never submits a dead token.