LLM Router API

The workspace exposes the LLM providers it has configured through two vendor-compatible HTTP surfaces, so an existing client or SDK can use those providers without ever holding the provider credentials:

Surface Base URL to give the client Endpoints
OpenAI-compatible <server>/llm-router/openai/v1 GET /models, POST /chat/completions
Anthropic-compatible <server>/llm-router/anthropic GET /v1/models, GET /v1/models/{id}, POST /v1/messages, POST /v1/messages/count_tokens

The Anthropic base URL stops at /anthropic because the Anthropic SDKs append /v1/... themselves; the OpenAI base URL includes /v1 for the same reason.

Calls run as the user the token was minted for, so everything the request reads is filtered by that user's permissions, and the tokens they consume are counted in LLM usage tracking like any other chat turn.

Authentication

Both surfaces authenticate with an endpoint token carrying the matching claim, minted from Manage → Access → Tokens (#/manage/access/tokens) → Add token:

  • OpenAI-compatible API — presented as Authorization: Bearer <token>.
  • Anthropic-compatible API — presented as x-api-key: <token> (the Anthropic convention) or as Authorization: Bearer <token>.

Minting asks which user the token acts as, and the token has the same revocation and last-used tracking as the other token types. A missing or invalid token answers 401.

Models are providers

Each configured provider is advertised as one "model" whose id is the provider's UID. Its display name is accepted as an alias when that name is unambiguous, so either of these resolves:

curl "$SERVER/llm-router/openai/v1/models" \
  -H "Authorization: Bearer $CURIOSITY_TOKEN"
curl "$SERVER/llm-router/openai/v1/chat/completions" \
  -H "Authorization: Bearer $CURIOSITY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
        "model": "<provider-uid-or-display-name>",
        "messages": [{ "role": "user", "content": "Summarise our refund policy." }]
      }'

A model the workspace cannot resolve answers 404 with code: "model_not_found"; a provider that resolves but is not configured answers 503.

Streaming

Set "stream": true on either surface to get server-sent events; leave it off for a single buffered response. Either way the request is bounded by the workspace's chat idle timeout, so a provider that stops producing tokens ends the call rather than hanging.

Using it from a client SDK

from openai import OpenAI

client = OpenAI(
base_url=f"{SERVER}/llm-router/openai/v1",
api_key=CURIOSITY_TOKEN,
)

reply = client.chat.completions.create(
model=PROVIDER_UID,
messages=[{"role": "user", "content": "Summarise our refund policy."}],
)
What this is not

These surfaces relay a completion to a configured provider. They do not run the workspace's own assistant — no tools, no skills, no retrieval over your data. For that, call the chat and agent APIs or a custom endpoint instead.

  • LLM configuration — adding providers and choosing which models the workspace may use.
  • Token scopes — the other bearer-token types and how to scope them.
  • REST API — the workspace's own HTTP surface.
© 2026 Curiosity. All rights reserved.