ReferenceErrors

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).

Error format

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.

Error handling in the SDK
When the Platform rejects a payment, 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).
401invalid_agent_key

The 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"
}
Resolution: Send Authorization: Bearer gak_pub_...:gak_sec_.... If the key was rotated or revoked, grab the current one from Account → Agents → [your agent] → Agent setup → Connect.
402insufficient_usdc_balance

The 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."
}
Resolution: Fund the agent wallet with USDC on Base (Account → Agents → [your agent] → Fund). No ETH is needed — gas is sponsored.
402amount_exceeds_per_call_limit

The 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 µ)."
}
Resolution: Lower max_payment_units in the request, or raise the per-call cap in the agent's Services tab.
402daily_spend_limit_exceeded

This 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
}
Resolution: Wait for the daily window to roll over, or raise max_per_day_units for the service.
402amount_exceeds_max

The 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
}
Resolution: Raise max_payment_units if the price is expected. This is your price-spike guard — leave it tight to abort on surprises.
402approval_required

The 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."
}
Resolution: A human resolves it in the dashboard Approvals queue. Poll GET /approvals/:id with the approval_id; once approved, retry the same call carrying approval_id. (The SDK surfaces this as a GordonPaymentError with approval_id in error.body.)
403service_not_enabled

The 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"
}
Resolution: Enable the service in Account → Agents → [your agent] → Services. New agents have the curated catalog pre-enabled, so this fires for services you disabled or for non-curated services.
403operation_not_enabled_for_agent

The 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."
}
Resolution: Add the operation to the service's enabled_operations, or set enabled_operations to null to allow all operations.
403domain_not_allowed

For 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."
}
Resolution: Add the domain to allowed_domains for the service (a bare domain matches all subdomains; a glob like *.example.com matches one label).
403policy_blocked

A 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"
}
Resolution: Inspect decision.reason and adjust the agent's policy rules (Account → Agents → [your agent] → Agent setup → Policies).
404service_not_found

The service_id doesn't match any catalog service (or isn't in the curated set).

Response body

{
  "error": "service_not_found"
}
Resolution: Use GET /services or gordon_list_services to get a valid service slug.
409idempotency_key_reused_for_different_request

The 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"
}
Resolution: Use a fresh idempotency_key per logically distinct request. Reuse a key only to retry the exact same request.
409idempotent_replay

An 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"
}
Resolution: The payment already settled against this approval. Don't retry — fetch the receipt (GET /x402/settlements/:id) to confirm.
422provider_proof_required

POST /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)."
}
Resolution: Pass the provider's X-Payment-Response value as payment_response_header when confirming the settlement.
422onchain_settlement_not_verified

The 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
}
Resolution: Do not treat the receipt as confirmed. Check the transaction hash and provider response. Retry only when retryable is true.
429too_many_requests

Too many requests from this client in a short window (per-IP throttle on agent endpoints).

Response body

{
  "error": "too_many_requests",
  "retry_after": 60
}
Resolution: Back off for retry_after seconds (not milliseconds) before retrying.
500internal_error

An unexpected server error. The body never leaks stack traces or internals.

Response body

{
  "error": "internal_error"
}
Resolution: Retry with backoff. If it persists, contact support with the approximate timestamp and the endpoint you called.
Quick reference

Common error slugs

HTTPerrorWhen
400invalid_requestMalformed body or failed schema validation (details[] lists the issues)
400<field>_requiredA required field is missing (e.g. payment_requirement_required, max_payment_units_required)
401invalid_agent_keyMissing, malformed, or revoked agent key
402insufficient_usdc_balanceAgent wallet USDC balance too low
402amount_exceeds_per_call_limitCall exceeds the per-call spend cap
402daily_spend_limit_exceededCall exceeds the rolling daily cap
402amount_exceeds_maxProvider price exceeds the request's max_payment_units
402approval_requiredTrips an approval rule/threshold — escalates with an approval_id
403service_not_enabledService not in the agent's enabled set (also returns code)
403operation_not_enabled_for_agentOperation not in the agent's enabled_operations
403domain_not_allowedtarget_url hostname not in allowed_domains (scrape/crawl)
403policy_blockedA policy rule blocked the call (see decision.reason)
404service_not_foundservice_id not in the catalog
404not_foundUnknown route
409idempotency_key_reused_for_different_requestKey reused with a different request body
409idempotent_replayApproved retry already executed (double-spend guard)
413request_too_largeJSON body over the size limit
422provider_proof_requiredConfirm called without payment_response_header
422settlement_transaction_hash_requiredProvider proof did not contain a transaction hash
422onchain_settlement_not_verifiedTransaction reverted or exact USDC transfer did not match
409settlement_transaction_already_usedTransaction hash already finalized another settlement
429too_many_requestsPer-IP rate limit; back off retry_after seconds
503onchain_settlement_not_verifiedRPC verification unavailable; retryable is true
500internal_errorUnexpected server error