Skip to content

Integrate InstantBlock.

Check a browser when it reaches a protected action. Verify the signed result on your server before you let that action continue.

01Start here

Choose the action you want to protect, such as submitting a form or creating an account. Run verification immediately before that action.

  1. Create a site. Add its domain under Your sites. Save the site API key and signing secret when the site is created; the console displays them once.
  2. Call the browser client. Pass the public sitekey to runChallenge() and send its token with the action request.
  3. Enforce on your backend. Check the token signature, expiry, sitekey and decision === 'accept' before processing the action.

A browser check is a risk decision. Keep your normal login, authorization, rate limiting and abuse controls in place.

02Browser integration

Only the sitekey is public. Use this in an ES module (such as a <script type="module"> block or your bundler) when a visitor submits a protected action. Replace the example endpoint with your own backend route.

Browser · JavaScript
import { runChallenge } from 'https://instantblock.eu/client/api.js';

async function submitProtectedAction(actionData) {
  const result = await runChallenge({
    sitekey: 'YOUR_SITEKEY'
  });
  if (!result.ok || !result.token) {
    throw new Error('Verification could not complete. Please retry.');
  }

  const response = await fetch('/api/protected-action', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: result.token, actionData })
  });
  if (!response.ok) throw new Error('Action could not be completed');
  return response.json();
}

Show a retry option when verification cannot complete. result.ok means the request completed; it is not the visitor's verdict. A signed token may contain a rejected or unfinished challenge decision, so the presence of result.token is not permission to continue.

03Verify on your server

InstantBlock signs site tokens with HMAC-SHA256 using your site's hex-encoded signing secret. Set IB_SITEKEY and IB_SITE_SECRET as server environment variables. This Node.js example rejects malformed, expired and non-accepted tokens.

Backend · Node.js 20+
const crypto = require('node:crypto');
const express = require('express');
// Add the route below to your existing Express app.

function acceptedToken(token, sitekey, secretHex) {
  if (typeof token !== 'string' || !sitekey ||
      !/^[0-9a-f]{64}$/i.test(secretHex || '')) return null;
  const parts = token.split('.');
  if (parts.length !== 3 || parts.some(p => !/^[A-Za-z0-9_-]+$/.test(p))) return null;
  const [header, payload, signature] = parts;
  const expected = crypto.createHmac('sha256', Buffer.from(secretHex, 'hex'))
    .update(header + '.' + payload).digest();
  const received = Buffer.from(signature, 'base64url');
  if (received.length !== expected.length ||
      !crypto.timingSafeEqual(received, expected)) return null;

  try {
    const meta = JSON.parse(Buffer.from(header, 'base64url').toString('utf8'));
    const claims = JSON.parse(Buffer.from(payload, 'base64url').toString('utf8'));
    const now = Math.floor(Date.now() / 1000);
    if (meta.alg !== 'HS256' || meta.typ !== 'IB-JWT' || meta.kid !== sitekey) return null;
    if (!Number.isSafeInteger(claims.iat) || !Number.isSafeInteger(claims.exp)) return null;
    if (claims.iat > now + 60 || claims.exp <= now) return null;
    if (claims.sitekey !== sitekey || claims.decision !== 'accept' || claims.pz) return null;
    return claims;
  } catch { return null; }
}

app.post('/api/protected-action', express.json(), (req, res) => {
  const claims = acceptedToken(
    req.body?.token, process.env.IB_SITEKEY, process.env.IB_SITE_SECRET
  );
  if (!claims) return res.sendStatus(403);
  // Apply your own authorization and perform the protected action here.
  return res.json({ ok: true });
});

Check the decision. A rejected session can still receive a valid signed token. Checking only the signature would let that request through. The example also rejects the rejection marker pz.

The token is valid for up to one hour. If you need one-time use for a sensitive action, track its jti on your server and reject a second use. Offline signature verification alone does not prevent reuse during the token's lifetime.

04What the decisions mean

Accept

The browser check passed. Continue only after your own authorization and business checks.

