Docs / Reference

API reference

LaneZero exposes OpenAI-compatible chat and model routes, a public health check, and a session-authenticated management API.

Gateway authentication

Send Authorization: Bearer lz-live_... to both /v1 routes. Provider credentials are separate. The health and documentation routes are public.

POST

/v1/chat/completions

Creates a normalized chat completion. The request body is JSON. Set stream to true for Server-Sent Events.

Request fields

FieldTypeBehavior
modelstringA namespaced provider/model ID. Required unless models is present.
modelsstring[]LaneZero extension. A non-empty ordered fallback chain. Its first entry is the primary model. If model is also sent, it must equal the first entry.
messages requiredobject[]Ordered system, user, assistant, and tool messages. Content may be a string or an array of text parts. Image, audio, and other content-part types are not accepted.
streambooleanOnly literal true selects SSE. Missing or false returns one JSON response.
toolsobject[]Function tools with name, optional description, optional JSON Schema parameters, and optional strict.
tool_choicestring or objectnone, auto, required, or one named function choice.
temperaturenumberForwarded or translated to the selected provider. LaneZero does not impose a range.
top_pnumberForwarded or translated to the selected provider.
max_tokensintegerMaximum output tokens. Anthropic uses catalog metadata or 4096 when this field is absent.
stopstring or string[]One or more stop sequences.
userstringForwarded to OpenAI-compatible providers. Anthropic and Google ignore it.
Other top-level fieldsany JSONPreserved and forwarded to OpenAI-compatible providers. Anthropic and Google ignore unmodeled fields.

LaneZero removes models before calling a provider and sends only that attempt's provider-local model name. For Anthropic and Google, strict is currently not forwarded. Assistant tool-call arguments must contain valid JSON for Anthropic and a JSON object for Google.

Provider-key passthrough headers

HeaderScope and precedence
x-provider-keyCredentials for the primary provider only. The primary provider comes from model or the first models entry.
x-provider-key-<provider>Credentials for one named provider, such as x-provider-key-openai. This takes precedence over the generic header and also works for fallback entries.

If neither matching header is present, LaneZero looks for that provider in the user's vault. Passthrough values are used for the current request and are not stored.

Request example

{
  "models": [
    "anthropic/claude-sonnet-4-6",
    "openai/gpt-4.1"
  ],
  "messages": [
    {"role": "user", "content": "What is the weather in Liverpool?"}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Read current weather",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }
  ],
  "tool_choice": "auto",
  "stream": false
}

Non-streaming response

