Node

Copy sdk/node/privcaptcha.js into your project. It uses the built-in fetch, so Node 18 or newer and no dependencies.

Verify

const { verify } = require('./privcaptcha');

const result = await verify({
  secret: process.env.PRIVCAPTCHA_SECRET,
  response: req.body['privcaptcha-response'],
  remoteIp: req.ip,
});
if (!result.success) {
  return res.status(400).json({ errors: result.errorCodes });
}
Option Default Description
secret your site secret
response the token from the form field
remoteIp null the visitor's IP, sent as remoteip
endpoint https://privcaptcha.com/api/v1/siteverify override for testing
timeoutMs 10000 aborts the request after this long

The result is { success, challengeTs, hostname, errorCodes }.

Express middleware

const { middleware } = require('./privcaptcha');

app.post(
  '/signup',
  middleware({ secret: process.env.PRIVCAPTCHA_SECRET }),
  handler
);

On success it sets req.privcaptcha to the result and calls next(). On failure it responds 400 with { error: 'captcha_failed', codes: [...] }, unless you pass onFail(req, res, result).

It reads the token from whichever of these fields is present, so the compatibility shims work unchanged:

privcaptcha-response
h-captcha-response
g-recaptcha-response

This requires a body parser to have run first.

Fail-closed behaviour

A timeout, a network error or a non-200 response resolves to { success: false, errorCodes: ['verification-unavailable'] } rather than throwing. Our outage must not become your breach. An empty response short-circuits to missing-input-response with no network call.