InferMux

InferMux REST API

One OpenAI-compatible endpoint in front of every model InferMux routes to — plus a small Bearer-authenticated JSON API for the rest of this app.

Pointing an AI agent at this API?

Hand it the LLM-ready Markdown version — self-contained instructions an agent can follow with just a URL and an API key.

Open /docs/llm/api.md

Getting started

Base URL

All endpoints live under https://infermux.net/api/v1. https://infermux.net is the origin you were given (scheme + host, e.g. https://example.com). Do not add a trailing slash. That same path is an OpenAI-compatible base URL: point any OpenAI SDK at https://infermux.net/api/v1 with one of our keys and /chat/completions and /models work unchanged.

Authentication

Every endpoint requires an API key sent as a Bearer token: Authorization: Bearer sk_your_key_here. Keys always start with sk_. A missing or invalid key returns 401. There are two kinds. Router keys (sk_mr_…, created under API keys) can be scoped to specific models or collections, given an expiry and a spend cap — those are the ones to hand to an app or mint through the login widget. Account keys (sk_…, under Settings) authenticate their owner with no model restrictions.

Routing & collections

The model field of a completion takes either an upstream model id (anthropic/claude-3.5-sonnet) or one of your collections as collection/{slug}. A collection is an ordered bucket of models; with auto-route on we try them in order, falling through on failure or rate limit. When a collection mixes vision and text models, an image request is analysed by the vision models first and the result — even an error — is passed to the text models, which write the final answer. The router block in the response says exactly what ran.

Billing

Prices are the upstream router's prices plus a platform markup (2% by default, set per provider by an admin) and are exposed on GET /models in USD per token. Each call is charged to the key owner's prepaid credits unless an active subscription covers that model, in which case it draws from the plan's hourly/daily/weekly token allowance instead. 402 means out of credits or the key hit its spend cap. Free models cost nothing either way. Note on subscriptions: a plan is primarily for Dave (the hosted coding agent, below). Unless the admin enabled "also cover direct API calls" on the plan, calls you make straight to /chat/completions with your own key are billed to credits even while you are subscribed.

Dave — the hosted coding agent (WebSocket)

Dave is a coding agent whose reasoning loop runs on this server while every tool call (read/write/edit files, ls/find/grep, shell commands) is executed by the small dave client on the user's own machine, inside the project it was started in. The server never touches its own filesystem or shell on the agent's behalf; the client is the security boundary (refuses paths outside the workspace, asks before network / git commit / package installs, never runs sudo). Transport: one WebSocket at https://infermux.net/api/v1/dave/ws (ws:// or wss://), authenticated by sending Authorization: Bearer sk_… on the upgrade request (401/402/429 are returned as plain HTTP before the upgrade). Frames are JSON. The client first sends {"type":"hello","protocol":1,"client":{…},"workspace":{"root":"/abs/path","name":"proj"},"model":"optional/model-id"} and gets {"type":"ready","sessionId":…,"model":{…},"tools":[…],"billing":{…}}. Then: client → {"type":"prompt","id":"p1","text":"…"} / steer / abort / tool.result / tool.stream / pong / bye; server → event (text_delta, thinking_delta, tool_start, tool_end, message_end, …), tool.request ({"callId","method":"fs.read|fs.write|fs.edit|fs.list|fs.glob|fs.grep|proc.exec","params":{…}}), tool.cancel, prompt.done, ping, error, bye. Sending hello with "resume":"<sessionId>" re-attaches to a session after a dropped connection. Model calls made by the loop are metered as source: "dave" and are what a subscription covers. GET /api/v1/dave returns the gateway URL, protocol version, tool list, client downloads and the models your key may use; the full protocol is in src/lib/dave/protocol.ts and docs/DAVE.md.

Content type

Responses are JSON unless noted (file download returns raw bytes). Request bodies are JSON (Content-Type: application/json) except file upload, which is multipart/form-data.

Rate limiting

Requests are rate limited per API key. When you exceed a limit you get 429 (or 403 if the limit is configured to block) with an error message and, when applicable, a Retry-After header (seconds). Back off and retry.

