Errors

One error shape, a named code for every failure, and a docs link in each.

Every error, on every endpoint, is the same shape:

json
{
  "error": {
    "code": "account_not_funded",
    "message": "The venue account 0x7e15…51f9 holds $0.00; this venue requires at least $5 to start.",
    "docs_url": "https://docs.superior.trade/runtime/credentials",
    "details": null
  }
}
  • code — stable, machine-readable, never reworded. Branch on this.
  • message — human-readable, specific to the instance (real values, not placeholders).
  • docs_url — the page that explains the fix. Agents: fetch it as markdown via /md/….
  • details — structured extras where they exist (validation line numbers, coverage windows).

Every response — success or error — also carries an x-request-id header. Quote it in any support conversation; it's how we find your exact request in our logs.

Code registry

HTTPCodeAreaNotes
401missing_api_keyauthNo x-api-key header
401invalid_api_keyauthUnknown or revoked
429rate_limitedauthHonor retry-after — see rate limits
400email_not_allowedaccountDisposable/blocked domain
400invalid_otpaccountWrong or superseded code
400otp_expiredaccountCode older than 10 minutes
429too_many_attemptsaccount5 failed tries burns the code
409key_limit_reachedaccount10 active keys max
400insufficient_held_balanceaccountWallet withdrawal exceeds held_usd
400no_login_walletaccountVerify a login wallet before withdrawing
400idempotency_key_requiredaccountWithdrawals must send the header
400unknown_venuecontext
400unknown_symbolcontext
404data_unavailablecontextCovered window in details
400window_too_largecontextSplit the request
400unsupported_framework_venueruntimeCheck the venue matrix
400strategy_invalidruntimeFramework diagnostics in details
400config_invalidruntimeOffending key named in details
400credential_in_configruntimeSecrets go in credentials, never config
409name_takenruntimeDeployment name is unique per account — a retried create hitting this means the first attempt succeeded
400credential_scope_unsaferuntimeMaster/withdrawal-capable key rejected by default; override with allow_unscoped_credential + acknowledgement fields (credentials)
400credentials_missingruntimeAttach credentials before starting live
400credentials_invalidruntimeVenue rejected the key (revoked agent, bad permissions)
402account_not_fundedruntimeBelow the venue's min_deposit_usd
409deployment_limit_reachedruntimePlan cap — see usage
429limit_exceededruntimeConcurrent backtest slots full
409positions_openruntimeOn delete; force requires body acknowledgement
409already_runningruntime
409not_runningruntime
execution_timeoutruntimeAsync: appears as the execution resource's error.code with status: "timed_out", not an HTTP response (executions)
script_errorruntimeAsync: the resource's error.code with status: "failed"; exit_code + stderr tail in details
500internal_errorserverOur fault — x-request-id please
503runtime_unavailableserverControl plane can't reach the cluster; running strategies are unaffected

Design commitments

  1. Distinct problems get distinct codes. "Not onboarded" is not "not funded"; an agent must be able to pick the right fix without parsing prose. (This is a direct lesson from the current API, where a missing agent wallet surfaced as a funding error.)
  2. Codes are append-only. New failure modes add codes; existing codes never change meaning.
  3. 5xx means us, 4xx means the request. A venue rejecting your key is a 4xx (credentials_invalid) — it's actionable by you. Our cluster being unreachable is a 5xx — retry later, we're paging someone.
  4. Creates are retry-safe; money movement demands it. POST /runtime/deployments, /runtime/backtests, and /runtime/executions accept an Idempotency-Key header (kept 24h; replays return the original response). POST /wallet/withdraw requires it.
  5. Destructive overrides are never a query flag. Deleting around open positions takes an explicit body acknowledgement (force + acknowledge_positions_open) — never a query flag.