AbuseZero

Evaluations

Evaluate a benefit claim, and read past evaluations, their evidence and their association graphs.

An evaluation decides one benefit claim. You send what you know about the user and the claim. AbuseZero checks it against your policy, your workspace's full claim history and every signal family, and returns one final result, allow or deny, within the evaluation's time limit.

Every evaluation is saved before its response is sent. It keeps the exact policy version it ran under, so you can always explain a past decision, even after your rules change.

EndpointScope
POST /v1/evaluationsevaluations:create
GET /v1/evaluationsevaluations:read
GET /v1/evaluations/{id}evaluations:read
GET /v1/evaluations/{id}/evidenceevaluation_evidence:read
GET /v1/evaluations/{id}/graphevaluations:read

To report what happened after an evaluation, see Outcomes.

The evaluation object

GET /v1/evaluations/{id} returns the full evaluation object.

FieldTypeDescription
objectstringAlways evaluation.
idstringThe evaluation's ID, starting with eval_.
benefitstring or nullThe benefit's key. See Benefits.
claim_idstring or nullThe claim ID you sent.
resultstringallow or deny. In observe mode this is always allow.
reasonobject or nullWhy the result was reached. See Reason.
outcomeobjectThe claim's outcome: state and due_at. See Outcomes.
policy_versioninteger or nullThe version of the policy the evaluation ran under.
created_atstringWhen the evaluation ran.
modestringprotect or observe: the benefit's protection mode when the evaluation ran.
observed_resultstringObserve mode only. The result your policy reached: allow or deny.
riskobjectThe AbuseZero Risk Score. See Risk.
signalsarrayEvidence only. The assessment of each signal family, returned by Retrieve an evaluation's evidence. See Signals.
inputsobjectWhich inputs the evaluation received. See Inputs.
browser_datastring or nullWhat the evaluation received from the browser SDK. See Browser data.
policyobjectThe policy the evaluation ran under: id (starting with pol_) and version.

Reason

FieldTypeDescription
codestringA stable reason code, such as prior_claim_email or custom_rule. See Reason codes.
messagestringA readable explanation you can show your team.
rulestringPresent when one of your own rules decided the result. The ID of that rule.

Risk

FieldTypeDescription
scorenumber or nullThe AbuseZero Risk Score, from 0 to 100. null when the result was decided without a score, for example by one of your rules, a list entry or a protection level.
bandstringlow, elevated, high or not_assessed. not_assessed means no score was produced. The result still stands.

Signals

An array with one entry for each signal family. It appears only in an evaluation's evidence, which needs the evaluation_evidence:read scope, and on the evaluation in the dashboard. Evaluation and preview responses don't include it.

FieldTypeDescription
familystringidentity, device, network, automation or benefit_history.
assessmentstringclear, elevated, high or unavailable. unavailable means the evaluation had no evidence for this family.

See Risk Score and signals.

Inputs

Each key is received or missing.

KeyReceived when you send
emailsubject.email
ipcontext.ip
user_idsubject.external_id. missing when the evaluation sent none. Without it, the email address still identifies the user. Evaluations recorded before 29 October 2026 read received.
phoneidentifiers.phone_hash
paymentidentifiers.payment
deviceidentifiers.device_id, or a device token with browser data
browseridentifiers.browser_cookie_id, or a device token with browser data

missing inputs are the first thing to check when an integration behaves unexpectedly.

Browser data

ValueMeaning
collectedBrowser data arrived with the evaluation.
consent_declinedThe visitor declined consent, so nothing was collected.
not_loadedYour consent tool kept the browser script from loading.
blockedThe visitor's browser or an extension blocked collection.
timed_outCollection didn't finish in time.
failedCollection failed in the browser.
site_not_listedThe page's site isn't in your site list. Add it in Developers → Browser setup.
sdk_outdatedThe page uses an outdated browser SDK. Update it.
expiredThe device token reached AbuseZero after it expired. Send it sooner.
not_passed_onBrowser data was collected for this claim, but the evaluation didn't include its device token. Pass deviceToken to your server and send it.
not_sentThe evaluation carried no device token.

