← All docs

Errors

Every error is the same shape:

{ "error": { "code": "TokenNotFound", "message": "Unknown or revoked secret API key" } }

Branch on code. The message is written for a human reading a log and may change; the code is the contract. A few validation errors from the agent's own endpoints (identification, the pulse, the credential lookup) also carry an issues array naming the fields that failed.

Codes

The Server API, the assess family and the endpoints the agent and the mobile SDKs call:

Code Status Means Do
TokenNotFound 403 The key is unknown, revoked, or the wrong kind for this endpoint — a management key sent to the Server API is unknown here Check you are sending a secret key to the Server API and the public key from the agent and the mobile SDKs. The Management API answers Unauthorized instead
CardNumberRefused 400 A card field held a card number (/assess, /assess/outcome) Send your processor's card fingerprint or a keyed hash of the number instead — see protecting checkout. The number is not echoed back.
KeyNotPermitted 403 The secret key was created for other uses — an assess-only key reading events or visitors, say Use a key allowed to do this; an edge's key should stay assess-only
OriginNotAllowed 403 The request's origin is not in your allowed origins Add it in Settings, or clear the list to allow every origin
DomainMismatch 403 The request arrived on a verified custom domain owned by a different organization Use a key from the organization that owns the host
RequestFiltered 403 A request filter rejected an identification Expected, if you configured it. Not billed, no event stored. (On /assess a filter is a block verdict, not an error.)
TrialEnded, TrialCallsUsed, SubscriptionEnded 402 An identification or assessment from an organization whose trial ended, whose trial calls are used, or whose subscription ended (billing) An owner subscribes on the dashboard's Billing page. Meanwhile the agent earns no receipt and the edge and server SDKs fail open: the site serves, unprotected
TooManyRequests 429 Rate limit exceeded — identification, challenges, the pulse, integrity reports, the waiting room, App Attest; and the Management API's expensive calls past their allowance (usage, fitting a model, reading calibration) Back off; Retry-After says how long
RateLimited 429 Too many breached-credential lookups from one address (60 a minute) Back off; this one sends no Retry-After
InvalidPayload 400 The body did not match the expected shape, or a field in it is unusable: an ip that is not an address, a phone not in E.164 form, a url that is not absolute, a PATCH that names nothing to change, a sealed payload that fails its checks Read the message, which names the field; for an identification, check the agent version
InvalidRequest 400 GET /related-visitors without visitor_id Pass it
InvalidJson 400 The body was empty, not JSON, or nested more than 32 levels deep
PayloadTooLarge 413 The body was over the endpoint's limit: 1 MB for an identification and for the Management API; 256 KB for /assess, /assess/outcome and /assess/receipts; 512 KB for a browser's policy reports (/csp/{key}, which answers 204 whatever it holds); 64 KB for everything else. An identification is also held to 10,000 values (each array element and object member counts one) An identification is normally under 50 KB and some 600 values; look for something large in tag or a page field you added
EventNotFound 404 No such event in this key's environment See Reading a 404
VisitorNotFound 404 No such visitor in this key's environment See Reading a 404
TokenExpired 401 The session token sent to the pulse or the waiting room has expired Identify again for a fresh one (the agent drops the expired token for you)
TokenInvalid 403 The session token was rejected: malformed, a bad signature, or minted for another organization or environment Identify again; do not retry the same token
SessionRevoked 403 A revoked session tried to join a waiting room Expected while the revocation lasts
QueueDenied 403 A session convicted as automation, or whose verdict was block, tried to join a waiting room Expected. See waiting rooms
AddressRequired 403 A waiting room that binds admissions to the visitor's network (bindIp) was entered with no client address the server could see Send the client's address the way your proxy setup says, or turn bindIp off
RoomNotFound 404 No waiting room with that slug in the key's environment Check the slug and the key's environment
TicketInvalid 403 A queue poll's ticket is not a queueToken this organization issued Send the queueToken from the enter answer, not the ticket number
TicketGone 410 The spot in line has expired: abandoned for a day, an admission that lapsed, a revoked session, a closed room Enter again
ChallengeInvalid 400 An App Attest registration's challenge is unknown, already used or expired Fetch a new challenge
AppIdsNotConfigured 409 App Attest registration in an environment with no Apple app ids Set appleAppIds with PATCH /environments/{id}
AttestationRejected 400 Apple's attestation failed verification, or its key id is not the one sent Do not trust the device's key

The Management API has codes of its own:

Code Status Means Do
Unauthorized 401 No key, or not a live management key (a secret key is not one) Send a management key
Forbidden 403 The key lacks the scope (the message names it), tries to grant scopes it does not hold, or tries to act on an owner or an admin or to grant ownership Add the scope, or do it from the dashboard
BadRequest 400 A field failed validation; the body is not JSON, not an object, or nested more than 32 levels deep; a required ?environment= is missing; the body and ?environment= name different environments Read the message, which names the field
NotFound 404 No such record — or environment, for ?environment= — in this organization
Conflict 409 The change would break a rule: a name already taken, the plan's subdomain limit, the last owner, the default environment, a key revoking itself, or one more than an environment holds (20 webhooks, 10 log streams, 1,000 request filters) or an organization (50 environments, 200 keys not revoked, 20 proxy secrets, 10 encryption keys, 10 SCIM tokens) Read the message
SeatLimitReached 409 An invitation beyond the plan's seats Remove a member or change plan
RuleLimitReached 409 A rule beyond the 500 an environment holds Delete rules no longer needed (let-through rules that stopped matching among them)
ThreatFeedFull 409 Threat-feed entries beyond the 50,000 live ones an environment holds Delete entries no longer needed, or add them with a ttlHours so they lapse
PayloadTooLarge 413 The body was over 1 MB
InternalError 500 Something failed on our side Retry later

404 (EventNotFound, VisitorNotFound; NotFound on the Management API) is returned for a record that does not exist — or that belongs to another environment or organization. That is deliberate: any other answer confirms the record exists.

Reading a 404

try {
  event = await antibot.getEvent(requestId)
} catch (e) {
  if (e.status === 404) {
    // Either it never existed, or this key cannot see it.
    // Most often: a staging key reading a production request id.
  }
}

If you are getting 404s for events you can see in the dashboard, check which environment your key belongs to.

Sealed results

unsealEventResponse throws SealedResultError for a payload that is too short, has the wrong header, or cannot be opened by any supplied key — and when it is given no keys at all.

A wrong key and a tampered payload are not distinguished. Both mean do not trust this result, and saying which would tell an attacker whether they had guessed the key. Treat the throw as a denial, not a retry.

Webhooks

Your endpoint's failures, not ours. Anything outside 2xx, or slower than three seconds, counts as a failure and is retried once after five minutes with the same event id. See Webhooks.

Signals that are absent rather than false

Not an error, but the same class of mistake:

{ "result": false, "unavailable": true, "reason": "ipPseudonymized" }

unavailable: true means the signal could not be computed. Check it before you read result, or you will treat "no opinion" as "no detection". See Privacy.

Agent errors

The browser agent throws if apiKey is missing. Network failures reject the promise from get(), and so does any error answer from the API (OriginNotAllowed, TooManyRequests, …), with the status and body in the message.

Identification failing should not take your page down:

let result = null
try {
  result = await agent.get({ ignoreCache: true })
} catch {
  // Decide what an unidentified visitor means for this action.
  // Usually: continue, and let the server treat it as unknown.
}

Whatever you do, do not fail open on the server side because the client did not send a request id. An absent identification is not a clean one.