Errors

The model-router endpoints (/models, /chat/completions, /collections, /usage, /subscription, /credits) return the OpenAI error envelope: { "error": { "message": "...", "type": "...", "code": "..." } }. The older endpoints return a flat { "error": "..." }. Both use matching HTTP status codes (400 bad input, 401 unauthenticated, 402 out of credits, 403 forbidden/out of scope, 404 not found, 413 payload too large, 429 rate limited, 500 server error).

Your base URL is https://infermux.net. Create API keys under Profile → API Keys.

Endpoints

GET
/api/v1/health

Health check

Confirms the API is up and your key is valid. Handy as a first call to verify credentials and connectivity.

Auth: Bearer tokenAccess: Any valid API key.

Request

curl https://infermux.net/api/v1/health \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "status": "healthy",
  "timestamp": "2026-07-19T12:00:00.000Z",
  "uptime": 1234.56,
  "version": "1.0.0",
  "apiKey": "My key",
  "userId": "usr_...",
  "message": "API is running successfully"
}
GET
/api/v1/stats

Account & API usage stats

Returns the calling user together with API-usage counters (requests today / this week / this month, error rate, API-key count).

Auth: Bearer tokenAccess: Any valid API key (scoped to the key owner).

Request

curl https://infermux.net/api/v1/stats \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "user": { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "createdAt": "..." },
  "apiStats": {
    "totalApiKeys": 2,
    "requestsToday": 14,
    "requestsThisWeek": 98,
    "requestsThisMonth": 412,
    "errorRate": "1.20%",
    "errorCount": 5
  },
  "meta": { "timestamp": "...", "apiKey": "My key" }
}
GET
/api/v1/users

List users

Lists users. A regular key returns only its own user record; an admin key returns all users with pagination.

Auth: Bearer tokenAccess: Any valid API key (admin keys see all users; others see themselves).
NameInTypeReq.Description
limitqueryintegernoPage size, 1–100 (default 10). Admin only; ignored for non-admins.
offsetqueryintegernoRows to skip (default 0). Admin only.

Request

curl "https://infermux.net/api/v1/users?limit=20&offset=0" \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "users": [
    { "id": "usr_...", "email": "you@example.com", "name": "You", "role": "user", "emailVerified": null, "createdAt": "..." }
  ],
  "meta": { "limit": 20, "offset": 0, "total": 1, "apiKey": "My key" }
}
POST
/api/v1/users

Create user (scaffold)

Admin-only endpoint scaffold for creating a user. Ships as a stub in this starter — it validates input and echoes it back rather than persisting. Fill in real creation logic before relying on it.

Auth: Bearer tokenAccess: Admin API keys only (others get 403).
NameInTypeReq.Description
emailbodystringyesNew user email.
namebodystringyesNew user display name.
rolebodystringno'user' (default) or 'admin'.

Request

curl -X POST https://infermux.net/api/v1/users \
  -H "Authorization: Bearer sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"email":"new@example.com","name":"New User","role":"user"}'

Response

{
  "message": "User creation endpoint - implementation needed",
  "requestedData": { "email": "new@example.com", "name": "New User", "role": "user" },
  "apiKey": "My key"
}
  • This is a template stub — no user is actually created yet.
POST
/api/v1/files

Upload a file

Uploads a file and stores its raw bytes. Use this instead of a form/Server Action for any real upload (Server Actions cap the body at ~1MB; this endpoint does not). Send `multipart/form-data` with a single `file` field.

Auth: Bearer tokenAccess: Any valid API key (the file is owned by the key owner).
NameInTypeReq.Description
fileformfileyesThe file to upload (multipart field name must be "file").

Request

curl -X POST https://infermux.net/api/v1/files \
  -H "Authorization: Bearer sk_your_key_here" \
  -F "file=@./photo.png"

Response

{
  "id": "fil_...",
  "filename": "photo.png",
  "url": "/api/v1/files/fil_..."
}
  • Default max size is 100MB (configurable via MAX_FILE_SIZE). Oversized uploads return 413.
  • The returned `url` is the Bearer-gated download endpoint below.