You can fix site_not_listed, sdk_outdated and not_passed_on. The others are expected for some visitors, and evaluations still run without browser data.

Create an evaluation

POST /v1/evaluations · Scope evaluations:create · Requires Idempotency-Key

Also available as abusezero evaluations create in the AbuseZero CLI, abusezero_create_evaluation on the AbuseZero MCP server and evaluate() in the AbuseZero Node.js SDK.

Evaluates one benefit claim and returns allow or deny. Call it from your server before you grant the benefit. Each evaluation uses workspace credits.

Request body

FieldTypeRequiredDescription
benefitstringYesThe benefit's key. Lowercase letters, numbers and underscores, starting with a letter; up to 64 characters.
claim_idstringYesYour ID for this claim, 1 to 128 printable characters with no spaces. Use one claim ID per award. See Evaluating the same claim again.
subjectobjectYesThe user making the claim.
subject.emailstringYesThe user's email address, up to 254 characters.
subject.external_idstringNoYour stable ID for the user, 1 to 128 characters. Recommended. Without it, AbuseZero identifies the user by email.
subject.email_verifiedbooleanNotrue when your app has verified the email address.
contextobject or nullNoDetails of the request your user made.
context.ipstring or nullNoThe user's public IP address, IPv4 or IPv6. Send the user's address, not your server's. Private, loopback and documentation addresses return IP_NOT_ROUTABLE.
context.user_agentstringNoThe user's User-Agent, up to 512 characters.
context.session_idstringNoYour session ID for the user, 1 to 128 characters.
identifiersobjectNoFurther identifiers for the user and device.
identifiers.device_tokenstringNoThe deviceToken the browser SDK returned, up to 4,096 characters. Send it unchanged, whatever its status. Send unavailable:not_loaded when your consent tool kept the script from loading.
identifiers.device_idstringNoYour own device ID, such as a mobile installation ID, 1 to 128 characters.
identifiers.browser_cookie_idstringNoYour own first-party browser cookie ID, 1 to 128 characters.
identifiers.paymentobjectNoThe user's payment method. All three fields are required when you send it.
identifiers.payment.providerstringYes, in paymentThe name of your payment processor, 1 to 128 printable characters.
identifiers.payment.scopestringYes, in paymentThe account at that processor in which the fingerprint is unique, 1 to 128 printable characters.
identifiers.payment.fingerprintstringYes, in paymentThe processor's fingerprint for the payment method, 1 to 128 characters.
identifiers.phone_hashobjectNoThe user's phone number as a keyed hash. Create it with createPhoneHash from abusezero/server. Requires phone_verified.
identifiers.phone_verifiedbooleanWith phone_hashtrue when your app has verified the phone number. Send it together with phone_hash.
identifiers.customarrayNoUp to 10 custom identifiers, each { "key", "value_hash" }. Create each with createCustomIdentifier from abusezero/server. The key and its key version must be registered first.
agent_evidencestringNoThe opaque AgentEvidence token verifyAgentRequest returned for this claim. Send it unchanged. See Verify AI agents.

Phone numbers and custom identifiers travel as keyed hashes, so AbuseZero matches them without ever receiving the raw value. See Send identifiers.

Example

import { randomUUID } from "node:crypto";
import { AbuseZero } from "abusezero/server";

const abusezero = new AbuseZero({
  key: process.env.ABUSEZERO_SECRET_KEY!,
  outage: "throw",
});

// deviceToken is the value the browser SDK returned on your page.
export async function evaluateTrial(deviceToken: string) {
  // Reuse this key if you retry this attempt.
  const idempotencyKey = randomUUID();

  const evaluation = await abusezero.evaluate(
    {
      benefit: "pro_trial",
      claim_id: "trial_user_4821",
      subject: {
        external_id: "user_4821",
        email: "jordan@example.com",
        email_verified: true,
      },
      context: { ip: "203.0.113.42" },
      identifiers: { device_token: deviceToken },
    },
    idempotencyKey,
  );

  if ("status" in evaluation) {
    // Skipped: no allow or deny was decided.
  } else if (evaluation.result === "allow") {
    // Grant the trial.
  }
  return evaluation;
}

