AbuseZero

Policies

Protection levels, custom policies, rule format, versions and the conditions available to your rules.

A policy decides the claims of every benefit that uses it. Every workspace has three protection levels, Lenient, Balanced and Strict, which AbuseZero defines and maintains. You can use a level as it is, or build a custom policy on one with your own rules.

With an OAuth access token, policies:write also needs the member's Publish policies and benefits permission.

The policy object

FieldTypeDescription
objectstringAlways policy.
idstringThe policy's ID, starting with pol_.
keystringThe key benefits use to select the policy.
namestringThe policy's display name.
kindstringmanaged for a protection level, custom for your own policy.
active_versionintegerThe version evaluations use now.
created_atstringWhen the policy was created.
updated_atstringWhen the policy last changed.

Protection levels

Protection levels appear in every workspace as managed policies with the keys lenient, balanced and strict. AbuseZero defines and maintains what each level contains. Levels are read-only: to change one, duplicate it and edit the copy.

See Protection levels for how to choose one.

Custom policy definition

A custom policy is a definition you write. It can build on a protection level.

FieldTypeRequiredDescription
basestringNoThe protection level to build on: lenient, balanced or strict.
default_resultstringYesallow or deny: the result when nothing else decides.
risk_thresholdinteger or nullNoA multiple of 5 from 5 to 100; other values return INVALID_FIELD. A claim whose AbuseZero Risk Score reaches this value is denied when no rule decides it. Leave it out or send null to keep the setting of your base level, or AbuseZero's standard setting when there is no base.
rulesarrayYesUp to 50 of your own rules. Send [] for none.

How a policy decides

  1. Your rules are checked in the order you list them. The first rule that matches decides the result.
  2. Your rules take priority over the protection level you build on.
  3. When no rule decides, a claim whose AbuseZero Risk Score reaches risk_threshold is denied.
  4. Otherwise, default_result applies.

Allow and block list entries decide a matching claim outright, whatever the policy says. Each evaluation's reason tells you what decided it. See Reason codes.

Rules

FieldTypeRequiredDescription
idstringYesYour ID for the rule, 1 to 64 characters, unique in the policy. The evaluation's reason.rule returns this ID when the rule decides a claim.
namestringYesA readable name, 1 to 100 characters. When you retrieve an evaluation the rule decided, its reason.message quotes this name.
resultstringYesallow or deny: the result when the rule matches.
whenobjectYesA condition, or a group of conditions.

Conditions and groups

A condition compares one value with your value:

{ "signal": "network.anonymous", "operator": "eq", "value": true }
FieldTypeRequiredDescription
signalstringYesA condition key from List conditions.
operatorstringYesSee Operators.
valuestring, number or booleanFor comparisonsThe value to compare with. Its type must match the condition's type. Leave it out for status operators.

Combine conditions with a group. all matches when every item matches. any matches when at least one item matches.

{
  "all": [
    { "signal": "network.anonymous", "operator": "eq", "value": true },
    { "any": [
      { "signal": "email.verified", "operator": "eq", "value": false },
      { "signal": "email.disposable", "operator": "eq", "value": true }
    ] }
  ]
}
  • A group holds 1 to 10 items.
  • Groups nest at most two levels deep.
  • A rule has at most 10 conditions in total.

Operators

OperatorMatches whenCondition types
eqThe value equals value.All
neqThe value differs from value.All
gtThe value is greater than value.Number
gteThe value is greater than or equal to value.Number
ltThe value is less than value.Number
lteThe value is less than or equal to value.Number
is_missingYou didn't send the input this condition needs.All
is_unknownAbuseZero received the input but couldn't determine a value for it.All
is_unavailableNo value was available when the evaluation ran.All

Comparison operators match only when the condition has a value. Use the status operators to write rules for inputs you didn't send or values that weren't available.

Create a policy

POST /v1/policies · Scope policies:write · Requires Idempotency-Key

