Files
routstr-core/docs/api/endpoints.md
T
9qeklajc f96acbb99c fix: address model-paths review findings
Provider scoping (items 1/2/6):
- Key visibility maps on (model_id.lower(), upstream_provider_id), matching
  refresh_model_maps, so a disable/override row on one provider never leaks
  onto another provider's model, and matching is case-insensitive.

Data safety (items 3/5):
- Degraded OpenRouter fetches (network error, 429, non-200, bad payload)
  return None (unknown) instead of []; a provider whose path set is unknown
  keeps its previously persisted rows instead of being wiped.
- Endpoint payload parsing moved fully inside try, with a list guard, so
  endpoints:null or non-list shapes are swallowed as documented.
- refresh with an empty live upstream list is a no-op; the unfiltered
  DELETE in the prune path is gone (prune now keys off enabled DB rows).

Hot path (items 4/12/14):
- Persist uses chunked bulk INSERTs (one statement per 500 rows) instead of
  per-row ORM adds; redundant ix_model_paths_model_id index dropped.
- Read routes filter in SQL instead of materializing the whole table, and
  output ordering is deterministic (public id + path), independent of rowid.
- Visibility no longer rebuilds fully priced Model objects per override row;
  it reads id/forwarded_model_id/canonical_slug straight off ModelRow.

Path/id contract (items 7/8/9/11):
- discovery_path_for_subprovider/discovery_base_paths hooks on
  BaseUpstreamProvider, overridden by OpenRouterUpstreamProvider, mirror
  _apply_provider_field so discovery and response stamping cannot drift
  (openrouter:OpenRouter now correctly maps to unknown).
- openrouter_author_slug falls back to a slash-containing forwarded_model_id,
  so admin-created alias rows are discoverable.
- public_model_id splits on the first slash, same as get_base_model_id, so
  discovery ids can be sent to chat completions verbatim.

Lifecycle (items 10/13):
- ENABLE_MODEL_PATHS_REFRESH kill switch; interval and flag re-read every
  loop iteration, and the task idles (not exits) while disabled.
- First 429 latches and aborts the remaining fan-out for the cycle; a
  per-cycle cache dedupes fetches across providers sharing a base URL.
- refresh_model_maps prunes paths of disabled/deleted providers so admin
  mutations take effect immediately; rows carry updated_at and both
  endpoints expose it.

Tests (item 15) rewritten through the public refresh entry point with
transport-level httpx.MockTransport fakes, FK enforcement on, and coverage
for the periodic loop. Migration re-chained onto 9c4d8e2f1a6b.
2026-07-26 13:23:31 +02:00

660 lines
12 KiB
Markdown

