Error codes

Every stable error and reason code AgentGate returns, from the browser SDK, /v1/siteverify, the gateway, the agent API and the console API.

Codes are stable and safe to branch on. Messages are for people and may change. Codes never reveal which rule matched or any score.

Browser SDK

Returned by /v1/browser/challenge, /v1/browser/instrument and /v1/browser/verify as {"error": "<code>", "message": "…"}, and surfaced by the SDK as AgentGate.Error.code (the rejection of execute(), or the argument of errorCallback). Refusals decided by the rule engine also carry a decisionId.

CodeHTTPMeaningFix
site_invalid400unknown or malformed site keycheck data-sitekey / siteKey
action_invalid400the action is not [a-z0-9_]{1,64} or not configured for the siteconfigure the action on the site, or fix data-action
origin_denied403the page's Origin is missing or not in the site's allowed origins, or the challenge was issued to another originadd the origin to the site
site_disabled403the site's account was deleted, so the key no longer issues challengesuse a site of an active account
rate_limited429over the per-address limit (60 challenges or 120 verifications per minute) or the site's limitwait for Retry-After (60 s)
challenge_invalid400unknown challenge, or one issued for another actioncall execute() again
challenge_expired410the challenge is older than five minutescall execute() again
challenge_used409the challenge already produced a tokencall execute() again
too_many_attempts429the challenge's four verify attempts are used upcall execute() again
challenge_failed403the evidence was declined, the step-up answer was invalid, or the visitor was still challenged after passing the checklet the person retry; check the decision in the dashboard
schema_unsupported400the SDK's signal schema is not the server'sload the SDK from the same AgentGate you verify against
payload_too_large413the request body is over the limita bug; report it
bad_request400malformed JSON or fieldsa bug; report it
unavailable503a dependency failedretry after Retry-After (5 s)

Codes the SDK adds itself (no HTTP status):

CodeMeaning
timeouttimeout (default 30 s) or, once the check is shown, checkTimeout (default 5 min) passed
abortedthe signal you passed was aborted
canceledthe person closed the check with Escape or Cancel
networkthe request failed: offline, DNS, TLS, CORS or a CSP connect-src block
erroranything else

With the testing site key site_test_always_fail the SDK always fails with challenge_failed (HTTP 403). See Testing.

Server-side validation

POST /v1/siteverify answers {"success": false, "error": "<code>", "message": "…"}:

CodeHTTPMeaning
invalid_request400malformed JSON (over 4 KiB included), missing token or expectedAction, or a requestId outside [A-Za-z0-9._:-]{1,128}
invalid_credential401the backend secret is missing, malformed, revoked or another site's, or Authorization and secret differ
invalid_token422not a token, bad signature, unknown site or key; also a test token sent with a real secret, or a real token sent with a test secret
expired_token422past its expiry (default two minutes after issue)
action_mismatch422issued for another action than expectedAction
origin_mismatch422issued to another origin than expectedOrigin
token_used409already redeemed, by another (or no) requestId
unavailable503the store failed; retry with the same requestId

The testing secrets return fixed answers for test tokens: ags_test_always_fail gives 422 invalid_token and ags_test_token_spent gives 409 token_used.

Gateway

GET /v1/gateway/check answers with a status and the headers X-AgentGate-Decision (allow, challenge or block) and X-AgentGate-Reason; denials also carry a small JSON body {"decision": "…", "reason": "…"}.

