Developer Resources

Integrations & SDKs

Collect browser evidence, forward it from your backend, and decide how your application handles allow, review and block.

Reviewed

Browser
SDK collects evidence for a visitor check
Backend
Your server keeps the API key
Policy
Your application enforces the result

Collect in the browser. Decide on your server.

A full visitor check needs evidence from the browser SDK and a server request to /v1/evaluate. A server HTTP client alone cannot collect device evidence. An IP lookup has narrower coverage.

Visitor evaluation
MethodPOST
URLhttps://maskbreak.com/v1/evaluate
AuthAuthorization: Bearer YOUR_API_KEY — backend only
Request{ "token": "<browser token>", "fingerprintEventId": "<browser device event>" }
Responsestatus, decision, risk_score, ip, network, device, reasons; test/sandbox/sample/degraded flags when applicable

Connect your existing form

Load sentinel.js on the page containing the action. For JSON submissions, check that window.Sentinel exists, call await Sentinel.collect() and send the token and fingerprintEventId it returns alongside your form fields to your own backend; never hold the form in the browser when the script is missing. Collection can still return a missing network token when the SDK is blocked or slow, and your server decides what a missing value means.

For SDK-enriched HTML forms, the hidden inputs are monocle and sentinel_fp; map them to token and fingerprintEventId on your backend. The Node example below expects JSON. Keep the live API key in a server secret. Only the two verification values go to Maskbreak; passwords, payment data and other application fields stay with your application.

The API permits network-only checks, but the strict policy below requires both network and device evidence. A missing device result can mean the event lookup failed, even if decision says allow. Incomplete checks pause the protected action.

Continue only on a complete live allow

This guard is the enforce step. Start with the watch mode in the integration guide: it sends every submission to Maskbreak, also one with a missing field, and blocks nobody, so the dashboard can say which half did not arrive. Switch to this guard once your Events look right. Use this Node.js 22 guard in an existing Express app. Replace your route registration with the one shown, preserving your existing authentication, authorization, CSRF and business handler. Define app, express and existingLoginHandler in your application, and set MASKBREAK_API_KEY to your live server key.

Node.js 22 / Express
const object = value =>
  value !== null && typeof value === 'object' && !Array.isArray(value);
const liveFlags = value => ['test', 'sandbox', 'sample', 'degraded'].every(
  key => value[key] === undefined || value[key] === false
);
const booleans = (value, keys) =>
  object(value) && keys.every(key => typeof value[key] === 'boolean');
const validToken = (value, max) =>
  typeof value === 'string' && value.length > 0 && value.length <= max
  && !/\s/.test(value) && !/^(test|sample|sandbox)(?:_|$)/i.test(value);

async function maskbreakGuard(req, res, next) {
  res.set('Cache-Control', 'private, no-store');
  res.set('CDN-Cache-Control', 'no-store');
  const refuse = (status, error) => res.status(status).json({ error });
  const key = process.env.MASKBREAK_API_KEY;
  if (typeof key !== 'string' || !/^sk_live_[A-Za-z0-9_-]{1,72}$/.test(key)) {
    return refuse(503, 'Verification unavailable');
  }
  const { token, fingerprintEventId } = req.body || {};
  if (!validToken(token, 32768) || !validToken(fingerprintEventId, 256)) {
    return refuse(403, 'Browser verification required');
  }

  try {
    const response = await fetch('https://maskbreak.com/v1/evaluate', {
      method: 'POST',
      headers: {
        Authorization: 'Bearer ' + key,
        'Content-Type': 'application/json',
        Accept: 'application/json'
      },
      body: JSON.stringify({ token, fingerprintEventId }),
      signal: AbortSignal.timeout(3000),
      redirect: 'error',
      cache: 'no-store'
    });
    if (!response.ok) {
      // The month's checks are used up (HTTP 429, code monthly_quota_exceeded):
      // continue unchecked until the 1st (00:00 UTC), logged, so a used-up
      // month never takes the form offline. Every other error holds.
      const refusal = response.status === 429 ? await response.json().catch(() => null) : null;
      if (!object(refusal) || refusal.code !== 'monthly_quota_exceeded') {
        return refuse(503, 'Verification unavailable');
      }
      console.warn('[maskbreak] monthly_quota_exceeded: continuing unchecked until the 1st');
    } else {
      const result = await response.json();
      const valid = object(result) && liveFlags(result)
        && result.status === 'success'
        && ['allow', 'review', 'block'].includes(result.decision)
        && Number.isFinite(result.risk_score)
        && result.risk_score >= 0 && result.risk_score <= 100
        && [result.details, result.network, result.device, result.deviceIntel].every(
          part => part === undefined || (object(part) && liveFlags(part))
        );
      if (!valid) return refuse(503, 'Verification unavailable');
      if (result.decision === 'block') return refuse(403, 'Request blocked');
      if (result.decision === 'review') {
        return refuse(409, 'Additional verification required');
      }
      const complete = typeof result.ip === 'string'
        && result.ip.trim() !== '' && result.ip !== 'unknown'
        && booleans(result.network,
          ['vpn', 'proxy', 'datacenter', 'anonymous', 'tor', 'residential'])
        && booleans(result.device,
          ['antidetect', 'automation', 'emulator', 'virtual_machine',
           'incognito', 'privacy_mode', 'ip_blocklisted', 'high_activity'])
        && Number.isFinite(result.device.tampering_score);
      if (!complete) return refuse(503, 'Verification unavailable');
    }
  } catch {
    return refuse(503, 'Verification unavailable');
  }
  return next();
}

