Errors#
Agent API errors use:
{
"ok": false,
"error": {
"code": "validation_failed",
"message": "Request body is invalid.",
"details": {}
}
}Always branch on error.code first. Treat details as additive context.
Stable Error Codes#
| HTTP status | Codes |
|---|---|
| 400 | validation_failed |
| 401 | authentication_failed, agent_disabled, agent_not_yet_active, agent_expired, invalid_token |
| 403 | owner_only_action, identity_gated_action, permission_not_granted, asset_not_allowed, recipient_not_allowed, protocol_not_allowed, limit_exceeded, cooldown_active, ltv_too_high, insufficient_scope |
| 404 | not_found |
| 409 | quote_expired, quote_already_consumed, timelock_not_elapsed, ltv_target_unreachable, idempotency_key_reuse_mismatch, idempotency_request_in_progress, rule_changed_during_execution, operation_not_cancellable |
| 422 | insufficient_funds, slippage_exceeded, price_stale |
| 429 | rate_limited |
| 500 | internal_error |
| 502 | execution_failed, execution_timeout, batch_step_failed |
Reserved v1 codes that current shipped v1 routes may not emit yet:
cooldown_activetimelock_not_elapsedrule_changed_during_executionbatch_step_failed
Keep handlers for reserved codes because they may become active in a v1.x release.
Authentication Details#
authentication_failed covers missing credentials, unknown agent ids, key mismatches, missing owners, and soft-deleted owners.
These codes are returned only after the supplied key matches the agent:
agent_disabledagent_not_yet_activeagent_expired
OAuth Challenges#
OAuth Bearer auth adds RFC 6750 challenge headers.
Malformed, expired, revoked, wrong-audience, or otherwise invalid tokens return 401 invalid_token.
WWW-Authenticate: Bearer resource_metadata="https://api.hightop.com/.well-known/oauth-protected-resource/v1/agent", error="invalid_token"Valid tokens that lack a required route scope return 403 insufficient_scope.
WWW-Authenticate: Bearer resource_metadata="https://api.hightop.com/.well-known/oauth-protected-resource/v1/agent", error="insufficient_scope", scope="agent:withdrawals:write"The error body also uses insufficient_scope.
Typed error.details#
Some errors include stable details shapes.
| Code | Details |
|---|---|
idempotency_request_in_progress | idempotency_key: string |
idempotency_key_reuse_mismatch | idempotency_key: string, original_method: string, original_path: string |
slippage_exceeded | quote_id: string, expected_out: string, min_amount_out: string |
quote_already_consumed | quote_id: string, consumed_at: string | null, optional consumed_by_operation_id: string |
quote_expired | quote_id: string, expired_at: string |
rate_limited | limit_count: number, attempted_count: number, period: "transaction" |
limit_exceeded | cap: number, observed: number, period: "rolling_30d" | "active" or max_endpoints: number |
ltv_too_high | current_ltv: string, estimated_ltv_after: string, max_ltv_after: string |
ltv_target_unreachable | current_ltv: string, target_ltv: string, max_repay_usd: string, required_repay_usd: string, estimated_ltv_after: string |
Example:
{
"ok": false,
"error": {
"code": "quote_expired",
"message": "Quote has expired.",
"details": {
"quote_id": "00000000-0000-0000-0000-000000000000",
"expired_at": "2026-05-16T18:30:00.000Z"
}
}
}