{
  "id": "chatcmpl_example",
  "object": "chat.completion",
  "created": 1720000000,
  "model": "openai/gpt-4.1",
  "choices": [
    {
      "index": 0,
      "message": {"role": "assistant", "content": "Use soft tyres."},
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 12,
    "completion_tokens": 4,
    "total_tokens": 16
  }
}

usage and system_fingerprint are omitted when the provider does not supply them. created is 0 for Anthropic and Google because those adapters do not receive a compatible creation timestamp.

Streaming response

The response uses Content-Type: text/event-stream and Cache-Control: no-cache. Each chunk is one data: event followed by a blank line. The final success chunk has empty choices and a usage object. LaneZero writes data: [DONE] immediately after that usage chunk.

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","created":1720000000,"model":"openai/gpt-4.1","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","created":1720000000,"model":"openai/gpt-4.1","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","created":1720000000,"model":"openai/gpt-4.1","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl_example","object":"chat.completion.chunk","created":1720000000,"model":"openai/gpt-4.1","choices":[],"usage":{"prompt_tokens":7,"completion_tokens":2,"total_tokens":9}}

data: [DONE]

Tool calls arrive in delta.tool_calls. Function argument text can be split across chunks. Z.ai and Mistral do not receive stream_options; if they omit usage, LaneZero synthesizes a final usage object containing zeros. For the other OpenAI-compatible adapters, LaneZero requests usage but does not synthesize it. If one of those providers ends with [DONE] and no usage chunk, the downstream response currently ends without a final usage chunk or its own [DONE].

A failure after the first downstream chunk cannot switch providers. The HTTP status is already 200. LaneZero sends event: error with the normal error JSON in data:, then sends data: [DONE].

Routing response headers

HeaderMeaning
x-lz-providerThe provider for the selected model or final routing failure. It is unknown when failure happens before a model can be routed.
x-lz-attemptsThe number of upstream calls made. Missing credentials do not increment it.
x-lz-fallbacktrue when the final provider differs from the primary provider. It does not mean simply that attempts are greater than one.

These three headers are added to every /v1/chat/completions response, including gateway authentication and JSON errors. Early failures use unknown, 0, and false.

Routing and fallback

The models array is evaluated from left to right. LaneZero makes at most three upstream calls.

  • Fallback advances after provider 429, provider 5xx, a transport or upstream-protocol failure, or a request timeout.
  • For streaming, fallback is allowed only before the first normalized chunk reaches the client. This includes a timeout waiting for response headers or the first upstream data. Once one chunk exists, later failures become SSE error events.
  • Fallback does not advance after provider 400, 401, 403, or another non-429 4xx.
  • A fallback entry without a passthrough or vaulted credential is skipped. The skip does not consume the three-call budget.
  • An upstream Retry-After delay is honored only when it parses successfully and is no more than two seconds.

The implementation also classifies upstream 3xx responses as retryable upstream errors. Non-streaming timeouts can occur while waiting for the full response body, not only before the first byte.

GET

/v1/models

Returns the embedded chat-model catalog, sorted by namespaced model ID. A gateway bearer key is required.

{
  "object": "list",
  "data": [
    {
      "id": "anthropic/claude-sonnet-4-6",
      "object": "model",
      "owned_by": "anthropic"
    }
  ]
}

The response does not include prices, context limits, or tool capabilities. See Models and providers for catalog details.

GET

/healthz

Public liveness response. The HTTP status and top-level status remain 200 and ok even when the database check is degraded.

Database reachable
{"status":"ok","db":"ok"}
Database check failed
{"status":"ok","db":"degraded"}
No database configured
{"status":"ok","db":"absent"}

Management API

Management routes use the signed browser session created by GitHub or Google OAuth. They do not accept a gateway bearer key as a substitute. Every POST, PUT, and DELETE also requires X-LaneZero-CSRF: 1. They return 503 when LaneZero runs without DATABASE_URL.

For privacy, usage-event records are retained for 90 days by default. Operators can change the window with RACEWAY_USAGE_RETENTION_DAYS; setting it to 0 disables automatic pruning. Account deletion anonymizes usage unless RACEWAY_DELETE_USAGE_ON_ACCOUNT_DELETE=true.

RoutePurpose
GET /mgmt/meReturns the backward-compatible github_login console label; Google-only accounts receive their verified email in that field.
POST /mgmt/keysMints a gateway key from name and optional rate_limit_rps. The default is 10 requests per second. The response is the only time the full key is returned.
GET /mgmt/keysLists key metadata and prefixes. Revoked keys remain in the list. Plaintext keys and hashes are never returned.
DELETE /mgmt/keys/{id}Revokes one active gateway key owned by the session user.
POST /mgmt/keys/{id}/rotateMints a replacement key and returns its plaintext once. The old key remains usable until its configured grace deadline.
GET /mgmt/provider-keysLists stored provider names, masked key prefixes and four-character hints, and creation times.
POST /mgmt/provider-keys/testTests {"provider":"openai","key":"..."} against that provider's read-only models-list endpoint with a 10-second timeout. Returns a translated success or failure reason. Limited to six tests per minute per browser session.
PUT /mgmt/provider-keys/{provider}Validates, encrypts, and creates or replaces one provider key from {"key":"..."}. Connectivity failures store it with stored_unverified: true; provider 401/403 responses reject it. Set save_anyway to true only to bypass validation when the gateway cannot reach the provider.
DELETE /mgmt/provider-keys/{provider}Deletes one stored provider key.
POST /mgmt/signoutClears the current browser session cookie.
POST /mgmt/signout-allClears the cookie and invalidates every previously issued session for the user.
GET /mgmt/sessionsReturns the current stateless browser session's issue time and session epoch.
GET /mgmt/usage?window=24hReturns totals and per-model summaries. Valid windows are 24h, 7d, and 30d.
GET /mgmt/usage/recent?limit=20Returns recent requests. The default limit is 20. The allowed range is 1 through 100.
GET /mgmt/usage/export?format=csvStreams all usage events owned by the account as csv or json.
DELETE /mgmt/accountRevokes and removes gateway keys, crypto-shreds provider keys, disconnects identities, invalidates sessions, and anonymizes usage unless deletion is configured.

Error JSON

Gateway-generated errors use one envelope.

{
  "error": {
    "message": "model `openai/not-in-the-catalog` is not present in the catalog",
    "type": "invalid_request_error",
    "code": "model_not_found"
  }
}
Error typeHTTP statusTypical codes
invalid_request_error400 or 404invalid_json, model_not_found, invalid_request, management validation and not-found codes
authentication_error401invalid_api_key, missing_provider_key, invalid_session
rate_limit_error429rate_limit_exceeded
upstream_error502upstream_error, oauth_upstream_error
timeout_error504timeout
service_unavailable_error503auth_backend_unavailable, account_backend_unavailable, vault_unavailable, database_disabled
not_implemented_error501provider_not_implemented
server_error500oauth_configuration_error

See Errors and retries for triggers and caller guidance. Malformed management JSON or query extraction can be rejected by the web framework before this envelope is built.

Limits and spend caps

Rate limits are enforced per gateway key across the authenticated /v1 routes. The configured requests-per-second value is also the burst size. Exceeding it returns 429 with rate_limit_error.

Spend caps are advisory. They use estimated provider cost for the current calendar month. They never block a request. The current wire header is:

x-lz-spend-cap: true

x-lz-spend-cap appears after an authenticated /v1 request when the asynchronously refreshed estimate is greater than the configured cap.