Errors and Rate Limits

The Oris API uses standard HTTP status codes and returns structured error responses. The SDKs map these to typed exception classes for language-idiomatic error handling.

Error Response Format

Every error response follows a consistent JSON structure. The code field is a machine-readable identifier. The message field is a human-readable description. The details field provides additional context when available.

Error Response
{ "error": { "code": "POLICY_VIOLATED", "message": "Payment rejected by spending policy.", "request_id": "req_8f3a2b1c-d4e5-6789-abcd-ef0123456789", "details": { "violated_rules": ["max_per_transaction", "max_daily"], "action": "reject" } } }

Error Codes

HTTPCodeDescription
400BAD_REQUESTMalformed request body or missing required fields
401UNAUTHORIZEDInvalid API key, HMAC signature, timestamp, or nonce
402WALLET_INSUFFICIENT_BALANCEWallet balance too low for this transaction
403FORBIDDENDeveloper KYB not verified or insufficient permissions
403POLICY_VIOLATEDPayment rejected by one or more spending policies
403PAYMENT_COMPLIANCE_BLOCKEDPayment blocked by Veris compliance pre-screen
403PAYMENT_REJECTEDPayment rejected during processing
404RESOURCE_NOT_FOUNDRequested agent, wallet, payment, or policy does not exist
404WALLET_NOT_FOUNDNo wallet found for the specified agent and chain
409WALLET_FROZENWallet is frozen pending compliance review
422VALIDATION_ERRORRequest body fails schema validation
429RATE_LIMITEDToo many requests. Respect the Retry-After header.
202PAYMENT_ESCALATEDPayment requires human approval per escalation policy
502UPSTREAM_ERRORBlockchain RPC or external service failure
503SERVICE_UNAVAILABLEService temporarily unavailable
504GATEWAY_TIMEOUTUpstream request timed out

SDK Error Hierarchy

Both the Python and TypeScript SDKs map HTTP errors to a typed exception hierarchy. This lets you catch specific error categories without inspecting status codes.

Error Hierarchy
OrisError # Base class (all errors) OrisAuthError # 401, 403 (auth/permission) OrisRateLimitError # 429 (includes retryAfter) OrisValidationError # 400, 422 (bad input) OrisPaymentError # Payment-specific failures OrisNetworkError # Connection, timeout, DNS

Every error instance exposes these properties:

PropertyTypeDescription
messagestringHuman-readable error description
statusCodenumberHTTP status code
errorCodestringMachine-readable error code (e.g., POLICY_VIOLATED)
requestIdstringUnique request identifier for support debugging
detailsobjectAdditional context (violated rules, amounts, etc.)

TypeScript Error Handling

error-handling.ts
import { OrisError, OrisAuthError, OrisRateLimitError, OrisPaymentError, OrisValidationError, OrisNetworkError } from '@oris/sdk' try { const payment = await agent.pay({ to: '0x...', amount: 100 }) } catch (err) { if (err instanceof OrisRateLimitError) { console.log(`Rate limited. Retry after ${err.retryAfter}s`) } else if (err instanceof OrisPaymentError) { console.log(`Payment failed: ${err.errorCode}`, err.details) } else if (err instanceof OrisAuthError) { console.log('Check your API key and secret') } else if (err instanceof OrisValidationError) { console.log(`Invalid request: ${err.message}`) } else if (err instanceof OrisNetworkError) { console.log('Network issue, will retry automatically') } }

Python Error Handling

error_handling.py
from oris.errors import ( OrisError, OrisAuthError, OrisRateLimitError, OrisPaymentError, OrisValidationError, OrisNetworkError ) try: payment = agent.pay(to="0x...", amount=100) except OrisRateLimitError as e: print(f"Rate limited. Retry after {e.retry_after}s") except OrisPaymentError as e: print(f"Payment failed: {e.error_code}", e.details) except OrisAuthError: print("Check your API key and secret") except OrisError as e: print(f"Unexpected error: {e}")

Rate Limits

Rate limits are enforced per developer using a Lua token bucket in Redis. The limit applies per second, derived from the per-minute allocation for your plan.

PlanRequests/minAgentsTransactions/mo
Free603500
Growth3002510,000
Scale1,000200100,000
EnterpriseCustomUnlimitedUnlimited

Rate Limit Headers

Every response includes rate limit information in the headers.

HeaderDescription
X-RateLimit-LimitMaximum requests per window
X-RateLimit-RemainingRemaining requests in the current window
X-RateLimit-ResetSeconds until the window resets
Retry-AfterSeconds to wait before retrying (only on 429 responses)

Retry Strategy

The SDKs implement automatic retries with exponential backoff and jitter for retryable errors (429, 502, 503, 504). The default configuration retries up to 3 times with a maximum backoff of 30 seconds.

ParameterDefaultDescription
Max retries3Maximum number of retry attempts
Base delay500msInitial backoff delay
Max backoff30,000msMaximum delay between retries
Jitter0-500msRandom jitter added to each delay
429 handlingRespect Retry-AfterUses the server-provided delay for rate limit errors
Backoff Formula
delay = min(500 * 2^attempt, 30000) + random(0, 500) # Attempt 0: 500-1000ms # Attempt 1: 1000-1500ms # Attempt 2: 2000-2500ms # Attempt 3: 4000-4500ms

Non-Retryable Errors

The following status codes are never retried because they indicate a problem with the request itself:

StatusReason
400Request is malformed. Fix the payload before retrying.
401Authentication failed. Check credentials.
403Permission denied or policy violation. Will not change on retry.
404Resource does not exist.
409Conflict state (e.g., wallet frozen). Resolve the conflict first.
422Validation error. Fix the request body.
Idempotency. POST and PATCH requests include an Idempotency-Key header. If a retry sends the same key, the server returns the original response instead of executing the operation again. This prevents duplicate payments on network failures.