ReasonStatusMeaning
allowed204enforce mode allowed the request
monitor204monitor mode; X-AgentGate-Would-Decision says what enforce would do
receipt_required401a browser route and no token (or clearance) was sent
invalid_token401the token is not valid for this site (a test token included)
expired_token401the token expired
token_used401the token was already spent
action_mismatch401the token is for another action than the route's
origin_mismatch401the token or clearance is for another origin
invalid_clearance401the clearance is malformed, badly signed or for another site
expired_clearance401the clearance expired
agent_signature_invalid403a Web Bot Auth signature failed (bad, expired, replayed, or the body digest does not match)
rate_limited403over the gateway rate budget or the agent's quota; the nginx example answers the client 429
payment_required403the action has a price and no acceptable offer was sent; the nginx example answers 402 with Crawler-Price
payment_invalid403the price offer is malformed, conflicting or unsigned
agent_denied403the site's agent policy does not allow this agent this action
agent_unverified401 or 403a User-Agent claims an AI agent or crawler without a valid signature (challenge or block, per the site's policy)
policy401 or 403any other rule (the rule name is never exposed)
invalid_credential403the caller's backend secret is wrong; nothing was evaluated
invalid_request403the adapter's X-Original-* headers are missing or malformed
service_unavailable503, or 204 in monitor modea dependency failed; enforce mode fails closed

The gateway never answers 429 itself. The complete header contract is on Gateway overview.

Agents

/agent/submit and gateway routes answer agents with plain HTTP statuses:

StatusMeaning
400invalid request body, or a malformed price offer (crawler-error, below)
401no valid Web Bot Auth signature or agent key ("invalid agent credential")
402payment required: see crawler-price and crawler-error
403the site's policy does not permit this agent this action ("not permitted"), or a challenge; a challenge for a verified agent carries a handoff object
409the submission id was already used for different content
429over the agent's per-site quota; Retry-After: 60
503a dependency failed

Pay-per-crawl errors, in the crawler-error header:

crawler-errorStatusMeaning
MissingCrawlerPrice402no offer sent
InvalidCrawlerExactPrice402the exact offer differs from the price (value or currency)
InvalidCrawlerMaxPrice402the maximum is below the price
InvalidCrawlerPriceValue400not CUR 0.00
ConflictingPriceHeaders400both offer headers sent
StrongAuthRequired400 or 402the offer is not covered by a signature, or the agent is not verified

Human handoff polling (GET /v1/handoff/{id}): 404 when anyone other than the session or agent that received the handoff polls, 410 once it is redeemed or expired. See Human handoff.

Console and admin API

/v1/console/*, /v1/account/* and /v1/admin/* answer {"error": "<code>", "message": "…"}. The codes you are likely to meet:

CodeHTTPMeaning
unauthorized401not signed in, or a wrong operator credential
session_expired401the session ended (console sessions last 12 h, or 2 h without use); sign in again
invalid_credentials401wrong email or password
mfa_required401the password was right; enter the two-factor code (POST /v1/account/login/mfa)
invalid_code401wrong or already used two-factor or recovery code
invalid_token401the API token is invalid, revoked or expired
csrf403the X-CSRF-Token header is missing or wrong
forbidden403your role in the account, or the API token's scopes, lack the permission message names
account_suspended403the account is suspended: console and API are closed, its sites keep protecting
user_suspended403your user is suspended
no_account403you belong to no account; create one or ask for an invitation
token_not_allowed403API tokens work only on /v1/console
mfa_setup_required403the account requires two-factor authentication; turn it on first
invalid_password403the current password is wrong (password change, turning 2FA off)
cross_origin403the request came from another site
browser_check_failed403the request-access form's browser check did not pass
not_found, site_not_found404no such object, or not yours
invalid_link404an invite or reset link is invalid, used or expired
invalid_request, invalid_site, invalid_filter, invalid_limit, invalid_range, invalid_cursor400the request or a parameter is malformed; message says which
forbidden_field, immutable_field400a field the server sets, or that cannot change (siteKey, customer)
weak_password400the password does not meet the rules
site_limit, credential_limit, member_limit409the account's limits (default 5 sites, 3 live secrets per site, 10 members)
account_limit409a user can own at most 3 accounts
not_member404switching to an account you do not belong to
already_member, already_active409the person is already a member, or has already set a password
last_owner409every account keeps an owner; make someone else owner first
sites_enforcing409switch every site to monitor mode before deleting the account
confirm_mismatch400type the account name exactly to confirm deletion
site_key_taken409the site key exists
revision_conflict409the rules changed since you loaded them; reload and retry
token_limit409at most 25 live API tokens per account
mfa_already_enabled, mfa_not_enabled, mfa_not_pending, mfa_required_by_account409the two-factor state does not allow that step
rate_limited429too many attempts; wait
store_unavailable503the database did not answer; retry

The full contracts are in the API reference.

View as Markdown