# API Endpoints
Complete reference for all Routstr API endpoints.
## Overview
Routstr provides OpenAI-compatible endpoints with Bitcoin/eCash payment integration.
### Base URL
All endpoints use the base URL:
```text
https://api.routstr.com/v1
```
### Authentication
All endpoints require authentication via:
- **Bearer Token**: `Authorization: Bearer sk-...` or `Authorization: Bearer cashuAeyJ0...`
- **X-Cashu Header**: `X-Cashu: cashuAeyJ0...` (for direct eCash payments)
See [Authentication](authentication.md) for details.
## Chat
### Create Chat Completion
Send messages to generate model responses.
```http
POST /v1/chat/completions
```
**Request Body:**
```json
{
"model": "gpt-4",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "Hello!"
}
],
"temperature": 0.7,
"stream": false
}
```
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `model` | string | Yes | - | Model ID to use |
| `messages` | array | Yes | - | Array of message objects |
| `temperature` | number | No | 1.0 | Sampling temperature (0-2) |
| `max_tokens` | integer | No | Model default | Maximum tokens to generate |
| `stream` | boolean | No | false | Stream partial responses |
| `top_p` | number | No | 1.0 | Nucleus sampling |
| `n` | integer | No | 1 | Number of completions |
| `stop` | string/array | No | null | Stop sequences |
| `presence_penalty` | number | No | 0 | Presence penalty (-2 to 2) |
| `frequency_penalty` | number | No | 0 | Frequency penalty (-2 to 2) |
**Response:**
```json
{
"id": "chatcmpl-123",
"object": "chat.completion",
"created": 1677652288,
"model": "gpt-4",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 13,
"completion_tokens": 9,
"total_tokens": 22
}
}
```
### Streaming Response
When `stream: true`:
```text
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-123","object":"chat.completion.chunk","created":1677652288,"model":"gpt-3.5-turbo","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
## Completions (Coming Soon)
### Create Completion
**Note: This endpoint is coming soon and not yet available.**
Generate text completion (legacy endpoint).
```http
POST /v1/completions
```
**Request Body:**
```json
{
"model": "gpt-3.5-turbo-instruct",
"prompt": "Once upon a time",
"max_tokens": 50,
"temperature": 0.7
}
```
**Response:**
```json
{
"id": "cmpl-123",
"object": "text_completion",
"created": 1677652288,
"model": "gpt-3.5-turbo-instruct",
"choices": [{
"text": " in a faraway land, there lived a brave knight...",
"index": 0,
"logprobs": null,
"finish_reason": "length"
}],
"usage": {
"prompt_tokens": 4,
"completion_tokens": 50,
"total_tokens": 54
}
}
```
## Embeddings
### Create Embeddings (Coming Soon)
**Note: This endpoint is coming soon and not yet available.**
Generate vector representations of text.
```http
POST /v1/embeddings
```
**Request Body:**
```json
{
"model": "text-embedding-3-small",
"input": "The quick brown fox jumps over the lazy dog",
"encoding_format": "float"
}
```
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `model` | string | Yes | - | Embedding model ID |
| `input` | string/array | Yes | - | Text(s) to embed |
| `encoding_format` | string | No | "float" | Format: "float" or "base64" |
| `dimensions` | integer | No | Model default | Output dimensions |
**Response:**
```json
{
"object": "list",
"data": [{
"object": "embedding",
"index": 0,
"embedding": [0.0023064255, -0.009327292, ...]
}],
"model": "text-embedding-3-small",
"usage": {
"prompt_tokens": 9,
"total_tokens": 9
}
}
```
## Images (Coming Soon)
### Create Image
**Note: This endpoint is coming soon and not yet available.**
Generate images from text prompts.
```http
POST /v1/images/generations
```
**Request Body:**
```json
{
"model": "dall-e-3",
"prompt": "A white siamese cat wearing a space helmet",
"n": 1,
"size": "1024x1024",
"quality": "standard"
}
```
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `model` | string | Yes | - | Model: dall-e-2, dall-e-3 |
| `prompt` | string | Yes | - | Text description |
| `n` | integer | No | 1 | Number of images |
| `size` | string | No | "1024x1024" | Image dimensions |
| `quality` | string | No | "standard" | Quality: standard, hd |
| `style` | string | No | "vivid" | Style: vivid, natural |
| `response_format` | string | No | "url" | Format: url, b64_json |
**Response:**
```json
{
"created": 1677652288,
"data": [{
"url": "https://generated-image-url.com/image.png",
"revised_prompt": "A white Siamese cat wearing a detailed space helmet..."
}]
}
```
## Audio (Coming Soon)
### Create Transcription
**Note: This endpoint is coming soon and not yet available.**
Convert audio to text.
```http
POST /v1/audio/transcriptions
Content-Type: multipart/form-data
```
**Form Data:**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `file` | file | Yes | Audio file (mp3, mp4, mpeg, mpga, m4a, wav, webm) |
| `model` | string | Yes | Model ID (whisper-1) |
| `language` | string | No | Language code (ISO-639-1) |
| `prompt` | string | No | Context prompt |
| `response_format` | string | No | Format: json, text, srt, verbose_json, vtt |
| `temperature` | number | No | Sampling temperature |
**Response:**
```json
{
"text": "Hello, this is the transcribed audio content."
}
```
### Create Translation
**Note: This endpoint is coming soon and not yet available.**
Translate audio to English.
```http
POST /v1/audio/translations
Content-Type: multipart/form-data
```
Same parameters as transcription, but always translates to English.
## Models
### List Models
Get available models and pricing.
```http
GET /v1/models
```
**Response:**
```json
{
"object": "list",
"data": [
{
"id": "gpt-3.5-turbo",
"object": "model",
"created": 1677610602,
"owned_by": "openai",
"permission": [...],
"root": "gpt-3.5-turbo",
"parent": null,
"pricing": {
"prompt": 0.001,
"completion": 0.002,
"unit": "1k tokens"
}
}
]
}
```
### List Model Paths
Get the upstream provider paths each advertised model can be reached through.
This is discovery data only; routing still chooses the provider per request.
```http
GET /v1/models/paths
```
**Response:**
```json
{
"data": [
{
"id": "claude-sonnet-4",
"paths": [
{"path": "anthropic"},
{"path": "openrouter:Anthropic"}
]
}
]
}
```
### List Paths for One Model
Use a query parameter so model IDs containing `/` are handled safely. Lookup is
by the public, unqualified model ID: `glm-5v-turbo` resolves
`z-ai/glm-5v-turbo`, and `deepseek-v4-pro` and `deepseek/deepseek-v4-pro`
return the same merged path set.
```http
GET /v1/models/paths/model?model_id=anthropic/claude-sonnet-4
```
**Response:**
```json
{
"data": [
{"path": "anthropic"},
{"path": "openrouter:Anthropic"}
]
}
```
Model IDs in responses are base model IDs: the leading provider prefix such as
`z-ai/` or `openai/` is stripped (the same rule routing uses, so the ID can be
sent back to `/v1/chat/completions` verbatim). Path values match the provider
string stamped on chat-completion responses, such as `anthropic`,
`generic:Anthropic`, `openrouter:Anthropic`, or `unknown` (native OpenRouter
with no usable sub-provider). Responses also carry an `updated_at` Unix
timestamp of the last successful refresh (`null` when no refresh has run).
## Wallet Management
### Create Wallet (Coming Soon)
**Note: This endpoint is coming soon. Currently, you can use Cashu tokens directly as API keys.**
Create a new wallet with eCash deposit.
```http
POST /v1/wallet/create
```
**Request Body:**
```json
{
"cashu_token": "cashuAeyJ0...",
"admin_key": "optional-admin-key"
}
```
**Response:**
```json
{
"api_key": "sk-1234567890abcdef",
"admin_key": "radmin_fedcba0987654321",
"balance": 10000,
"mint": "https://mint.example.com",
"unit": "sat"
}
```
### Get Key Information
Get current balance, consumption data, and child keys for an API key.
```http
GET /v1/balance/info
Authorization: Bearer sk-...
```
**Response:**
```json
{
"api_key": "sk-abc...",
"balance": 8500000,
"reserved": 0,
"is_child": false,
"parent_key": null,
"total_requests": 42,
"total_spent": 1500000,
"balance_limit": null,
"balance_limit_reset": null,
"validity_date": null,
"child_keys": [
{
"api_key": "sk-child1...",
"total_requests": 10,
"total_spent": 500000,
"balance_limit": 1000000,
"balance_limit_reset": "daily",
"validity_date": 1738000000
}
]
}
```
`balance` is the spendable balance used by request admission.
### Check Balance
Get current wallet balance.
```http
GET /v1/wallet/balance
Authorization: Bearer sk-...
```
**Response:**
```json
{
"balance": 8500,
"currency": "sat",
"reserved": 0
}
```
### Top Up Wallet
Add funds to existing wallet.
```http
POST /v1/wallet/topup
Authorization: Bearer sk-...
```
**Request Body:**
```json
{
"cashu_token": "cashuAeyJ0..."
}
```
**Response:**
```json
{
"balance": 18500,
"amount_added": 10000,
"currency": "sat"
}
```
### Withdraw Funds
Withdraw balance as eCash.
```http
POST /v1/wallet/withdraw
Authorization: Bearer sk-...
```
**Request Body:**
```json
{
"amount": 5000,
"mint": "https://mint.example.com"
}
```
**Response:**
```json
{
"cashu_token": "cashuAeyJ0...",
"amount": 5000,
"mint": "https://mint.example.com"
}
```
### Create Child Key
Creates one or more child API keys that share the parent's balance. Each child key creation costs a fixed amount (configurable).
```http
POST /v1/balance/child-key
Authorization: Bearer sk-...
```
**Request Body:**
```json
{
"count": 1
}
```
**Parameters:**
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `count` | integer | Yes | - | Number of child keys to create (1-50) |
**Response:**
```json
{
"api_keys": ["sk-abc...", "sk-def..."],
"count": 2,
"cost_msats": 2000,
"cost_sats": 2,
"parent_balance": 98000,
"parent_balance_sats": 98
}
```
## Provider Discovery
## Admin Settings
These endpoints are protected by the Admin cookie (`admin_password` set to your configured admin password).
### Get Settings
```http
GET /admin/api/settings
```
Returns the current application settings (sensitive values may be redacted).
### Update Settings
```http
PATCH /admin/api/settings
Content-Type: application/json
```
Body is a partial JSON of settings fields to update. Validated and persisted to the database.
### List Providers
Get available upstream providers.
```http
GET /v1/providers
```
**Response:**
```json
{
"providers": [
{
"name": "openai",
"models": ["gpt-4", "gpt-3.5-turbo"],
"endpoints": ["chat/completions", "completions"],
"status": "active"
}
]
}
```
### Provider Info
Get specific provider details.
```http
GET /v1/providers/{provider_name}
```
**Response:**
```json
{
"name": "openai",
"display_name": "OpenAI",
"description": "Official OpenAI API",
"models": [
{
"id": "gpt-4",
"name": "GPT-4",
"context_window": 8192,
"pricing": {
"prompt": 0.03,
"completion": 0.06,
"unit": "1k tokens"
}
}
],
"endpoints": ["chat/completions", "completions", "embeddings"],
"features": ["streaming", "function_calling"],
"status": "active"
}
```
## Rate Limiting
All endpoints are subject to rate limiting:
- **Per minute**: 60 requests
- **Per hour**: 1000 requests
- **Per day**: 10000 requests
Rate limit information is included in response headers.
## Next Steps
- [Errors](errors.md) - Error handling reference
- [Authentication](authentication.md) - Auth details
- [Integration Guide](../client/integration.md) - Code examples