Challenge harder

The client may ask the visitor to complete an interactive step. A successful check upgrades the token. If it does not pass, the original decision remains unaccepted.

Reject

Do not complete the action. A reject token may still be signed, so always read its decision.

05Session telemetry

By default, runChallenge() starts a bounded behavior recorder on the protected page. It continues after verification until the page closes, you stop it, or its 12-hour credential expires. Starting another verification finishes the previous recorder and starts a new one.

The recorder captures normalized pointer positions, click and key timing, scroll activity, focus and visibility changes, input-method categories, and resource and performance timings. It does not collect typed characters, form values, clipboard contents, DOM selectors, page URLs or browsing history. Password fields, payment autocomplete fields and elements inside data-ib-private are excluded.

Nonempty batches are encrypted before POST /tm, normally every 15 seconds and when the page becomes hidden or closes. Telemetry files are encrypted at rest and grouped by a site-specific keyed IP hash. Uploads and retries run separately from the verification result; these extra records currently support diagnostics and future model development, without changing the visitor's score.

Browser · Lifecycle controls
import { runChallenge, flushTelemetry, stopTelemetry }
  from 'https://instantblock.eu/client/api.js';

await runChallenge({ sitekey: 'YOUR_SITEKEY' });
await flushTelemetry(); // Optional: upload the current nonempty batch.
await stopTelemetry();  // Flush once and remove the recorder's listeners.

// Disable the additional recorder for this verification:
await runChallenge({ sitekey: 'YOUR_SITEKEY', telemetry: false });

Describe the recorder in your site's privacy notice and apply your site's consent requirements before starting it. Network failures or page closure can prevent delivery; these records are observations, not proof that a visitor is human or automated. The existing 90-day verification-data cleanup also covers telemetry.

06Which credential goes where?

Integration credentials and where to keep them
CredentialUseLocation
sitekeyIdentifies the site for browser checksBrowser and server
secretVerifies signed decision tokensServer only
api_keyReads your site's API records and configurationServer only
decode_seedDecodes scattered fingerprint recordsServer only

You have no site yet. Create a site to get a sitekey.

The console masks secret values after site creation. If you still have the site's API key, your backend can call GET https://instantblock.eu/api/v1/me with Authorization: Bearer <site API key> to retrieve its signing secret. Never call that endpoint from browser code. The separate keys under Dashboard → API keys are for the console API, not the engine Site API.

07Site API

These endpoints require the site API key in an Authorization: Bearer header. They return records for that site only. Keep the key in a trusted backend or command-line environment.

Site API endpoints
EndpointReturns
GET /api/v1/meSite configuration, signing secret and decode seed
GET /api/v1/statsDecision totals; optional since Unix milliseconds
GET /api/v1/verificationsRecent records; optional limit, since, decision and user_id
GET /api/v1/verifications/:cidOne record; add ?format=plain for readable fingerprint fields
GET /api/v1/users/:user_idRecent sessions for a user identifier you supplied
GET /api/v1/sessions/:tokenCurrent in-memory status for a signed token or its jti, while available
Server terminal · cURL
curl -H "Authorization: Bearer $IB_SITE_API_KEY" \
  "https://instantblock.eu/api/v1/verifications?limit=20"

The /admin/* engine routes use a separate master key and are for the service operator. A site API key does not grant access to them.

08Troubleshooting

My browser call returns no token.
Check that the site is enabled, the sitekey is correct, and the client can reach the engine. Offer a retry when a network or device check fails.
The token has a valid signature but access is denied.
Decode the signed payload on your backend and inspect decision, sitekey, exp and pz. Rejected decisions are intentionally signed.
Why is the API returning 401?
Use the per-site api_key with Authorization: Bearer. Console account API keys and the engine master key are different credentials.
Where can I review traffic?
Use Events in the console or the site API. Record IDs let you inspect individual checks.

For help with an integration, contact the InstantBlock team. Include a sitekey and record ID, but never send a secret or API key.