GET
/api/v1/files/:id

Download / preview a file

Streams the raw file bytes with the stored Content-Type. Because it is Bearer-gated you cannot put it directly in an `<img src>`; fetch it with the token and build an object URL client-side.

Auth: Bearer tokenAccess: Any valid API key.
NameInTypeReq.Description
idpathstringyesFile id returned by the upload endpoint.

Request

curl https://infermux.net/api/v1/files/fil_your_file_id \
  -H "Authorization: Bearer sk_your_key_here" \
  --output downloaded-file

Response

Raw binary body with the stored `Content-Type` and `Content-Disposition: inline; filename="..."`. Returns `404 { "error": "File not found" }` if unknown.
DELETE
/api/v1/files/:id

Delete a file

Deletes a file owned by the calling key.

Auth: Bearer tokenAccess: Any valid API key (only the owner may delete).
NameInTypeReq.Description
idpathstringyesFile id to delete.

Request

curl -X DELETE https://infermux.net/api/v1/files/fil_your_file_id \
  -H "Authorization: Bearer sk_your_key_here"

Response

{ "deleted": true }   // { "deleted": false } with status 404 if not found / not owned
GET
/api/v1/models

List available models

Every model this key may use, in the OpenAI `list` shape, with prices already including the platform markup. Your collections are listed too, as `collection/{slug}` pseudo-models you can pass straight to /chat/completions.

Auth: Bearer tokenAccess: Any valid key. A scoped router key sees only the models it is allowed to use.
NameInTypeReq.Description
searchquerystringnoSubstring match on id, name or description.
categoryquerystringnoComma-separated tags to require, e.g. `programming,vision`.
input_modalityquerystringnoComma-separated required input modalities, e.g. `image`.
freequerybooleanno`true` returns only zero-cost models.
toolsquerybooleanno`true` returns only models supporting tool calling.
min_contextqueryintegernoMinimum context window in tokens.
max_prompt_pricequerynumbernoMaximum prompt price in USD per million tokens.
sortquerystringno`name` (default), `newest`, `price-asc`, `price-desc`, `context-desc`, `context-asc`.
limitqueryintegernoPage size (default: all matches).
offsetqueryintegernoRows to skip (default 0).
include_collectionsquerybooleanno`false` omits your collections from the list.

Request

curl "https://infermux.net/api/v1/models?category=vision&free=true" \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "object": "list",
  "data": [
    {
      "id": "collection/free-vision",
      "object": "model",
      "owned_by": "you",
      "name": "Free vision models",
      "collection": { "id": "col_...", "auto_route": true }
    },
    {
      "id": "anthropic/claude-3.5-sonnet",
      "object": "model",
      "owned_by": "openrouter",
      "name": "Anthropic: Claude 3.5 Sonnet",
      "context_length": 200000,
      "architecture": { "input_modalities": ["text", "image"], "output_modalities": ["text"] },
      "categories": ["vision", "tools", "long-context"],
      "pricing": { "prompt": "0.00000306", "completion": "0.0000153", "request": "0", "image": "0" }
    }
  ],
  "meta": { "total": 2, "returned": 1, "collections": 1 }
}
  • Prices are USD per token and already include the markup — this is what you are charged.
  • The catalogue is mirrored from the upstream router every 3 hours.
POST
/api/v1/chat/completions

Chat completion (OpenAI-compatible)

The main endpoint. Accepts a standard OpenAI chat-completions body. `model` is either an upstream model id (`anthropic/claude-3.5-sonnet`) or one of your collections as `collection/{slug}`. With auto-route on, a collection is walked in order and falls through to the next model on failure or rate limit; if the collection mixes vision and text models, an image request goes to the vision models first and their result — even an error — is handed to the text models, which produce the answer. Set `stream: true` for a standard SSE stream.