app.post('/your-login-endpoint',
  express.json({ limit: '64kb' }), maskbreakGuard, existingLoginHandler);
Sandbox only — non-production

This public key and token return synthetic results. They do not check a real visitor and must never authorize a login, order or payment. The production guard rejects them.

curl --fail-with-body --max-time 3 \
  'https://maskbreak.com/v1/evaluate' \
  -H 'Authorization: Bearer sk_test_sandbox' \
  -H 'Content-Type: application/json' \
  --data '{"token":"test_clean"}'

Choose what happens when a check cannot pass

This guard calls next() only after a complete live allow, so your existing handler still decides whether credentials and the action are valid. It returns 403 for a block or missing browser evidence, 409 for review, and 503 for test results, degradation, incomplete allows, HTTP failures, malformed responses or a timeout. One answer continues: when the month’s checks are used up (HTTP 429 with the code monthly_quota_exceeded), the guard writes a log line and calls next() until the 1st (00:00 UTC), so a used-up month, or a bot flood that used it up, never takes your form offline. The three-second deadline includes reading the response body; it is an application policy, not a latency promise. There are no automatic retries.

Build a real review or step-up flow for 409; displaying that status alone does not implement one. Keep rate limits, replay protection, and binding evidence to the session and action in your application. This guard does not establish token freshness or bind the two evidence values to each other. Test your CSP, SDK collection, authentication and outage experience in staging before enforcing it. Keep sensitive responses uncached and redact tokens and credentials from application logs.

See sandbox scenarios, timeout handling, and rollout guidance.

Server SDKs and other languages

Server SDKs wrap HTTP calls; they do not replace browser collection or your enforcement policy. Install the current release: since Node 0.3.3, Python 0.2.5 and PHP 0.1.3 they read MASKBREAK_API_KEY by themselves, and each README carries the same three setup steps as the integration guide. Language and platform guidance here is not a claim that every sample has been tested in your stack.

  • Node.js: npm install @sentinelsup/sdk — npm / source.
  • Python: pip install sentinelsup — PyPI / source.
  • PHP: composer require sentinelsup/sdk — Packagist / source.

For Python, PHP, Go, Ruby, Java or another backend, use the HTTP contract with a JSON serializer and a secret from your server environment. Forward only the two evidence fields. Set connection and response deadlines, refuse redirects, check HTTP status, catch transport and JSON errors, and apply the same review, live-result and completeness checks before invoking your existing handler.

Python's HTTP client documentation and PHP's cURL documentation describe the transport APIs. Adapt these to your framework and verify its error and timeout behavior; these links are implementation guidance, not complete middleware snippets.

Connect your framework

These framework guides provide starting points. Apply the live-result policy above and verify browser collection and your framework's own security and error handling.

Plan around your platform's boundaries

These integration notes describe where checks can run and what your application must supply.

Cloudflare Workers

Use the existing Worker setup guide, worker.mjs and wrangler.jsonc. The example guards a sensitive POST route in front of your existing origin. In watch mode, the default, it logs what enforce mode would do and forwards the request; with MASKBREAK_MODE=enforce it forwards the original request only after a complete live allow, or unchecked, logged, while the month’s checks are used up.

Follow its browser-field mapping, route and secret setup, origin restrictions and failure policy. The origin retains authentication, authorization, CSRF, rate limiting and the actual business action. Validate those boundaries in your staging zone. The Cloudflare Workers guide explains what the edge can and cannot see, with a shorter Worker that sends the form's monocle, sentinel_fp and sentinel_tz on every check and refuses only a block in enforce mode.

Shopify

An orders/create webhook arrives after the order is created. Use it for post-order review, with verified webhook signatures and duplicate handling. It cannot act as a pre-order gate. See Shopify's webhook documentation.

Installing a script in a storefront theme does not automatically add browser tokens to order metadata or run it in checkout, and a theme alone cannot call Maskbreak: the key stays on a backend of yours. The Shopify guide writes the browser evidence into cart attributes when a shopper presses Checkout on the cart form, sends the check from the order webhook in watch mode, and in enforce mode tags review and block orders to hold before fulfilment, and marks the orders that arrived without evidence. It cannot refuse the order itself. Do not forward the entire order payload to Maskbreak.

