Reference

API documentation

One base URL, 2 models, and the request shape you already use. This page documents everything the API does — nothing more.

Setup

AXON is a router: it forwards OpenAI-shaped requests to whichever upstream provider is configured for the model you name. Two things have to exist before any call succeeds — a provider (a base URL plus that provider's key) and at least one enabled model bound to it.

Once a model exists, point any OpenAI client at the base URL below and use an API key created in the console.

# Every endpoint lives under this prefix
https://your-axon-host/api/v1

# Endpoints implemented by this build
POST /api/v1/chat/completions
GET  /api/v1/models

Authentication

Create a key under Console → API keys. The plaintext is shown once at creation; the server stores only a SHA-256 hash and cannot recover it. Send it as a bearer token.

Authorization: Bearer ax_live_xxxxxxxxxxxxxxxxxxxx
A revoked key and a deleted key both return 401. Revoking keeps the row for audit; deleting removes it entirely.

Chat completions

POST /api/v1/chat/completions takes the standard OpenAI body. Fields listed below are forwarded; anything else in the body is passed through untouched to the upstream.

FieldTypeDescription
modelreqstringAn id from this instance's catalog, e.g. "claude-opus-5". An unknown id returns 404 — the router never silently substitutes a different model.
messagesreqMessage[]Non-empty array of { role, content }. Roles are forwarded to the upstream unchanged.
streambooleanServer-sent events when true. Defaults to false.
temperaturenumberForwarded to the upstream only when present. No default is injected.
max_tokensintegerForwarded to the upstream only when present.
curl https://your-axon-host/api/v1/chat/completions \
  -H "Authorization: Bearer $AXON_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-opus-5",
    "messages": [{"role": "user", "content": "Hello"}]
  }'

The response is OpenAI-shaped, plus an axon object carrying the request id, the upstream model that served it, measured latency and the cost computed from this instance's price table.

Streaming

Set stream: true and the upstream's SSE frames are forwarded through byte-for-byte. After the upstream finishes, AXON appends one extra frame with the recorded totals so a client can display real cost without a second request.

data: {"axon":{"request_id":"req_9f2c…","ttft_ms":412,
  "total_ms":3180,"prompt_tokens":24,"completion_tokens":186,
  "cost_usd":0.0000431,"provider_id":"prv_1a2b…"}}
If the upstream fails mid-stream the connection errors and the request is logged with the failure — partial output is never presented as a complete answer.

List models

GET /api/v1/models returns the catalog this instance actually exposes, so a client can render a model picker without hardcoding ids. Requires a valid key. An empty instance returns an empty array.

{
  "object": "list",
  "data": [
    {
      "id": "claude-opus-5",
      "object": "model",
      "owned_by": "Palz",
      "context_window": null,
      "max_output_tokens": null,
      "pricing": { "input": 0, "output": 0, "unit": "usd_per_1m_tokens" }
    }
  ]
}

Errors

Errors carry the real status code and the upstream's own message. When an upstream returns a body, it is included verbatim as error.upstream_body rather than being replaced with a generic string.

FieldTypeDescription
401authentication_errorMissing, invalid, revoked or deleted API key.
400invalid_request_errorBody is not JSON, or model/messages are missing or malformed.
402insufficient_creditsThe account's credit balance is zero. Nothing is sent upstream and nothing is charged.
403account_suspendedThe account is suspended by an administrator. The reason is in the message.
429quota_exceededThe plan's credit quota for the current rolling window is exhausted. Retry-After gives the seconds until the window frees up.
429rate_limit_exceededThe plan's requests-per-minute limit was reached. Retry-After is 60.
404invalid_request_errorNo provider on this instance exposes the requested model id.
502upstream_errorThe upstream was unreachable (DNS, TLS, timeout, refused) or returned a non-JSON body.
503upstream_errorThe model or its provider is disabled, or credentials are missing.
otherupstream_errorAny other status the upstream returned is passed through unchanged, including its message.
{
  "error": {
    "message": "Unknown model 'gpt-9'. No provider in this instance exposes it.",
    "type": "invalid_request_error"
  }
}

Usage & cost

Every attempt — success or failure — writes one row to your request log with its status, measured latency, token counts and computed cost. The console's usage charts are SQL aggregates over those rows, which is why a new account shows zeros rather than sample data.

Token counts come from the upstream's usage object when it provides one. If an upstream omits usage, AXON estimates from character length and the log reflects that estimate — it is not presented as an exact billed figure.

Not implemented

This build is deliberately narrow. The following are not implemented, and the API does not pretend otherwise:

FieldTypeDescription
Automatic failoverplannedOne model maps to exactly one provider. A failing upstream returns its error; no second provider is tried.
Prompt cachingplannedNo cache-aware discount is applied. A call is billed on prompt + completion tokens at the configured rate.
Self-serve top-upplannedThere is no payment processor. Credits are added by an instance administrator, and every movement is on the ledger.
Per-key spend capsplannedLimits apply to the account, not to individual keys. Every key on an account draws from the same balance and quota.
Tool calling passthroughuntestedExtra body fields are forwarded to the upstream, but tool schemas are not validated by AXON.