Using the API

Endpoints

The OpenAI-compatible subset the router implements, the fields it honours, the extension fields in usage, and the streaming rules.

ReferenceFor developers

Base URL: https://api.router.superprotocol.com/v1 directly, or http://127.0.0.1:8787/v1 through Gatekeeper. Same paths, same bodies: Gatekeeper is a transparent forward proxy. The contract is the OpenAI API where specified and silent where not; the normative text is docs/contracts/router-api.md in the repository.

Authentication

Authorization: Bearer sk-tee-v1-<43 characters> on every /v1 request. No cookies, no query parameters.

A key is sk-tee-v1- plus 32 random bytes in base64url. The router stores its SHA-256 and the first 12 characters for display; the plaintext is shown exactly once, at creation.

Optional headers: X-Request-Id is echoed back; the response carries X-Confidential-Router-Generation-Id.

Paths

Method and pathStatusNotes
POST /v1/chat/completionsrequirednon-stream and stream: true
GET /v1/modelsrequiredOpenAI list shape plus extension fields; only models within the key's scope
GET /v1/models/{id}requiredone model
POST /v1/completionsrequiredlegacy text completions, same streaming rules
POST /v1/embeddingsoptionalonly when the model's capabilities include embeddings
GET /v1/generation?id=requiredthe metering record of one generation, never any content
GET /v1/evidencerequiredthis deployment's latest published bundle, no key; see Evidence endpoints
GET /v1/evidence/{endpoint}requiredthe same for a named endpoint, including a registered external upstream
GET /v1/invites/{code}extensionwhat an invitation code grants; no key, rate-limited per source address

Unsupported OpenAI paths answer 404:

run against production, 2026-10-12
$ curl -s https://api.router.superprotocol.com/v1/nope{"error":{"message":"Unknown endpoint.","type":"invalid_request_error","code":"not_found"}}

POST /v1/chat/completions

Request: the OpenAI schema. Honoured fields: model, messages, stream, stream_options.include_usage, max_tokens or max_completion_tokens, temperature, top_p, stop, n (1 only), seed, response_format, tools, tool_choice, user. Unknown fields are forwarded to the model backend unchanged. The router never inspects or stores messages.

Response: an OpenAI chat.completion object. id is the router's generation id (gen-<ulid>), model echoes the router model id, and usage is authoritative and extended:

json
{  "id": "gen-01J6…",  "object": "chat.completion",  "created": 1756550000,  "model": "<model-id>",  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "…" }, "finish_reason": "stop" }],  "usage": {    "prompt_tokens": 5454,    "completion_tokens": 362,    "total_tokens": 5816,    "cost_micros": 5450,    "endpoint": "confidential-router",    "evidence_digest": "sha256/…"  }}

The three extension fields live inside usage, where OpenAI SDKs ignore them:

FieldMeaning
cost_microsWhat the generation cost, in integer micro-dollars, debited from the workspace balance
endpointThe router endpoint that served it; an external endpoint's name for a model served elsewhere
evidence_digestThe digest the platform had published for that endpoint at generation time, null if none. A fact about publication, never a verdict

Streaming

Content-Type: text/event-stream, one data: <chat.completion.chunk> per event, a final usage-only chunk (empty choices) when stream_options.include_usage is true, then data: [DONE]. A comment line : ping is sent every 15 seconds while waiting for the first token. Chunks are forwarded as they arrive, with no buffering, through Gatekeeper as well.

An error after the stream started is sent as a last data: event carrying an error object, then [DONE].

[DONE] is where the generation ends for metering. A client that closes the connection after reading it has the whole answer and the row is ok. A client that leaves before it is metered aborted.

GET /v1/models

json
{ "object": "list", "data": [ {  "id": "<model-id>", "object": "model", "created": 1756550000, "owned_by": "confidential-router",  "name": "…", "context_length": 131072,  "pricing": { "prompt_per_1m_micros": 280000, "completion_per_1m_micros": 420000 },  "endpoint": { "name": "…", "hostname": "…", "tee": "Intel TDX (Azure)" },  "capabilities": ["chat", "completions"]} ] }

pricing is micro-dollars per million tokens. endpoint.tee is read from the serving endpoint’s published evidence, not declared by the operator: it is one of a closed set of labels the platform’s own branch map defines, Intel TDX (Azure) on production today. It is null when the evidence names nothing this router recognises, and absent for a model served by an external upstream, because this router reads no evidence label for another deployment. None of these labels says anything about a GPU; see Deployment topology.

GET /v1/generation?id=gen-…

The Generation record for a generation owned by the key's workspace: tokens, cost, model, endpoint, timings and evidence_digest. Never any content.

Metering

Every accepted request creates one generation record at completion, or at abort with what was counted. Debit is prompt_tokens × prompt price + completion_tokens × completion price, per million tokens, rounded up to one micro-dollar, written as one ledger row. The meter is exact: the router asks the backend for usage on every stream and strips the extra chunk if you did not ask for it.

Compatibility promise

/v1 paths and response shapes are stable; extension fields only ever get added. A breaking change would be /v2.