Pre-checkout enforcement needs a supported checkout extension or validation design. Shopify Functions have restricted external network access and eligibility requirements; an arbitrary HTTP call is not available in every shop or function. Check Shopify's network-access requirements before choosing that design.

WordPress and WooCommerce

The WooCommerce guide covers the classic checkout (the [woocommerce_checkout] shortcode) and My Account registration. It loads the script on those pages, adds class="monocle-enriched" to the <form> elements themselves (never to an input, which stays empty) and sends the check from woocommerce_after_checkout_validation, before the payment gateway runs.

The Cart and Checkout blocks post through WooCommerce's Store API, where those hooks do not run, so the guide's plugin does not check those orders. Keep the cart, checkout and account pages out of every page cache, and keep the key in wp-config.php, not in the options table.

Stripe

Run the browser-evidence check on your server before permitting the protected payment action. A review, block or unavailable result must pause that action. Keep both Stripe and Maskbreak secret keys on the backend.

Authenticate the customer, authorize access to the cart and protect cookie-authenticated endpoints against CSRF. Calculate the amount and currency from server-authoritative products, prices, discounts and taxes; never trust a submitted amount. Bind the check to that customer and cart, and use idempotency for payment creation and retries.

Preserve Stripe's payment lifecycle, webhook verification and Strong Customer Authentication (SCA), including 3D Secure. A Maskbreak allow does not replace them or guarantee a payment is safe. Follow Stripe's Payment Intents documentation.

iOS (Swift) and Android (Kotlin)

There is no shipped native Maskbreak SDK for iOS or Android. Never embed a live API key in an app binary or call the authenticated evaluation endpoint directly from the app. Native HTTP requests alone do not provide the browser evidence required by the full visitor check.

A web-based flow in a WebView needs the browser SDK, JavaScript and a compatible Content Security Policy (CSP). Verify collection of both evidence values in each supported WebView, then send them to your own backend for evaluation and enforcement. A WebView is not a guarantee of device-signal coverage. See browser setup, Apple's WKWebView documentation and Android's WebView guide.

MCP — IP lookup and status

The hosted HTTP MCP endpoint provides IP lookup and service-status tools. It does not collect browser evidence or perform full visitor checks. Client transport and authentication configuration vary; use the MCP API reference for the endpoint, tool list and authentication requirements.

Privacy and consent assessment

Before loading browser collection, assess the storage and access technologies used in your deployment. Document whether a PECR exemption applies to the specific fraud-prevention purpose, its necessity and proportionality; if no exemption applies, obtain valid consent before collection. Avoiding cookies does not by itself remove consent requirements. See the ICO's exemption guidance.

Provide a clear notice covering browser and network data, purpose, recipients and retention. Assess the relevant data-protection lawful basis and your controller responsibilities separately. Review the DPA, Cookie Policy and Privacy Policy; an integration example cannot establish compliance for your deployment.

Storage on your site

When your pages load /assets/sentinel.js, the SDK and the network and device checks it loads use the browser storage below, for fraud prevention and security only. None of it is used for advertising or analytics, and the values are opaque: no name, email address or account details. List it in your own cookie policy and privacy notice, and decide whether your rules on storing or accessing information on a visitor’s device require consent before the SDK runs (DPA Section 3).

NameWherePurposeLifetime
_sntl_mcl_vlocalStorage on your siteWritten by the SDK itself: a version marker so that network-check storage left by an older setup is cleared once. Holds only a version number.Until cleared
_vid_tCookie on your site and localStorageDevice check: a pseudonymous identifier so the same browser can be recognised on a later visit.Cookie up to 1 year; localStorage until cleared
_vid_lrlocalStorage on your siteDevice check: state it keeps between visits.Until cleared
_iidtCookie for maskbreak.com (HttpOnly)Device check: a pseudonymous identifier set by the device check’s server on a maskbreak.com host. From your site it is a third-party cookie; browsers that block third-party cookies do not keep it.Up to 1 year
Network-check statelocalStorage or IndexedDB on your siteThe network check may keep its own session state between page loads. It also reads browser characteristics (user agent, languages, time zone, window size, processor count, an automation flag) and measures the connection.Until cleared

Both checks send what they collect, with the visitor’s IP address, to our network-intelligence and device-intelligence providers in the United States; the sub-processor list describes them. Separately, Maskbreak keeps a pseudonymous, service-wide count of how often a device has been seen (the times_seen field), described under “Device sighting counters” in our Privacy Policy, which you can link from your own notice. Names can change when a detection script is updated: check your own pages in the browser’s developer tools before you publish your cookie policy.

Get your free API key

Start with synthetic sandbox scenarios, then verify your integration in staging. See current plans and limits.