Also available as abusezero policies create in the AbuseZero CLI, abusezero_create_policy on the AbuseZero MCP server and createPolicy() in the AbuseZero Node.js SDK.

Creates a custom policy at version 1.

Request body

FieldTypeRequiredDescription
namestringYesDisplay name, 1 to 80 characters.
keystringYesLowercase letters, numbers and underscores, starting with a letter; up to 64 characters. Unique in the workspace.
definitionobjectYesThe custom policy definition.

Example

import { randomUUID } from "node:crypto";

const policy = await abusezero.createPolicy(
  {
    name: "Trial protection",
    key: "trial_protection",
    definition: {
      base: "balanced",
      default_result: "allow",
      rules: [
        {
          id: "unverified_anonymous",
          name: "Unverified email on an anonymous network",
          result: "deny",
          when: {
            all: [
              { signal: "network.anonymous", operator: "eq", value: true },
              { signal: "email.verified", operator: "eq", value: false },
            ],
          },
        },
      ],
    },
  },
  randomUUID(),
);

Response

Returns 201.

{
  "object": "policy",
  "id": "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
  "revision": 1,
  "version": 1
}

To start using the policy, set it as a benefit's policy_key. To see its effect first, run a replay.

Errors

StatusCodeWhen
409POLICY_KEY_TAKENAnother policy uses this key.
409IDEMPOTENCY_CONFLICTThe key was already used with a different body.
422INVALID_FIELDThe definition is invalid, for example a duplicate rule ID, a reserved rule ID, a value of the wrong type, or groups nested too deeply. error.field gives the path to the problem, such as definition.rules.0.id.
422UNSUPPORTED_FIELDA rule uses a condition that isn't supported, or a custom identifier that isn't registered.
422POLICY_NOT_FOUNDbase isn't an available protection level.
422IDEMPOTENCY_KEY_REQUIREDThe Idempotency-Key header is missing or invalid.

List policies

GET /v1/policies · Scope policies:read

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

Lists the workspace's protection levels and custom policies, newest first.

Query parameters

ParameterTypeRequiredDescription
limitintegerNoItems per page, 1 to 100. Defaults to 50.
cursorstringNoThe next_cursor from the previous page.

Example

const page = await abusezero.policies({ limit: 50 });

Response

A list of policy objects.

Errors

StatusCodeWhen
422CURSOR_INVALIDThe cursor was changed or belongs to another list.

Retrieve a policy

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

Also available as abusezero policies get in the AbuseZero CLI, abusezero_get_policy on the AbuseZero MCP server and getPolicy() in the AbuseZero Node.js SDK.

Example

const policy = await abusezero.getPolicy("pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35");

Response

{
  "object": "policy",
  "id": "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
  "key": "trial_protection",
  "name": "Trial protection",
  "kind": "custom",
  "active_version": 2,
  "created_at": "2026-10-28T09:15:00.000Z",
  "updated_at": "2026-10-29T11:20:00.000Z"
}

To read the rules, retrieve the active version.

Errors

StatusCodeWhen
404RESOURCE_NOT_FOUNDNo policy with this ID exists in the workspace.
422INVALID_FIELDThe ID doesn't start with pol_.

Update a policy

PATCH /v1/policies/{id} · Scope policies:write · Requires Idempotency-Key

Also available as abusezero policies update in the AbuseZero CLI, abusezero_update_policy on the AbuseZero MCP server and updatePolicy() in the AbuseZero Node.js SDK.

Saves a new version of a custom policy. Send the complete definition. Every benefit that uses the policy evaluates with the new version from the moment the update succeeds. Earlier evaluations keep the version they ran under.

Request body

FieldTypeRequiredDescription
namestringYesDisplay name, 1 to 80 characters.
definitionobjectYesThe complete custom policy definition.
revisionintegerYesThe policy's current active_version.

Example

