Error codes
Every Gordon API error returns a JSON body with a snake_case error slug, usually a human-readable message, plus context fields where relevant. Branch on error — most responses do not include a separate code field (service_not_enabled is the lone exception).
Standard error shape
Every error shares the same envelope — the error slug, plus context:
{
"error": "snake_case_slug",
"message": "Human-readable description (present on most errors).",
// context fields vary by error, e.g.:
"amount_units": 50000,
"max_per_call_units": 10000
}Branch on the error slug. Governance decisions (block / escalate) also include a transaction_id (the audit reference); an escalate additionally returns an approval_id. Only service_not_enabled carries a SCREAMING_SNAKE code.
gordon.fetch() throws a GordonPaymentError. It exposes error.message, error.statusCode (the HTTP status), and error.body (the parsed error JSON — the error slug plus context fields such as approval_id).invalid_agent_keyThe agent key is missing, malformed, or revoked. A header that isn't Bearer <gak_pub>:<gak_sec> returns invalid_authorization_header instead.
Response body
{
"error": "invalid_agent_key"
}insufficient_usdc_balanceThe agent wallet's USDC balance can't cover this call.
Response body
{
"error": "insufficient_usdc_balance",
"balance_usdc": "0.000000",
"required_usdc": "0.005000",
"wallet_address": "0x6d6E695b09861467c7d462f5AAF31cF3540B9192",
"message": "Your agent wallet has 0.000000 USDC but this call costs 0.005000 USDC."
}amount_exceeds_per_call_limitThe call costs more than the agent's per-call spend cap for this service.
Response body
{
"error": "amount_exceeds_per_call_limit",
"amount_units": 50000,
"max_per_call_units": 10000,
"message": "This call costs $0.05 (50000 µ) but your agent policy caps per-call spend at $0.01 (10000 µ)."
}daily_spend_limit_exceededThis call would push the agent past its rolling 24h daily cap for the service.
Response body
{
"error": "daily_spend_limit_exceeded",
"spend_today": 9990000,
"amount_units": 50000,
"max_per_day_units": 10000000
}amount_exceeds_maxThe provider's price is higher than the max_payment_units ceiling you supplied.
Response body
{
"error": "amount_exceeds_max",
"amount_units": 70000,
"max_payment_units": 50000
}approval_requiredThe payment trips an approval_required rule or the per-service approval threshold, so Gordon escalates instead of paying. The response carries an approval_id and a transaction_id (audit reference).
Response body
{
"error": "approval_required",
"transaction_id": "8510fc02-c699-42ea-aafc-04f0e18dd3d5",
"approval_id": "4f1c2d3e-5a6b-47c8-9d0e-1f2a3b4c5d6e",
"amount_units": 2000000,
"require_approval_above_units": 1000000,
"message": "A human operator must approve this payment before the agent can proceed."
}service_not_enabledThe service isn't in the agent's enabled set. This is the one error that also carries a SCREAMING_SNAKE code field.
Response body
{
"error": "service_not_enabled",
"code": "SERVICE_NOT_ENABLED",
"service_id": "exa",
"message": "This service has not been enabled for your agent. Enable it at https://withgordon.ai/account/agents/<id>/services",
"transaction_id": "8510fc02-c699-42ea-aafc-04f0e18dd3d5"
}operation_not_enabled_for_agentThe operation isn't in the agent's enabled_operations allowlist for this service.
Response body
{
"error": "operation_not_enabled_for_agent",
"operation_id": "scrape.url",
"enabled_operations": ["search.web"],
"message": "Operation scrape.url is not enabled for your agent."
}domain_not_allowedFor scrape/crawl operations, the target_url hostname isn't permitted by the agent's allowed_domains policy.
Response body
{
"error": "domain_not_allowed",
"hostname": "evil.com",
"allowed_domains": ["example.com"],
"message": "The domain evil.com is not in your agent's allowed_domains policy for this service."
}policy_blockedA policy rule (budget_limit, vendor_allowlist, category_block) blocked the call. The decision object carries the reason.
Response body
{
"error": "policy_blocked",
"decision": {
"result": "block",
"reason": "category_block:gambling",
"rule_triggered": "rule_3",
"latency_ms": 1
},
"transaction_id": "8510fc02-c699-42ea-aafc-04f0e18dd3d5"
}service_not_foundThe service_id doesn't match any catalog service (or isn't in the curated set).
Response body
{
"error": "service_not_found"
}idempotency_key_reused_for_different_requestThe idempotency_key was already used, but with a different request body.
Response body
{
"error": "idempotency_key_reused_for_different_request",
"settlement_id": "85e6f79c-4e6a-4f95-b18c-1baabd45f879"
}idempotent_replayAn approved retry was already executed against this approval — replaying it is blocked to prevent a double-spend.
Response body
{
"error": "idempotent_replay",
"approval_id": "4f1c2d3e-5a6b-47c8-9d0e-1f2a3b4c5d6e"
}provider_proof_requiredPOST /x402/settlements/:id/complete was called without the provider's payment proof.
Response body
{
"error": "provider_proof_required",
"message": "Supply payment_response_header (set GORDON_ALLOW_PROVIDER_OK_ASSERTION=true to accept provider_ok in dev)."
}onchain_settlement_not_verifiedThe transaction exists, but Gordon could not match a successful exact USDC transfer from the agent wallet to the authorized recipient for the authorized amount.
Response body
{
"error": "onchain_settlement_not_verified",
"reason": "usdc_transfer_mismatch",
"retryable": false
}too_many_requestsToo many requests from this client in a short window (per-IP throttle on agent endpoints).
Response body
{
"error": "too_many_requests",
"retry_after": 60
}internal_errorAn unexpected server error. The body never leaks stack traces or internals.
Response body
{
"error": "internal_error"
}Common error slugs
| HTTP | error | When |
|---|---|---|
| 400 | invalid_request | Malformed body or failed schema validation (details[] lists the issues) |
| 400 | <field>_required | A required field is missing (e.g. payment_requirement_required, max_payment_units_required) |
| 401 | invalid_agent_key | Missing, malformed, or revoked agent key |
| 402 | insufficient_usdc_balance | Agent wallet USDC balance too low |
| 402 | amount_exceeds_per_call_limit | Call exceeds the per-call spend cap |
| 402 | daily_spend_limit_exceeded | Call exceeds the rolling daily cap |
| 402 | amount_exceeds_max | Provider price exceeds the request's max_payment_units |
| 402 | approval_required | Trips an approval rule/threshold — escalates with an approval_id |
| 403 | service_not_enabled | Service not in the agent's enabled set (also returns code) |
| 403 | operation_not_enabled_for_agent | Operation not in the agent's enabled_operations |
| 403 | domain_not_allowed | target_url hostname not in allowed_domains (scrape/crawl) |
| 403 | policy_blocked | A policy rule blocked the call (see decision.reason) |
| 404 | service_not_found | service_id not in the catalog |
| 404 | not_found | Unknown route |
| 409 | idempotency_key_reused_for_different_request | Key reused with a different request body |
| 409 | idempotent_replay | Approved retry already executed (double-spend guard) |
| 413 | request_too_large | JSON body over the size limit |
| 422 | provider_proof_required | Confirm called without payment_response_header |
| 422 | settlement_transaction_hash_required | Provider proof did not contain a transaction hash |
| 422 | onchain_settlement_not_verified | Transaction reverted or exact USDC transfer did not match |
| 409 | settlement_transaction_already_used | Transaction hash already finalized another settlement |
| 429 | too_many_requests | Per-IP rate limit; back off retry_after seconds |
| 503 | onchain_settlement_not_verified | RPC verification unavailable; retryable is true |
| 500 | internal_error | Unexpected server error |