Replace the example IP address with the user's public address. 203.0.113.42 comes from a range reserved for documentation, and AbuseZero rejects documentation, private and other non-routable addresses with IP_NOT_ROUTABLE.

Response

Returns 200 with the evaluation result.

Allowed
{
  "object": "evaluation",
  "id": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
  "result": "allow",
  "risk": { "score": 10, "band": "low" },
  "reason": {
    "code": "default_allow",
    "message": "No rule matched, so the policy's default allowed this claim."
  },
  "outcome": { "state": "assumed_granted", "due_at": null }
}
Denied
{
  "object": "evaluation",
  "id": "eval_8a1d4f20-6c3b-4e57-b9d2-0f7e6a5c3b18",
  "result": "deny",
  "risk": { "score": null, "band": "not_assessed" },
  "reason": {
    "code": "prior_claim_email",
    "message": "An earlier claim used this email address."
  },
  "outcome": null
}
FieldTypeDescription
objectstringAlways evaluation.
idstringThe evaluation's ID. Store it with the claim: you need it to report the outcome.
resultstringallow or deny. Grant the benefit only on allow.
riskobjectThe AbuseZero Risk Score. See Risk.
reasonobject or nullWhy the result was reached. See Reason.
outcomeobject or nullFor an allowed claim, its outcome state and due_at. null for a denied claim.

When the claim's outcome is awaiting_outcome, report it by outcome.due_at. See Outcomes.

An allowed claim's response also carries its outcome in two headers, AbuseZero-Outcome-State and, when there is a due time, AbuseZero-Outcome-Due-At.

Re-evaluating a claim_id

A claim ID stands for one award of a benefit. AbuseZero keeps every claim consistent, however often you evaluate it:

  • The same Idempotency-Key and body returns the original response. Nothing is evaluated twice.
  • A new key for a claim that was allowed returns allow with the reason previously_authorized and the original evaluation's id.
  • A claim whose outcome you reported as not_granted is evaluated again from the start.
  • A claim ID already used by a different user returns CLAIM_CONFLICT. Use a new claim ID for each award.
  • A claim ID from an earlier period of a calendar-based benefit returns CLAIM_PERIOD_ENDED. Use a new claim ID for each period.
  • A claim whose outcome report is overdue returns OUTCOME_CONFLICT. Report its outcome before evaluating it again.

Skipped evaluations

When the workspace can't run an evaluation, the response is 200 with status: "skipped" and no result. No allow or deny was decided, so handle skips as their own branch.

{
  "object": "evaluation",
  "status": "skipped",
  "reason": "insufficient_credits"
}
reasonMeaning
insufficient_creditsThe workspace doesn't have enough credits for the evaluation.
billing_overdueThe workspace doesn't have enough credits while a payment is overdue.
service_inactiveThe workspace can't run evaluations, for example while it's being deleted.
benefit_pausedThe benefit is paused because the workspace is over its plan's limits.

New skip reasons can be added within v1. Handle an unknown reason like any other skip. The workspace owner manages credits, payments and plan limits in Workspace settings → Billing & Subscriptions.

Errors