import { randomUUID } from "node:crypto";

await abusezero.updatePolicy(
  "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
  {
    name: "Trial protection",
    definition: { base: "strict", default_result: "allow", rules: [] },
    revision: 2,
  },
  randomUUID(),
);

Response

Returns 200.

{
  "object": "policy",
  "id": "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
  "revision": 3,
  "version": 3
}

Errors

StatusCodeWhen
404POLICY_NOT_FOUNDNo custom policy with this ID exists in the workspace.
409POLICY_CONFLICTrevision isn't the current version. Read the policy again.
409PROTECTION_LEVEL_READ_ONLYThe policy is a protection level. Duplicate it instead.
422INVALID_FIELDThe definition is invalid.
422UNSUPPORTED_FIELDA rule uses an unsupported condition or an unregistered custom identifier.

Duplicate a policy

POST /v1/policies/{id}/duplicate · Scope policies:write · Requires Idempotency-Key

Also available as abusezero policies duplicate in the AbuseZero CLI, abusezero_duplicate_policy on the AbuseZero MCP server and duplicatePolicy() in the AbuseZero Node.js SDK.

Creates a new custom policy from the active version of an existing one. Duplicating a protection level creates a custom policy built on that level, ready for your own rules.

Request body

FieldTypeRequiredDescription
namestringYesDisplay name for the copy, 1 to 80 characters.
keystringYesKey for the copy. Lowercase letters, numbers and underscores, starting with a letter; up to 64 characters.

Example

import { randomUUID } from "node:crypto";

const copy = await abusezero.duplicatePolicy(
  "pol_0e6b4d82-7a3c-4f19-8b5e-1d9c7a3f5e60",
  { name: "Balanced with my rules", key: "balanced_custom" },
  randomUUID(),
);

Response

Returns 201.

{
  "object": "policy",
  "id": "pol_b3f7d1a9-2c6e-4d85-9a0b-6e4c2f8d1b39",
  "revision": 1,
  "version": 1
}

Errors

StatusCodeWhen
404POLICY_NOT_FOUNDNo policy with this ID exists in the workspace.
409POLICY_KEY_TAKENAnother policy uses this key.

Delete a policy

DELETE /v1/policies/{id} · Scope policies:write · Requires Idempotency-Key

Also available as abusezero policies delete in the AbuseZero CLI, abusezero_delete_policy on the AbuseZero MCP server and deletePolicy() in the AbuseZero Node.js SDK.

Deleting a custom policy is permanent and removes all its versions. Move every benefit that uses it to another policy first. Protection levels can't be deleted.

Example

import { randomUUID } from "node:crypto";

await abusezero.deletePolicy("pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35", randomUUID());

Response

Returns 200.

{
  "object": "deleted",
  "id": "pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35",
  "deleted": true
}

Errors

StatusCodeWhen
404POLICY_NOT_FOUNDNo custom policy with this ID exists in the workspace. A retry with the same Idempotency-Key after a successful delete returns the original response instead.
409POLICY_IN_USEBenefits still use the policy. The message says how many.
409PROTECTION_LEVEL_READ_ONLYThe policy is a protection level.

List policy versions

GET /v1/policies/{id}/versions · Scope policies:read

Also available as abusezero policies versions in the AbuseZero CLI, abusezero_list_policy_versions on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.

Lists every version of a policy, newest first, in one page.

Example

const versions = await abusezero.call(
  "/v1/policies/pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35/versions",
);

Response

{
  "object": "list",
  "data": [
    { "object": "policy_version", "version": 2, "created_at": "2026-10-29T11:20:00.000Z" },
    { "object": "policy_version", "version": 1, "created_at": "2026-10-28T09:15:00.000Z" }
  ],
  "next_cursor": null
}

Retrieve a policy version

GET /v1/policies/{id}/versions/{version} · Scope policies:read

Also available as abusezero policies version in the AbuseZero CLI, abusezero_get_policy_version on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.

