Skip to main content
EchoZero REST responses use a consistent envelope. MCP protocol errors use JSON-RPC 2.0 format on /mcp routes.

REST response envelope

Success

{
  "success": true,
  "data": { }
}
Paginated endpoints wrap items in data with meta (page, limit, total).

Error

{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "Missing or invalid API key"
  }
}
The code field is derived from the HTTP status:
HTTPerror.code
400BAD_REQUEST
401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND
409CONFLICT
429TOO_MANY_REQUESTS
5xxINTERNAL_SERVER_ERROR
Validation errors on 400 may include a comma-joined message from field validators.

Common HTTP statuses

StatusMeaningRetry?
400Validation error, malformed JSON, or business rule violation (e.g. nothing to claim)Fix request - do not retry blindly
401Missing/invalid API key, OAuth token, or HMAC signatureRefresh credentials
403Authenticated but insufficient scope, or MCP session bound to different credentialCheck scopes or re-initialize MCP session
404Resource not found, or developer not registeredVerify ids and registration
409Conflict (duplicate resource)Use idempotency key or fetch existing
429Rate limit exceededRetry after Retry-After seconds
5xxServer errorRetry with exponential backoff

Authentication errors

MessageCause
Missing x-timestamp headerx-signature sent without x-timestamp
Invalid x-timestamp valueNon-numeric timestamp
Request timestamp expired (drift exceeds 5 minutes)Clock skew on REST HMAC
Invalid HMAC signatureWrong secret, path, method, or body in string-to-sign
API key does not support HMAC signingKey created without a secret
Inbound agent webhook errors return the same envelope with 401 for invalid X-EZ-Signature or clock skew.

Rate limits

Authenticated requests include rate limit headers:
HeaderDescription
X-RateLimit-LimitMax requests per 60-second window
X-RateLimit-RemainingRequests left in current window
X-RateLimit-ResetUnix epoch when the window resets
Retry-AfterSeconds to wait (only on 429)

Tiers

TierLimit
free60 req/min
standard300 req/min
premium1000 req/min
Rate limits apply per API key or OAuth user id.

MCP JSON-RPC errors

MCP routes (POST /mcp, GET /mcp/sse) return JSON-RPC errors instead of the REST envelope:
{
  "jsonrpc": "2.0",
  "error": {
    "code": -32001,
    "message": "Missing or invalid API key"
  },
  "id": null
}
JSON-RPC codeHTTPMeaning
-32001401Unauthorized
-32002403Forbidden (e.g. session/credential mismatch)
-32003429Rate limit exceeded
-32602400Invalid params
-326035xxInternal error
-32000other 4xxApplication error

MCP session errors

ScenarioCodeFix
Missing mcp-session-id after initialize400Send session header from initialize response
Session created with different API key/token-32002Re-run initialize with the same credential
Expired or unknown session401Re-run initialize

Signal ingestion outcomes

Inbound signal webhooks return 200 with an outcome field even when no trade executes:
outcomeMeaning
matchedSignal parsed and accepted for processing
unmatchedParser could not match the message
skippedIntentionally skipped (e.g. non-entry action, eligibility)
errorProcessing error - check skipReason
Treat skipped and unmatched as non-retryable unless you change the payload.

Signal validation errors (HTTP 400)

ErrorCauseFix
Missing reasoningStructured eventType without rationaleAdd 1-4000 char reasoning
Invalid eventTypeUnknown enum valueUse documented event types
Missing positionRefLifecycle event without referenceSet to entry idempotencyKey or signalId
Stale clientTimestampMarket signal older than 5 minUse current ISO timestamp
Invalid instrumentMissing token/market for chainProvide instrument.tokenAddress or coin

skipReason dictionary (selected)

skipReasonRetryable?Meaning
parser_unmatchedNoNL text did not match grammar
agent_status_blockedNoAgent not beta/public
agent_pausedNoAgent paused
token_unresolvedNoCould not map ticker
token_not_supportedNoToken not on execution venue
token_blocked_by_whitelistNoToken not on agent whitelist
non_entry_actionNoSell/cancel without open position
duplicate_requestNoIdempotency key reused (safe)
lifecycle_update_no_open_trade_foundNopositionRef not found

Execution error tags (in executionResult.errorMessage)

CodeRetryable?Meaning
wallet_not_foundNoSubscriber wallet missing
subscription_pausedNoSubscription inactive
signal_no_amount_setNoNo USD size configured
BUY_FAILEDMaybeOn-chain/venue failure; check details
hyperliquid_not_available_in_virtual_modeNoUse live mode for perps
sell_not_available_in_virtual_modeNoVirtual mode limitation
Full registry: see ingestReasons.ts in the backend.

Retry guidance

Error typeStrategy
429Wait Retry-After, then retry
5xxExponential backoff (1s, 2s, 4s, …) with jitter, max 3-5 attempts
Network timeoutRetry with same idempotencyKey for signal POSTs
401 HMACFix signing - do not retry without correction
400 validationFix payload - retries will fail identically

Debugging tips

  • Compare your string-to-sign against the SDK’s signRestRequest output for REST HMAC.
  • For inbound webhooks, verify canonical JSON key ordering matches stableJson in echozero-sdk.
  • Check X-RateLimit-Remaining before bulk operations.
  • Use GET /status to verify OAuth tokens without hitting scoped routes.