StatusCodeWhen
422IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid.
422INVALID_FIELDA field is missing or invalid. error.field names it.
422INVALID_EMAILsubject.email isn't in a supported form. Quoted and international local parts aren't supported.
422INVALID_IPcontext.ip isn't a valid IPv4 or IPv6 address.
422IP_NOT_ROUTABLEcontext.ip is a private, loopback, documentation or other non-routable address. Send the user's public address, or leave it out.
422UNSUPPORTED_FIELDThe body has an unknown field, or a custom identifier and key version aren't registered in Developers → Custom identifiers.
422DEVICE_TOKEN_INVALIDidentifiers.device_token isn't a value the browser SDK returned.
422DEVICE_TOKEN_SCOPEThe device token was collected for a different claim ID.
422BROWSER_SDK_UPGRADE_REQUIREDThe device token came from an outdated browser SDK.
422BOT_EVIDENCE_REQUIREDThe benefit requires a bot check and the evaluation has none that can be used.
422AGENT_EVIDENCE_SCOPEagent_evidence was produced for a different claim.
422AGENT_EVIDENCE_OUTDATEDagent_evidence came from a Node.js SDK before 0.11.0. Update the SDK.
404BENEFIT_NOT_FOUNDNo benefit has this key.
409IDEMPOTENCY_CONFLICTThe key was already used with a different body.
409EVALUATION_PENDINGAn evaluation with this key is still running. Retry with the same key.
409CLAIM_CONFLICTThe claim ID belongs to a different user.
409CLAIM_PERIOD_ENDEDThe claim ID belongs to an earlier eligibility period.
409OUTCOME_CONFLICTThe claim's earlier approval expired before its outcome was reported.
409OUTCOME_ALREADY_RECORDEDAn imported outcome already exists for this claim.
409DEVICE_TOKEN_USEDThe device token was already used for another claim.
409DELETION_IN_PROGRESSThe user's history or the workspace is being deleted.
409POLICY_UNAVAILABLEThe benefit's policy isn't available.
413PAYLOAD_TOO_LARGEThe body is over 16 KiB.
429RATE_LIMITEDThe workspace is over a traffic limit. Wait for Retry-After.
503BOT_EVIDENCE_UNAVAILABLEThe bot check couldn't be completed. Collect new browser data and retry.
503TEMPORARILY_UNAVAILABLEAbuseZero couldn't finish the request. Retry with the same key.
504DEADLINE_EXCEEDEDThe evaluation reached its time limit. Retry with the same key.

List evaluations

GET /v1/evaluations · Scope evaluations:read

Also available as abusezero evaluations list in the AbuseZero CLI, abusezero_list_evaluations on the AbuseZero MCP server and evaluations() in the AbuseZero Node.js SDK.

Lists evaluations, newest first.

Query parameters

ParameterTypeRequiredDescription
limitintegerNoItems per page, 1 to 100. Defaults to 50.
cursorstringNoThe next_cursor from the previous page.
resultstringNoOnly allow or only deny evaluations.
benefitstringNoOnly evaluations for this benefit key.
querystringNoOnly evaluations with this claim ID, user ID (subject.external_id) or email address. Up to 254 characters.
daysintegerNoHow many days back to look, 1 to 90. Defaults to 7.

Example

const page = await abusezero.evaluations({ result: "deny", days: 30, limit: 20 });

Response

{
  "object": "list",
  "data": [
    {
      "object": "evaluation",
      "id": "eval_8a1d4f20-6c3b-4e57-b9d2-0f7e6a5c3b18",
      "benefit": "pro_trial",
      "claim_id": "trial_user_5307",
      "result": "deny",
      "reason": {
        "code": "prior_claim_email",
        "message": "An earlier claim used this email address."
      },
      "outcome": { "state": null },
      "policy_version": 3,
      "created_at": "2026-10-29T14:05:12.000Z"
    }
  ],
  "next_cursor": null
}

Each item is a summary. Retrieve an evaluation for its full detail.

Errors

StatusCodeWhen
422CURSOR_INVALIDThe cursor was changed or belongs to another list.
422INVALID_FIELDA query parameter is invalid.

Retrieve an evaluation

GET /v1/evaluations/{id} · Scope evaluations:read

Also available as abusezero evaluations get in the AbuseZero CLI, abusezero_get_evaluation on the AbuseZero MCP server and getEvaluation() in the AbuseZero Node.js SDK.

Returns the full evaluation object.

Example

const evaluation = await abusezero.getEvaluation("eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93");

Response

{
  "object": "evaluation",
  "id": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
  "benefit": "pro_trial",
  "claim_id": "trial_user_4821",
  "result": "allow",
  "reason": {
    "code": "default_allow",
    "message": "No rule matched, so the policy's default allowed this claim."
  },
  "outcome": { "state": "granted", "due_at": null },
  "policy_version": 3,
  "created_at": "2026-10-29T14:02:47.000Z",
  "mode": "protect",
  "risk": { "score": 10, "band": "low" },
  "inputs": {
    "email": "received",
    "ip": "received",
    "user_id": "received",
    "phone": "missing",
    "payment": "missing",
    "device": "received",
    "browser": "received"
  },
  "browser_data": "collected",
  "policy": {
    "id": "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
    "version": 3
  }
}

