Sevren docsAPI reference

Errors

Every Sevren endpoint answers errors in the OpenAI format with a stable code, except the Anthropic-compatible routes, which use Anthropic's format. Messages are short and fixed; they never repeat your request content.

The error envelope

HTTP
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": {
    "message": "The account's remaining credit cannot cover this request.",
    "type": "insufficient_quota",
    "code": "insufficient_quota"
  }
}

Branch on code, not on message: messages can be reworded, codes do not change. type follows the HTTP status:

Error type by status
Statustype
400, 404, 409, 413invalid_request_error
401authentication_error
402insufficient_quota
403permission_error
429rate_limit_error
500 to 504server_error

During a stream the status is already 200, so errors arrive as an event, data: {"error": {…}}, followed by data: [DONE]. Check each event for an error key.

Request ids

Every response from POST /v1/chat/completions, POST /v1/messages and POST /v1/messages/count_tokens, successful or not, carries an X-Request-Id header such as req_3f9a… (req_ and 32 hex digits). It is random and identifies nothing but that response. Quote it when you report a problem.

When one of these requests answers 400, Sevren keeps it for up to 7 days so the error can be debugged: the request body (up to 64 KiB), the error answer and, if the model refused the request, the model's answer, under that request id. No IP address, API key or account is stored with it. Requests that do not get a 400, and requests the child-safety screen refuses, are never stored. See Privacy.

Model API

POST /v1/chat/completions and GET /v1/models on api.example.com. POST /v1/messages and POST /v1/messages/count_tokens answer the same statuses in Anthropic's shape, {"type": "error", "error": {"type", "message"}}, without the code; see Anthropic API.

Model API error codes
StatusCodeMeaning
400invalid_requestThe body could not be read, the conversation could not be formatted for the model, or the model backend rejected the request.
400invalid_jsonThe body is not one JSON object.
400invalid_modelNo model, or an id longer than 128 characters.
400invalid_messagesmessages is missing, empty or has no user message; a tool message has no tool_call_id; or a tool call is malformed.
400too_many_messagesThe conversation has more messages than allowed.
400invalid_roleA role other than system, user, assistant, tool or developer.
400invalid_contentContent is not a string or text parts, or not valid UTF-8.
400unsupported_contentA non-text part: images, audio or files.
400invalid_toolstools is not an array of function tools, has more than 128, repeats a name, or has a name or parameters schema that is not valid.
400invalid_tool_choicetool_choice is not "none", "auto", "required" or a declared function.
400unsupported_parameterA field such as logprobs, n > 1 or the legacy functions with a value other than its no-op value.
400invalid_parameterA sampling field or max_tokens out of range or of the wrong type.
400context_length_exceededThe prompt does not fit the model's context window.
400content_blockedRefused by the child-safety screen.
400input_too_long_for_screeningThe input is too long for the child-safety screen to check.
401key_requiredA paid model was called without a session token or API key. Sign in or use an API key.
401invalid_api_keyThe key is unknown, or the session token is expired or revoked.
401key_inactiveThe account is suspended. A request that is running when its account is suspended or its key is revoked also ends with this code.
402insufficient_quotaFree tokens and credit cannot pay for the prompt and one output token.
403entitlement_requiredA gated model was called without a valid entitlement.
403model_not_allowedThe account may not use this model.
403free_limit_reachedA free question from the web terminal without an account has a prompt that is too long. Sign in to continue.
404model_not_foundUnknown or disabled model id.
413request_too_largeThe body is over the size limit.
429rate_limitedThe model's request limit was reached. Retry-After is set.
429free_limit_reachedToday's free questions without an account are used up. Retry-After is set to the next 00:00 UTC.
502upstream_errorThe model backend failed.
503model_unavailableNo healthy backend for the model.
503upstream_unavailableThe model backend is overloaded.
503guard_unavailableThe child-safety screen is unavailable; requests are refused until it is back.
503unavailableA database or other dependency is unavailable.
503maintenanceThe operator switched on maintenance. The message is the operator's notice; Retry-After: 60.
504model_not_respondingThe model did not start answering within the first-token limit (180 seconds). Nothing is charged, from credit or free tokens; retry or choose another model.
504timeoutThe request ran longer than the server allows.

Account API

Routes under /account on api.example.com.

Account API error codes
StatusCodeMeaning
400invalid_requestThe body is not valid JSON, has unknown fields or trailing data.
401invalid_sessionMissing, unknown or expired session token. Sign in again.
401invalid_assertionThe wallet assertion is invalid, expired or already used. Sign again.
409wallet_expiredThe auth service no longer has the wallet record. Sign in with the wallet again.
413request_too_largeThe body is over 8 KiB.
429rate_limitedA wallet sign-in or plan refresh budget is used up. Retry-After is set.
503wallet_unavailableWallet sign-in or holder plans are not enabled.
503unavailableThe account service could not complete the request.

Wallet routes

Routes under /auth on auth.example.com.

Wallet route error codes
StatusCodeMeaning
400invalid_requestMissing message or signature, or a malformed body.
400invalid_messageThe text is not a valid EIP-4361 message.
400invalid_resourceNot exactly one urn:sevren:wallet: resource, or one other than urn:sevren:wallet:login.
400unsupported_signatureA signature type that cannot be verified, such as one from a wallet that is not deployed yet.
401unauthorizedBad signature, domain, chain, statement or age, or a nonce that was already used.
413request_too_largeThe body is too large.
429rate_limitedA service-wide per-minute budget for nonces or verifications is used up.
503unavailableThe chain or a database could not be read.

Rate limits and Retry-After

Limits are counted per model, per account or for the whole service. The one exception is free questions without an account, counted under a keyed pseudonym of the IP address that changes every UTC day (see Privacy):

Rate limits
WhatLimit
Token-gated modelsA per-hour request budget per holder, set on each gate.
Free questions in the web terminal7 per UTC day per client without an account, or 25 per signed-in wallet, shared across the free models, plus a service-wide daily budget.
POST /account/login/walletA service-wide per-minute budget.
POST /account/plan/refresh6 per hour per account.
POST /auth/siwe/nonce, POST /auth/wallet/verifyService-wide per-minute budgets.

Retry-After is a number of seconds. The model API sets it to the time left in the current window, the account API to 60, and maintenance responses to 60. The wallet routes do not send it; their budgets reset every minute.

Retrying

  • Retry 429, 502, 503 and 504: wait for Retry-After when it is present, otherwise back off exponentially.
  • Do not retry other 4xx errors unchanged; fix the request, sign in again or add credit first.
  • A retried request is a new request and is charged again if it runs.
PYTHON
import time, requests

def post_with_retry(url, **kwargs):
    for attempt in range(5):
        response = requests.post(url, timeout=320, **kwargs)
        if response.status_code not in (429, 502, 503, 504):
            return response
        wait = response.headers.get("Retry-After")
        time.sleep(int(wait) if wait and wait.isdigit() else 2 ** attempt)
    return response