Returns one version. For a custom policy, the version includes the definition you saved. For a protection level, it names the level only: AbuseZero keeps each level's contents private.

Example

const version = await abusezero.call(
  "/v1/policies/pol_c4d8e2a7-1b5f-4a93-8e6d-2f0b9c7a1e35/versions/2",
);

Response

Custom policy
{
  "object": "policy_version",
  "version": 2,
  "managed": false,
  "definition": {
    "base": "balanced",
    "risk_threshold": 75,
    "default_result": "allow",
    "rules": [
      {
        "id": "unverified_anonymous",
        "name": "Unverified email on an anonymous network",
        "result": "deny",
        "when": {
          "all": [
            { "signal": "network.anonymous", "operator": "eq", "value": true },
            { "signal": "email.verified", "operator": "eq", "value": false }
          ]
        }
      }
    ]
  },
  "created_at": "2026-10-29T11:20:00.000Z"
}
Protection level
{
  "object": "policy_version",
  "version": 1,
  "managed": true,
  "level": "balanced",
  "created_at": "2026-10-01T08:00:00.000Z"
}
FieldTypeDescription
objectstringAlways policy_version.
versionintegerThe version number.
managedbooleantrue for a protection level.
definitionobjectCustom policies only. The definition saved with this version: base, risk_threshold, default_result and your rules. risk_threshold is null unless you set it yourself, so a definition you read can be saved back unchanged.
levelstringProtection levels only. lenient, balanced or strict.
created_atstringWhen the version was saved.

Errors

StatusCodeWhen
404POLICY_VERSION_NOT_FOUNDThe policy has no such version.

List conditions

GET /v1/policy-signals · Scope policies:read

Also available as abusezero policies signals in the AbuseZero CLI, abusezero_list_policy_signals on the AbuseZero MCP server and call() in the AbuseZero Node.js SDK.

Lists every condition your custom rules can use, in one page. Takes no parameters.

Example

const conditions = await abusezero.call("/v1/policy-signals");

Response

{
  "object": "list",
  "data": [
    { "object": "condition", "key": "email.verified", "label": "Email verified by your app", "type": "boolean" },
    { "object": "condition", "key": "user.prior_claims", "label": "Earlier claims by this user", "type": "number", "min": 0 },
    { "object": "condition", "key": "network.risk", "label": "Network risk", "type": "string", "values": ["low", "elevated", "high"] }
  ],
  "next_cursor": null
}

The example shows three of the conditions.

FieldTypeDescription
keystringThe value to use as signal in a condition.
labelstringA readable name.
typestringboolean, number or string. A comparison's value must have this type.
valuesarrayPresent when only certain values are allowed.
min, maxnumberPresent when numbers have a range.

Available conditions

KeyLabelType
email.verifiedEmail verified by your appboolean
email.disposableDisposable email addressboolean
phone.verifiedPhone verified by your appboolean
user.prior_claimsEarlier claims by this userwhole number, 0 or more
email.prior_claimsEarlier claims with this emailwhole number, 0 or more
device.prior_claimsEarlier claims from this devicewhole number, 0 or more
payment.prior_claimsEarlier claims with this payment methodwhole number, 0 or more
phone.prior_claimsEarlier claims with this phonewhole number, 0 or more
network.riskNetwork risklow, elevated or high
network.anonymousAnonymous networkboolean
network.repeat_attemptsRecent attempts from this networkwhole number, 0 or more
network.countryNetwork country (ISO code)string, such as DE
automation.detectedAutomation detectedboolean
custom.<key>.prior_outcomesEarlier claims with your custom identifier <key>whole number, 0 or more

For a custom identifier condition, replace <key> with the key of a registered custom identifier, such as custom.license_id.prior_outcomes. A custom identifier that a policy uses can't be deleted. New conditions can be added within v1.

How is this guide?

Last updated on

On this page