Previews, simulations and replays
Try a request or a policy change against the real evaluation engine before it reaches your users.
A preview runs the same evaluation as live traffic without recording a claim. Simulations and replays reuse the evidence your past evaluations retained and apply a policy to it, so a changed result comes from your policy change. Use them to see how AbuseZero will decide before anything changes for a real user.
| Endpoint | Scope | What it does | Uses credits |
|---|---|---|---|
POST /v1/evaluation-previews | simulations:run | Runs one full evaluation and records nothing. | Yes |
POST /v1/simulations | simulations:run | Runs a benefit's recent claims, in sequence, under a policy you choose. | No |
POST /v1/replays | simulations:run | Re-decides a benefit's recent evaluations under a policy you choose, and shows which results would change. | No |
Preview an evaluation
POST /v1/evaluation-previews · Scope simulations:run · Requires Idempotency-Key
Also available as abusezero evaluations preview in the AbuseZero CLI, abusezero_preview_evaluation on the AbuseZero MCP server and previewEvaluation() in the AbuseZero Node.js SDK.
Runs a complete evaluation and returns the result without recording anything. The preview creates no evaluation, claim or outcome, and never affects a later evaluation.
A preview is a real evaluation and uses workspace credits, like POST /v1/evaluations. Previews and Test console runs also have their own rate limit, separate from evaluations. Over it, a preview returns 429 RATE_LIMITED with a Retry-After header.
Request body
The same body as Create an evaluation.
Example
import { randomUUID } from "node:crypto";
const preview = await abusezero.previewEvaluation(
{
benefit: "pro_trial",
claim_id: "preview_user_4821",
subject: { external_id: "user_4821", email: "jordan@example.com" },
context: { ip: "203.0.113.42" },
},
randomUUID(),
);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 201.
{
"object": "evaluation_preview",
"preview": true,
"result": "deny",
"risk": { "score": null, "band": "not_assessed" },
"reason": {
"code": "prior_claim_user",
"message": "This user already received this benefit."
},
"inputs": {
"email": "received",
"ip": "received",
"user_id": "received",
"phone": "missing",
"payment": "missing",
"device": "missing",
"browser": "missing"
}
}| Field | Type | Description |
|---|---|---|
object | string | Always evaluation_preview. |
preview | boolean | Always true. |
result | string | allow or deny: the result a real evaluation would return now. |
risk | object | The AbuseZero Risk Score. See Evaluations. |
reason | object or null | Why the result was reached. See Reason codes. |
inputs | object | Which inputs the preview received. |
A preview can also return { "object": "evaluation_preview", "status": "skipped", "reason": … } when the workspace can't run it. See Skipped evaluations.
Errors
| Status | Code | When |
|---|---|---|
| 422 | IDEMPOTENCY_KEY_REQUIRED | The Idempotency-Key header is missing or invalid. |
| 422 | INVALID_FIELD | A field is missing, invalid or not defined for this endpoint. error.field names it. |
| 422 | INVALID_EMAIL | subject.email isn't in a supported form. |
| 422 | INVALID_IP | context.ip isn't a valid IPv4 or IPv6 address. |
| 422 | IP_NOT_ROUTABLE | context.ip is a private, loopback, documentation or other non-routable address. Send the user's public address, or leave it out. |
| 422 | UNSUPPORTED_FIELD | A custom identifier and key version aren't registered. |
| 422 | DEVICE_TOKEN_INVALID | identifiers.device_token isn't a value the browser SDK returned. |
| 422 | DEVICE_TOKEN_SCOPE | The device token was collected for a different claim ID. |
| 422 | BROWSER_SDK_UPGRADE_REQUIRED | The device token came from an outdated browser SDK. |
| 422 | BOT_EVIDENCE_REQUIRED | The benefit requires a bot check and the preview has none that can be used. |
| 422 | AGENT_EVIDENCE_SCOPE | agent_evidence was produced for a different claim. |
| 422 | AGENT_EVIDENCE_OUTDATED | agent_evidence came from a Node.js SDK before 0.11.0. Update the SDK. |
| 404 | BENEFIT_NOT_FOUND | No benefit has this key. |
| 409 | IDEMPOTENCY_CONFLICT | The key was already used with a different body. |
| 409 | EVALUATION_PENDING | A preview with this key is still running. Retry with the same key. |
| 409 | CLAIM_CONFLICT | The claim ID belongs to a different user. |
| 409 | DEVICE_TOKEN_USED | The device token was already used for another claim. |
| 409 | DELETION_IN_PROGRESS | The user's history or the workspace is being deleted. |
| 409 | POLICY_UNAVAILABLE | The benefit's policy isn't available. |
| 413 | PAYLOAD_TOO_LARGE | The body is over 256 KiB. |
| 429 | RATE_LIMITED | The workspace is over a traffic limit. Wait for Retry-After. |
| 503 | BOT_EVIDENCE_UNAVAILABLE | The bot check couldn't be completed. Collect new browser data and retry. |
| 504 | DEADLINE_EXCEEDED | The preview reached its time limit. Retry with the same key. |
Run a simulation
POST /v1/simulations · Scope simulations:run
Also available as abusezero simulations run in the AbuseZero CLI, abusezero_simulate_sequence on the AbuseZero MCP server and simulate() in the AbuseZero Node.js SDK.
Runs a benefit's most recent evaluations again, one after another, under the policy you choose, as if that policy had been active at the time. The sequence starts from an empty history, and each step sees the claims the simulation allowed before it, so a simulation shows how a policy handles repeat claims across a sequence.
Simulations use evaluations whose request details are still retained. They apply the chosen policy and the benefit's current settings, not your allow and block list entries. They use no credits and change nothing.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
benefit_id | string | Yes | The benefit's ID, starting with ben_. |
policy_key | string | Yes | The key of the policy to simulate: a protection level (lenient, balanced or strict) or one of your custom policies. Up to 64 characters. |
Example
const simulation = await abusezero.simulate({
benefit_id: "ben_5b2e9c41-7d3a-4f68-a1c0-9e8d7b6a5f24",
policy_key: "strict",
});Response
Returns 201.
{
"object": "simulation",
"data": [
{
"step": 1,
"evaluation": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
"result": "allow",
"reason": {
"code": "default_allow",
"message": "No rule matched, so the policy's default allowed this claim."
}
},
{
"step": 2,
"evaluation": "eval_8a1d4f20-6c3b-4e57-b9d2-0f7e6a5c3b18",
"result": "deny",
"reason": {
"code": "prior_claim_email",
"message": "An earlier claim used this email address."
}
}
]
}| Field | Type | Description |
|---|---|---|
object | string | Always simulation. |
data[].step | integer | The step's position in the sequence, starting at 1. |
data[].evaluation | string | The original evaluation the step re-ran. |
data[].result | string | allow or deny under the simulated policy. |
data[].reason | object or null | Why the simulated policy reached that result. |
The benefit's current protection mode applies. In observe mode, every result is allow.
Errors
| Status | Code | When |
|---|---|---|
| 404 | POLICY_NOT_FOUND | The benefit or the policy doesn't exist in this workspace. |
| 422 | INVALID_FIELD | benefit_id or policy_key is missing or invalid. |
Run a replay
POST /v1/replays · Scope simulations:run
Also available as abusezero simulations replay in the AbuseZero CLI, abusezero_replay_evaluations on the AbuseZero MCP server and replay() in the AbuseZero Node.js SDK.
Re-decides a benefit's most recent evaluations under the policy you choose, using what each evaluation recorded, and shows every result that would change. Save a change as a draft policy and replay it before you assign it to a benefit.
Replays make no new lookups. They apply the chosen policy and the benefit's current settings to each evaluation's retained record, not your allow and block list entries. They use no credits and change nothing.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
benefit_id | string | Yes | The benefit's ID, starting with ben_. |
policy_key | string | Yes | The key of the policy to replay: a protection level (lenient, balanced or strict) or one of your custom policies. Up to 64 characters. |
Example
const replay = await abusezero.replay({
benefit_id: "ben_5b2e9c41-7d3a-4f68-a1c0-9e8d7b6a5f24",
policy_key: "trial_protection",
});
const rows = replay.data as { evaluation: string; changed: boolean }[];
const changed = rows.filter((row) => row.changed);Response
Returns 201.
{
"object": "replay",
"limit": 500,
"data": [
{
"evaluation": "eval_3f6c2a1e-8b4d-4c7a-9e2f-5d1b7a0c4e93",
"original": "allow",
"replayed": "allow",
"changed": false
},
{
"evaluation": "eval_d2b7f9a4-5e1c-4a38-9f6d-7c3e1b8a0d52",
"original": "allow",
"replayed": "deny",
"changed": true
}
]
}| Field | Type | Description |
|---|---|---|
object | string | Always replay. |
limit | integer | The most evaluations one replay covers. The newest evaluations are replayed first. |
data[].evaluation | string | The evaluation that was replayed. |
data[].original | string | The result it received. |
data[].replayed | string | The result under the chosen policy. |
data[].changed | boolean | true when the two results differ. |
The benefit's current protection mode applies. In observe mode, every replayed result is allow.
Errors
| Status | Code | When |
|---|---|---|
| 404 | POLICY_NOT_FOUND | The benefit or the policy doesn't exist in this workspace. |
| 422 | INVALID_FIELD | benefit_id or policy_key is missing or invalid. |
Next steps
How is this guide?
Last updated on