AbuseZero

Set up browser protection

List your sites, load the browser SDK, and send its device token with every evaluation.

Browser protection adds device and automation evidence to every evaluation. The AbuseZero browser SDK collects it on your pages, and your server passes the result to AbuseZero as one opaque value: the device token. Evaluations still run without it, and the response always says whether browser data arrived. For how it fits together, see Browser protection.

Before you begin

  • A publishable key (pk_…) from Developers → API keys. It can only send browser data, so it is safe to use in page code.
  • A server that already evaluates claims. See Protect a benefit end to end.
  • A server key with the scopes browser_collection:read, browser_collection:write and evaluations:create.
  • The HTTPS origins of the pages where people claim benefits.

List your sites

AbuseZero accepts browser data only from HTTPS sites in your site list. Add them in Developers → Browser setup, or with PATCH /v1/browser-collection in the AbuseZero REST API. The AbuseZero CLI command is abusezero browser-collection update, and the AbuseZero MCP server tool is abusezero_update_browser_collection. You can list up to 20 origins. An update replaces the whole list, so send the current revision to avoid overwriting a change made at the same time. A revision that is no longer current returns REVISION_CONFLICT (see REST API errors).

import { abusezero } from "./lib/abusezero";

const current = await abusezero.browserCollection() as { origins: string[]; revision: number };

await abusezero.updateBrowserCollection({
  origins: [...current.origins, "https://app.example.com"],
  revision: current.revision,
}, `browser-sites:${current.revision}`);

Pages on a site that isn't listed return the device token unavailable:site_not_listed. See Your sites.

Allow the SDK in your Content Security Policy

If your site sets a Content Security Policy, copy the sources shown in Developers → Browser setup into it. Without them, the browser can block the SDK, and evaluations arrive without browser data.

Load the browser SDK

Load the SDK once on every page where people claim a benefit. Choose the package if you use a bundler, or the standalone browser script if you don't.

npm install abusezero
app/abusezero-browser.ts
import { init } from "abusezero/browser";

init({ key: "pk_…" });

init tells AbuseZero, once per page load, that the SDK is installed on this site. It sends only the site's address and the SDK version, never visitor data. Browser setup uses it to show where the SDK is running.

Keep browser and server code apart. Import abusezero/browser in page code only, and never put a secret key in the browser.

Collect browser data for the claim

When the person submits the claim, call observe with the same claim_id your server will evaluate, then send the deviceToken to your server with the form.

app/trial-form.ts
import { observe } from "abusezero/browser";

export async function submitTrial(claimId: string, email: string) {
  const { deviceToken } = await observe({ key: "pk_…", claimId });

  await fetch("/api/trial", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ claimId, email, deviceToken }),
  });
}

In the standalone example, your server renders the claim ID into the form as data-claim-id.

observe always returns a deviceToken. When browser data is available, the token carries it. When it isn't, the token carries the reason, such as unavailable:timed_out. Send it either way. Unavailable reasons lists every reason.

A device token belongs to one claim. A token collected for another claim_id returns DEVICE_TOKEN_SCOPE (invalid request), and a token already used by another claim returns DEVICE_TOKEN_USED (conflict).

Collection runs by default. When you pass a consent callback and it resolves to false, nothing is collected and the token reports unavailable:consent_declined.

const { deviceToken } = await observe({
  key: "pk_…",
  claimId,
  consent: () => consentTool.allows("security"),
});

consentTool.allows stands for your consent tool's own check.

  • If your consent tool keeps the SDK from loading at all, send unavailable:not_loaded as the device token.
  • When a person withdraws consent, call forgetBrowser(). It removes what the SDK stored in that browser.

Send the device token with the evaluation

On your server, pass the token unchanged as identifiers.device_token in evaluate() or POST /v1/evaluations.

app/api/trial/route.ts
import { abusezero } from "@/lib/abusezero";

export async function POST(request: Request) {
  const body = await request.json() as { claimId: string; email: string; deviceToken: string };

  const evaluation = await abusezero.evaluate({
    benefit: "pro_trial",
    claim_id: body.claimId,
    subject: { email: body.email },
    identifiers: { device_token: body.deviceToken },
  }, `evaluate:${body.claimId}`);

  return Response.json({ evaluation });
}

A real integration sends the signed-in user's ID and every other identifier it has, as in Protect a benefit end to end.

Verify your integration

Open a page with the SDK, then read your browser setup with GET /v1/browser-collection, or abusezero browser-collection get in the AbuseZero CLI.

curl https://api.abusezero.com/v1/browser-collection \
  -H "Authorization: Bearer $ABUSEZERO_SECRET_KEY"
Response (abridged)
{
  "object": "browser_collection",
  "origins": ["https://app.example.com"],
  "revision": 2,
  "sites": [
    {
      "origin": "https://app.example.com",
      "listed": true,
      "first_seen_at": "2026-10-29T14:02:11.000Z",
      "last_seen_at": "2026-10-29T14:31:47.000Z",
      "sdk_version": "0.11.0"
    }
  ],
  "issue": null,
  "issue_since": null
}

Then submit a claim and open its evaluation with GET /v1/evaluations/{id}. browser_data is collected. If it isn't, the value tells you what to fix:

browser_dataWhat to do
not_passed_onYour page collected a token, but your server didn't send it. Pass deviceToken through to identifiers.device_token.
site_not_listedAdd the page's origin to your site list.
sdk_outdatedUpdate the abusezero package or the standalone script address.
not_sentThe evaluation carried no device token. Send one with every evaluation.
expiredSend the token sooner after the SDK returns it.

consent_declined, not_loaded, blocked, timed_out and failed are expected for some visitors. They don't need a fix. Troubleshooting covers every status. Browser setup shows the share of each status over the last seven days.

How is this guide?

Last updated on

On this page