Quickstart

Register a site, embed the widget and verify a submission — the whole loop in four steps.

The whole integration is a <div>, a <script> and one server-to-server call.

1. Register a site

Each application and environment gets its own site. Against the hosted service this is a button in the dashboard; against your own instance it is one call with your ADMIN_TOKEN:

bash
curl -s -X POST https://api.bollwark.eu/v1/sites \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"my-production-app"}'
json
{
  "site_key": "00000000-0000-0000-0000-000000000000",
  "secret_key": "hex-encoded-secret",
  "allowed_origins": [],
  "policy": {}
}
Key Where it goes
site_key Public. Goes in your HTML.
secret_key Private. Backend configuration only.

The secret is shown once. No endpoint reads it back, so if you lose it, rotate it. A reveal would be silent; a rotation breaks the integration immediately and loudly, which is the behaviour you want from a lost credential.

2. Embed the widget

html
<form action="/signup" method="post">
  <label>
    Email
    <input type="email" name="email" required>
  </label>

  <div data-sitekey="YOUR_SITE_KEY"></div>

  <button type="submit">Create account</button>
</form>

<script src="https://api.bollwark.eu/v1/widget.js"></script>

The widget writes a hidden captcha-token field into the enclosing <form>. /v1/widget.js is the only URL to hardcode — it pulls its stylesheet, worker and Argon2 bundle from a content-hashed directory pinned to that build.

3. Verify on your backend

Reject the submission unless /v1/verify returns 200 and success: true.

bash
curl -s -X POST https://api.bollwark.eu/v1/verify \
  -H "Authorization: Bearer YOUR_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "token": "<captcha-token value>" }'
json
{ "success": true, "failover": false, "risk": "low", "challenge_id": "…" }

Challenges are single-use — a replayed challenge_id fails.

4. Start in monitor mode

Putting this in front of a form that already works? Register it with "mode": "monitor". Every verdict is scored and logged exactly as it would be in enforcement, but nobody is ever refused.

bash
-d '{"name":"contact-form","policy":{"mode":"monitor"}}'

Watch the dashboard for a week, then flip to enforce. The scoring is identical in both modes, so none of the numbers move when you do.