Errors

StatusCodeWhen
404EVALUATION_NOT_FOUNDNo evaluation with this ID exists in the workspace.
422INVALID_FIELDThe ID doesn't start with eval_.

Retrieve an evaluation's evidence

GET /v1/evaluations/{id}/evidence · Scope evaluation_evidence:read

Also available as abusezero evaluations evidence in the AbuseZero CLI, abusezero_get_evaluation_evidence on the AbuseZero MCP server and evaluationEvidence() in the AbuseZero Node.js SDK.

Returns the full evaluation object with its signal family assessments, plus the request details AbuseZero retained for it. With an OAuth access token, the signed-in member also needs permission to reveal evidence.

Example

const evaluation = await abusezero.evaluationEvidence("eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93");

Response

The evaluation object, with signals and one more field:

{
  "object": "evaluation",
  "id": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
  "result": "allow",
  "signals": [
    { "family": "identity", "assessment": "clear" },
    { "family": "device", "assessment": "clear" },
    { "family": "network", "assessment": "clear" },
    { "family": "automation", "assessment": "clear" },
    { "family": "benefit_history", "assessment": "clear" }
  ],
  "evidence": {
    "request": {
      "benefit": "pro_trial",
      "claim_id": "trial_user_4821",
      "subject": {
        "external_id": "user_4821",
        "email": "jordan@example.com",
        "email_verified": true
      },
      "context": { "ip": "203.0.113.42" }
    },
    "device_profile": {
      "status": "captured",
      "expires_at": "2026-11-28T14:02:47.000Z"
    }
  }
}

The example shows only some fields of the evaluation object.

FieldTypeDescription
evidence.requestobject or nullThe request body AbuseZero received. null once your workspace's evidence retention has removed it.
evidence.device_profile.statusstringcaptured when browser data from the evaluation is still retained, not_retained when it isn't.
evidence.device_profile.expires_atstring or nullWhen the retained browser data will be removed.

Errors

StatusCodeWhen
403PERMISSION_REQUIREDThe signed-in member can't reveal evidence.
404EVALUATION_NOT_FOUNDNo evaluation with this ID exists in the workspace.

Retrieve an evaluation's association graph

GET /v1/evaluations/{id}/graph · Scope evaluations:read

Also available as abusezero evaluations graph in the AbuseZero CLI, abusezero_get_evaluation_graph on the AbuseZero MCP server and evaluationGraph() in the AbuseZero Node.js SDK.

Returns the other outcomes for the same benefit that are linked to this evaluation's claim through shared identifiers. See Association graph.

An allowed claim's graph starts from its outcome. A claim with no outcome, such as a denied claim, starts from the identifiers its evaluation recorded, so a prior-claim denial shows the earlier outcomes behind it.

Example

const graph = await abusezero.evaluationGraph("eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93");

Response

{
  "object": "evaluation_links",
  "evaluation": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
  "links": [
    { "outcome": "out_9e7c5a31-2d4b-4f86-b0a9-6c1e8d3f5a72", "family": "identity" },
    { "outcome": "out_9e7c5a31-2d4b-4f86-b0a9-6c1e8d3f5a72", "family": "device" },
    { "outcome": "out_1f3a5c7e-9b2d-4e6f-8a0c-3d5e7f9a1b24", "family": "network" }
  ],
  "linked_outcomes": 2,
  "complete": true
}
FieldTypeDescription
objectstringAlways evaluation_links.
evaluationstringThe evaluation's ID.
linksarrayOne entry for each link: the linked outcome ID and the signal family that links them, identity, device, network or benefit_history. Device and browser identifiers both report device. One outcome can be linked in more than one way.
linked_outcomesintegerHow many distinct outcomes are linked.
completebooleanfalse when the graph is too large to return in full.

A link shows an observed association. A shared device or network doesn't by itself mean two claims came from one person.

Errors

StatusCodeWhen
404EVALUATION_NOT_FOUNDNo evaluation with this ID exists in the workspace.

How is this guide?

Last updated on

On this page