Auth: Bearer tokenAccess: Any valid key, subject to its model/collection scope, expiry and spend cap.
NameInTypeReq.Description
modelbodystringyesModel id, or `collection/{slug}`.
messagesbodyarrayyesOpenAI messages. Content may be a string or an array of `text`/`image_url` parts.
streambodybooleanno`true` streams `text/event-stream` chunks, ending with a `router.summary` frame and `[DONE]`.
toolsbodyarraynoTool definitions. Auto-route only considers tool-capable models when present.
temperaturebodynumbernoPassed through to the upstream model, like any other sampling parameter.

Request

curl -X POST https://infermux.net/api/v1/chat/completions \
  -H "Authorization: Bearer sk_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{"model":"collection/free-vision","messages":[{"role":"user","content":"What is in this image?"}]}'

Response

{
  "id": "gen-...",
  "object": "chat.completion",
  "model": "meta-llama/llama-3.3-70b-instruct",
  "choices": [{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }],
  "usage": { "prompt_tokens": 42, "completion_tokens": 128, "total_tokens": 170 },
  "router": {
    "requested": "collection/free-vision",
    "resolved": "meta-llama/llama-3.3-70b-instruct",
    "collection": "free-vision",
    "auto_route": true,
    "attempts": 2,
    "stages": ["vision", "primary"],
    "charged_usd": 0.00021,
    "covered_by_subscription": false
  }
}
  • The extra `router` block is additive — every standard OpenAI field is still present.
  • Charges come from your prepaid credits unless an active subscription covers the model.
  • `402` means out of credits or the key hit its spend limit; `403` means the key is not scoped to that model.
GET
/api/v1/collections

List your collections

Your collections and their ordered members. Pass the `model` value of any of them to /chat/completions to route through it.

Auth: Bearer tokenAccess: Any valid key (collections-scoped keys see only the collections they may use).

Request

curl https://infermux.net/api/v1/collections \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "object": "list",
  "data": [
    {
      "id": "col_...",
      "model": "collection/free-vision",
      "name": "Free vision models",
      "slug": "free-vision",
      "auto_route": true,
      "models": [
        { "position": 0, "id": "qwen/qwen-2-vl-7b-instruct", "vision": true, "free": true, "available": true },
        { "position": 1, "id": "meta-llama/llama-3.3-70b-instruct", "vision": false, "free": true, "available": true }
      ]
    }
  ]
}
GET
/api/v1/usage

Token & spend statistics

Tokens in/out, request counts and spend for the hour, day, week, month, year and all-time, each with the instant its window resets. With `?window=` you get one window plus a bucketed series and the top models by spend.

Auth: Bearer tokenAccess: Any valid key (scoped to the key owner).
NameInTypeReq.Description
windowquerystringnoOne of `hour`, `day`, `week`, `month`, `year`, `ever`. Omit for all windows at once.

Request

curl "https://infermux.net/api/v1/usage?window=day" \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "usage": {
    "day": {
      "window": "day",
      "requests": 128,
      "successes": 126,
      "errors": 2,
      "promptTokens": 402113,
      "completionTokens": 88420,
      "totalTokens": 490533,
      "costUsd": 1.284,
      "since": "2026-07-19T00:00:00.000Z",
      "resetsAt": "2026-07-20T00:00:00.000Z"
    }
  },
  "credits": { "balanceUsd": 18.72, "lifetimeToppedUpUsd": 20, "lifetimeSpentUsd": 1.28 }
}
GET
/api/v1/subscription

Subscription allowances

Your active plan and, for each of the hour / day / week windows, the token limit, how much is used, how much is left and exactly when it resets.

Auth: Bearer tokenAccess: Any valid key (scoped to the key owner).

Request

