REST API errors
The AbuseZero REST API error envelope, error types and every error code.
Every error from the AbuseZero REST API uses the same envelope, on every endpoint. Your code can branch on type, act on code, and show message to your team.
Error envelope
{
"error": {
"type": "conflict",
"code": "IDEMPOTENCY_CONFLICT",
"message": "Use the original payload with this key, or a new key for a new evaluation.",
"retryable": false
},
"request_id": "7d0e5c2a-1f4b-4e8a-b6c3-9a2d5e0f1b74"
}| Field | Type | Description |
|---|---|---|
error.type | string | The error's category. See Error types. |
error.code | string | A stable, machine-readable code. Codes never change meaning within v1. |
error.message | string | A readable explanation with the next step. Messages can be reworded at any time, so don't parse them. |
error.field | string | The request field that caused the error, as a dotted path such as identifiers.device_token. Present only when one field caused it. |
error.retryable | boolean | true when the same request can succeed if you retry it unchanged. |
request_id | string | The request's ID. It matches the X-Request-ID response header. |
Error types
type | HTTP status | Meaning |
|---|---|---|
invalid_request | 400, 413, 422 | The request can't be accepted as sent. Fix it before retrying. |
authentication | 401 | The credential is missing, invalid or expired. |
permission | 403 | The credential is valid but can't do this. |
not_found | 404, 410 | The endpoint or object doesn't exist in this workspace, or is no longer available. |
conflict | 409 | The request conflicts with the current state of an object. |
rate_limit | 429 | The request went over a traffic limit. |
unavailable | 503, 504 | AbuseZero couldn't finish the request. Retry it. |
New codes can be added within v1. Handle an unknown code by its type.
Handling errors
retryable: true: retry the same request with the sameIdempotency-Key. Back off between attempts. When the response has aRetry-Afterheader, wait that many seconds first.invalid_request: fix the request. Retrying it unchanged returns the same error.authenticationandpermission: fix the credential, its scopes or the member's permissions.conflict: read the object's current state, then decide what to send.- Evaluations: never treat an error as an allow. Use the Node.js SDK's
outagesetting to decide what your code receives when AbuseZero can't be reached. See Handle outages and retries.
import { AbuseZero, AbuseZeroError, type EvaluationRequest } from "abusezero/server";
const abusezero = new AbuseZero({
key: process.env.ABUSEZERO_SECRET_KEY!,
outage: "throw",
});
export async function evaluateClaim(request: EvaluationRequest, idempotencyKey: string) {
try {
return await abusezero.evaluate(request, idempotencyKey);
} catch (error) {
if (error instanceof AbuseZeroError) {
console.error(error.code, error.message, error.requestId);
if (error.retryable) {
// Retry later with the same idempotency key.
}
}
throw error;
}
}Error codes
Invalid request
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | MALFORMED_JSON | The body isn't valid JSON, or isn't sent as JSON. | Send a JSON object with Content-Type: application/json. |
| 413 | PAYLOAD_TOO_LARGE | The body is over the size limit. | Keep the body under the limit given in the message. |
| 422 | INVALID_FIELD | A field is missing or has an invalid value. | Fix the field named in error.field. |
| 422 | UNSUPPORTED_FIELD | The request includes a field the endpoint doesn't define, a rule uses an unsupported condition, or a custom identifier isn't registered. | Remove the field, choose a supported condition, or register the custom identifier and key version in Developers → Custom identifiers first. |
| 422 | IDEMPOTENCY_KEY_REQUIRED | A write was sent without a valid Idempotency-Key header. | Send 1 to 128 printable characters with no spaces. |
| 422 | CURSOR_INVALID | The page cursor was changed, or belongs to another list or workspace. | Clear the cursor and request the first page again. |
| 422 | INVALID_EMAIL | An email address isn't in a supported form. Quoted and international local parts aren't supported. field names where it was sent: subject.email on an evaluation, value on an allow or block list. | Send an address such as name@example.com. |
| 422 | INVALID_IP | An IP address isn't a valid IPv4 or IPv6 address. field names where it was sent: context.ip on an evaluation, value on an allow or block list. | Send a valid address, or leave the IP out. |
| 422 | IP_NOT_ROUTABLE | context.ip is a private, loopback, documentation or other non-routable address. | Send the user's public IP address, or leave the IP out. |
| 422 | DEVICE_TOKEN_INVALID | identifiers.device_token isn't a value the browser SDK returned. | Send the browser SDK's deviceToken unchanged, or unavailable:not_loaded when your consent tool kept the script from loading. |
| 422 | DEVICE_TOKEN_SCOPE | The device token was collected for a different claim_id. | Collect browser data with the same claim ID you evaluate. |
| 422 | BROWSER_SDK_UPGRADE_REQUIRED | The device token came from an outdated browser SDK. | Update the browser SDK and collect new browser data. |
| 422 | BOT_EVIDENCE_REQUIRED | The benefit requires a bot check, and the evaluation has none that can be used. | Collect browser data with prepareBotObservation when the person submits, and evaluate within 2 minutes. |
| 422 | AGENT_EVIDENCE_SCOPE | agent_evidence was produced for a different claim. | Verify the agent's request with this evaluation's claim ID. |
| 422 | AGENT_EVIDENCE_OUTDATED | agent_evidence came from a Node.js SDK before 0.11.0. | Update the Node.js SDK to 0.11.0 or later and send the token verifyAgentRequest() returns, unchanged. |
| 422 | REASON_REQUIRED | A not_granted or reversed outcome report has no reason. | Add a reason of 3 to 500 characters. |
| 422 | POLICY_NOT_FOUND | A benefit names a policy key that doesn't exist, or a policy builds on an unavailable protection level. | Choose an existing policy key or protection level. |
| 422 | ORIGIN_NOT_ALLOWED | A site in your site list isn't HTTPS. | List only HTTPS sites. |
| 422 | INVALID_DOMAIN | A list entry's domain isn't valid. field is value. | Use a domain such as example.com. |
| 422 | INVALID_TIMEZONE | A benefit's calendar eligibility names a time zone AbuseZero doesn't recognize. field is config.eligibility.timezone. | Use an IANA time zone such as America/New_York. |
Authentication
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 401 | AUTHENTICATION_REQUIRED | The request has no credential. | Send Authorization: Bearer with a key or access token. |
| 401 | INVALID_API_KEY | The key is unknown or revoked. | Create a new key in Developers. |
| 401 | API_KEY_EXPIRED | The key passed its expiry. | Create a new key in Developers and replace the old one. |
| 401 | INVALID_ACCESS_TOKEN | The OAuth access token is invalid or expired. A value that isn't a complete API key is read as an access token. | Sign in again, then retry. If you meant to send an API key, check that it was copied in full. |
Permission
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 403 | KEY_TYPE_NOT_ALLOWED | The wrong kind of key was used: a publishable key on a server endpoint, or a server key in the browser. | Use a secret or restricted key on your server, and the publishable key in the browser. |
| 403 | KEY_PERMISSION_DENIED | The API key lacks the endpoint's scope. | Use a key with the scope named in the message. |
| 403 | OAUTH_SCOPE_REQUIRED | The access token lacks the endpoint's scope. | Authorize the scope named in the message. |
| 403 | WORKSPACE_REQUIRED | An OAuth request has no valid AbuseZero-Workspace header. | Send a ws_ ID from GET /v1/workspaces. |
| 403 | WORKSPACE_ACCESS_DENIED | The credential can't reach that workspace. | Choose a workspace the credential belongs to. |
| 403 | PERMISSION_REQUIRED | The signed-in member lacks a permission the scope needs. | Ask the workspace owner for the permission. |
| 403 | EVIDENCE_SCOPE_REQUIRED | An export with evidence needs the evaluation_evidence:read scope. | Add the scope, or export without evidence. |
| 403 | ONBOARDING_REQUIRED | The workspace hasn't finished setup. | Ask the workspace owner to finish onboarding in the dashboard. |
| 403 | ORIGIN_NOT_REGISTERED | The browser SDK ran on a site that isn't in your site list. | Add the site in Developers → Browser setup. Only HTTPS sites can send browser data. |
| 403 | WORKSPACE_DELETION_EXCLUDED | A data deletion tried to delete the whole workspace. | Delete workspaces in the dashboard. |
Not found
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 404 | ENDPOINT_NOT_FOUND | No endpoint matches the method and path. | Check the method and path. |
| 404 | RESOURCE_NOT_FOUND | No object with this ID exists in the workspace. | Check the ID and the workspace. |
| 404 | EVALUATION_NOT_FOUND | No evaluation with this ID exists in the workspace. | Check the evaluation ID. |
| 404 | BENEFIT_NOT_FOUND | No benefit with this key or ID exists in the workspace. | Check the benefit key or ID, or create the benefit. |
| 404 | POLICY_NOT_FOUND | No policy with this ID or key exists in the workspace. | Check the policy ID or key. |
| 404 | POLICY_VERSION_NOT_FOUND | The policy has no such version. | List the policy's versions and choose one. |
| 404 | IDENTIFIER_NOT_FOUND | No custom identifier with this ID exists. | Check the custom identifier's ID. |
| 404 | IMPORT_NOT_FOUND | No import with this ID exists in the workspace. | Check the import ID. |
| 404 | EXPORT_NOT_FOUND | The export doesn't exist or has expired. | Request a new export. |
| 410 | IMPORT_REPORT_EXPIRED | The import's error report is no longer available. | Upload the corrected CSV again. |
Conflict
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 409 | IDEMPOTENCY_CONFLICT | The Idempotency-Key was already used with a different body. | Send the original body again with this key, or use a new key for a new request. |
| 409 | EVALUATION_PENDING | An evaluation with this key is still running. | Retry with the same Idempotency-Key. |
| 409 | CLAIM_CONFLICT | The claim_id already belongs to a different user. | Use a new claim ID for a different award. |
| 409 | CLAIM_PERIOD_ENDED | The claim_id belongs to an earlier eligibility period. | Use a new claim ID for the new period's award. |
| 409 | OUTCOME_ALREADY_RECORDED | An imported outcome already exists for this claim. | Reconcile the claim in your application. |
| 409 | OUTCOME_CONFLICT | The outcome can't move to the requested state, the evaluation was denied or replaced, the claim's earlier approval expired, or the external outcome ID belongs elsewhere. | Read the evaluation and outcome, then report the correct state. See Outcomes. |
| 409 | DEVICE_TOKEN_USED | The device token was already used for another claim. | Collect new browser data for this claim. |
| 409 | DELETION_IN_PROGRESS | The user's history, or the workspace, is being deleted. | Retry after the deletion finishes. |
| 409 | POLICY_UNAVAILABLE | The benefit's policy isn't available. | Assign an available policy to the benefit. |
| 409 | BENEFIT_CONFLICT | The benefit changed since you read it. | Read the benefit again and send its current revision. |
| 409 | BENEFIT_SCOPE_FIXED | The update changes the benefit's eligibility. | Create a separate benefit for a different eligibility period. |
| 409 | POLICY_CONFLICT | The policy changed since you read it. | Read the policy again and send its current revision. |
| 409 | BENEFIT_KEY_TAKEN | Another benefit already uses this key. field is key. | Choose a different benefit key. |
| 409 | POLICY_KEY_TAKEN | Another policy already uses this key. | Choose a different key. |
| 409 | POLICY_IN_USE | Benefits still use the policy. | Move those benefits to another policy first. |
| 409 | PROTECTION_LEVEL_READ_ONLY | Protection levels can't be edited or deleted. | Duplicate the level and change the copy. |
| 409 | IDENTIFIER_ALREADY_REGISTERED | This custom identifier key and key version are already registered. field is key_version. | Register a new key version. |
| 409 | IDENTIFIER_IN_USE | A policy still uses the custom identifier. | Remove it from your policies first. |
| 409 | REVISION_CONFLICT | Your site list changed since you read it. | Read browser setup again and send its current revision. |
| 409 | EXPORT_NOT_READY | The export hasn't finished. | Wait until its state is complete, then download it. |
| 409 | IMPORT_STATE | The import isn't in a state that allows this step. | Read the import and follow its current state. |
| 409 | IMPORT_NOT_UPLOADED | Validation was requested before the CSV file was uploaded. | Upload the file to the import's upload URL, then validate. |
| 409 | IMPORT_COMPLETE | The import already finished, so it can't be cancelled. | To remove its history, delete the affected users' data. |
| 409 | IMPORTS_IN_PROGRESS | Active imports block a data deletion. | Finish or cancel them first. |
Rate limit
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 429 | RATE_LIMITED | The request went over a traffic limit. | Wait for the number of seconds in Retry-After, then retry with the same Idempotency-Key. |
Unavailable
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 503 | TEMPORARILY_UNAVAILABLE | AbuseZero couldn't finish the request. | Retry with the same Idempotency-Key. |
| 503 | LIMIT_CONFIGURATION_UNAVAILABLE | AbuseZero couldn't confirm the workspace's traffic limits. | Retry shortly. |
| 503 | BOT_EVIDENCE_UNAVAILABLE | The bot check couldn't be completed for this evaluation. | Collect new browser data with prepareBotObservation, then retry. |
| 504 | DEADLINE_EXCEEDED | The request reached its time limit. | Retry with the same Idempotency-Key. If the first attempt finished, you get its result. |
Skipped evaluations are not errors
When a workspace can't run an evaluation, for example because it has run out of credits, POST /v1/evaluations returns 200 with status: "skipped" instead of an error. No allow or deny was decided. See Evaluations.
How is this guide?
Last updated on