curl https://infermux.net/api/v1/subscription \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "subscribed": true,
  "plan": { "id": "pln_...", "name": "Pro", "price_usd": 50, "scope": "all" },
  "subscription": { "id": "sub_...", "status": "active", "current_period_end": "2026-08-19T12:00:00.000Z" },
  "allowances": [
    { "window": "hour", "token_limit": 600000, "tokens_used": 12500, "tokens_remaining": 587500, "resets_at": "2026-07-19T13:00:00.000Z" },
    { "window": "day",  "token_limit": 6000000, "tokens_used": 90200, "tokens_remaining": 5909800, "resets_at": "2026-07-20T00:00:00.000Z" },
    { "window": "week", "token_limit": 30000000, "tokens_used": 402000, "tokens_remaining": 29598000, "resets_at": "2026-07-20T00:00:00.000Z" }
  ]
}
  • `subscribed: false` means usage is billed against prepaid credits instead.
  • `plan.covers_direct_api` tells you whether the plan pays for direct `/chat/completions` calls too, or only for Dave sessions.
GET
/api/v1/dave

Dave gateway info

Everything a `dave` client (or someone setting one up) needs: the WebSocket gateway URL and protocol version, the tools the hosted agent can call, download links for the client binaries (when CI has built them), how this key's sessions are billed, the default model and the tool-capable models the key may run the agent on.

Auth: Bearer tokenAccess: Any valid key (scoped to the key owner).

Request

curl https://infermux.net/api/v1/dave \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "protocol": 1,
  "gateway": { "url": "wss://example.com/api/v1/dave/ws", "path": "/api/v1/dave/ws", "auth": "Authorization: Bearer <sk_mr_… or sk_…> on the upgrade request" },
  "tools": ["read", "write", "edit", "ls", "find", "grep", "bash"],
  "billing": { "mode": "subscription", "plan": "Pro", "covers_direct_api": false },
  "default_model": "openai/gpt-4o-mini",
  "models": [{ "id": "openai/gpt-4o-mini", "name": "OpenAI: GPT-4o-mini", "context_length": 128000 }],
  "downloads": [{ "arch": "macosx-arm", "label": "macOS (Apple silicon)", "filename": "dave", "url": "https://…/dave" }],
  "downloads_available": true,
  "downloads_note": null,
  "install": { "source": "bun run dave/src/main.ts --server <BASE_URL> --key <sk_…>", "binary": "dave login --server <BASE_URL> --key <sk_…> && dave" }
}
  • The WebSocket itself is documented under "Dave — the hosted coding agent" above; it is not an HTTP endpoint.
GET
/api/v1/dave/sessions

Your Dave sessions

The caller's recent Dave sessions, most recent first: model, workspace name/root as reported by the client, client version/platform, prompt and tool-call counters (including how many calls the client refused) and status.

Auth: Bearer tokenAccess: Any valid key (scoped to the key owner).
NameInTypeReq.Description
limitquerynumbernoMax rows (1–100, default 20).

Request

curl "https://infermux.net/api/v1/dave/sessions?limit=10" \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "object": "list",
  "data": [
    {
      "id": "ses_…", "status": "ended", "live": false,
      "model": "openai/gpt-4o-mini", "thinking_level": "medium",
      "workspace": { "root": "/home/me/project", "name": "project" },
      "client": { "version": "0.1.0", "platform": "darwin/arm64" },
      "prompts": 3, "tool_calls": 17, "tool_calls_denied": 1,
      "started_at": "2026-08-20T09:00:00.000Z", "last_seen_at": "2026-08-20T09:12:41.000Z",
      "ended_at": "2026-08-20T09:12:41.000Z", "end_reason": "client_bye"
    }
  ]
}
GET
/api/v1/credits

Credit balance

Prepaid balance and lifetime totals for the key owner, plus this key's own scope, spend and spend limit.

Auth: Bearer tokenAccess: Any valid key (scoped to the key owner).

Request

curl https://infermux.net/api/v1/credits \
  -H "Authorization: Bearer sk_your_key_here"

Response

{
  "balance_usd": 18.72,
  "lifetime_topped_up_usd": 20,
  "lifetime_spent_usd": 1.28,
  "key": {
    "name": "Production app",
    "scope": "collections",
    "spend_limit_usd": 5,
    "spent_usd": 0.42,
    "expires_at": "2026-07-26T12:00:00.000Z"
  }
}
API reference · InferMux