# HTTP API reference Quartermaster speaks the **OpenAI API** (plus Anthropic's `/v1/messages`), so most clients work by pointing them at your server. This page lists every endpoint with a request you can paste into a terminal. **Base URL** is your API port, `http://127.0.0.1:1250` by default. **Auth** is an API key if you have created one (`Authorization: Bearer qm-...`, or `x-api-key`, or the password half of Basic auth); when no keys exist the API is open and you can drop the header. See *API keys and access*. **Every inference route takes a `model`**, and that is what selects the backend: ask for a model that is not loaded and it is loaded for you, evicting others if VRAM demands it. Use the id from `/v1/models`. **Most routes are proxied**, not reimplemented, so any field the underlying llama.cpp / stable-diffusion.cpp / TTS server accepts passes straight through even if it is not listed here. Every `/v1/...` path below also answers on `/v/...`, for clients that insist on writing their own version prefix. The `/api/...` routes the dashboard uses are deliberately **not** documented here: they are the UI's private surface and change without notice. ## Discovery and health **`GET /v1/models`** - the catalog, OpenAI-shaped, plus a `quartermaster` metadata block per entry. Filtered to what your API key and listener are scoped to. ``` curl http://127.0.0.1:1250/v1/models -H "Authorization: Bearer qm-..." ``` **`GET /health`**, **`GET /wol-health`** - liveness. Answers `OK` without touching a model. ``` curl http://127.0.0.1:1250/health ``` **`GET /api/version`** - version, commit, and the self-update state. ``` curl http://127.0.0.1:1250/api/version ``` **`GET /metrics`** - Prometheus exposition for scraping. ``` curl http://127.0.0.1:1250/metrics ``` ## Text generation **`POST /v1/chat/completions`** - the one most clients use. `stream: true` gives SSE deltas. ``` curl http://127.0.0.1:1250/v1/chat/completions \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","messages":[{"role":"user","content":"Hi"}],"stream":false}' ``` **`POST /v1/messages`** - the Anthropic shape, for clients that speak it. `max_tokens` is required. ``` curl http://127.0.0.1:1250/v1/messages \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","max_tokens":256,"messages":[{"role":"user","content":"Hi"}]}' ``` **`POST /v1/messages/count_tokens`** - how many tokens that request would cost, without running it. ``` curl http://127.0.0.1:1250/v1/messages/count_tokens \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","messages":[{"role":"user","content":"Hi"}]}' ``` **`POST /v1/responses`** - OpenAI's Responses API. ``` curl http://127.0.0.1:1250/v1/responses \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","input":"Write a haiku about VRAM"}' ``` **`POST /v1/completions`** - legacy plain-prompt completion. ``` curl http://127.0.0.1:1250/v1/completions \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","prompt":"The capital of France is","max_tokens":16}' ``` **`POST /completion`** - llama.cpp's own completion endpoint, forwarded verbatim. Use it for llama-server fields that have no OpenAI equivalent. ``` curl http://127.0.0.1:1250/completion \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen3","prompt":"Once upon a time","n_predict":32}' ``` **`POST /infill`** - llama.cpp fill-in-the-middle, for code models that support it. ``` curl http://127.0.0.1:1250/infill \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwen-coder","input_prefix":"def add(a, b):\n return ","input_suffix":"\n"}' ``` ## Embeddings and reranking **`POST /v1/embeddings`** - vectors for one string or an array of them. ``` curl http://127.0.0.1:1250/v1/embeddings \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"nomic-embed","input":["first passage","second passage"]}' ``` **`POST /v1/rerank`** - score documents against a query. Also answers on `/v1/reranking`, `/rerank` and `/reranking`, since clients disagree about the spelling. ``` curl http://127.0.0.1:1250/v1/rerank \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"bge-reranker","query":"how do I evict a model","documents":["unloading frees VRAM","cats are nice"],"top_n":2}' ``` ## Decision models **`POST /v1/systemone`** - typed questions about a state, answered with probabilities. Needs a llama.cpp with System One (upstream b11361+, lemonade ROCm b1343+); see the Decision models article for the question types and the reply shape. ``` curl http://127.0.0.1:1250/v1/systemone -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" -d '{"model":"kev-4b","state":"The build failed on Linux only.","questions":{"area":{"type":"choice","instructions":"Which part broke?","criteria":{"tests":null,"packaging":null,"code":null}}}}' ``` ## Images **`POST /v1/images/generations`** - the OpenAI shape. Returns base64 images in `data`. ``` curl http://127.0.0.1:1250/v1/images/generations \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"sdxl","prompt":"a lighthouse in a storm","n":1,"size":"1024x1024"}' ``` **`POST /v1/images/edits`** - the OpenAI edit shape, `multipart/form-data` rather than JSON. ``` curl http://127.0.0.1:1250/v1/images/edits \ -H "Authorization: Bearer qm-..." \ -F model=qwen-image-edit -F prompt="make it snow" -F image=@photo.png ``` **`POST /sdapi/v1/txt2img`** - the AUTOMATIC1111-style route, and the one with the real knobs: `steps`, `cfg_scale`, `seed`, `sampler_name`, `scheduler`, `lora`, plus hires fix (`enable_hr`, `hr_scale`, `hr_upscaler`, `hr_steps`, `denoising_strength`) and `extra_images` for reference-conditioned models. Images come back base64 in `images`. ``` curl http://127.0.0.1:1250/sdapi/v1/txt2img \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"sdxl","prompt":"a lighthouse in a storm","negative_prompt":"blurry","width":1024,"height":1024,"steps":25,"cfg_scale":5,"seed":-1}' ``` **`POST /sdapi/v1/img2img`** - the same fields plus `init_images` (base64, no `data:` prefix), `denoising_strength`, and for inpainting a `mask` (white repaints, black keeps) with optional `inpainting_mask_invert`. ``` curl http://127.0.0.1:1250/sdapi/v1/img2img \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"sdxl","prompt":"add a red door","init_images":[""],"denoising_strength":0.55,"steps":25}' ``` **`GET /sdapi/v1/loras?model=`** - the LoRAs that model can load, filtered to files that really are LoRAs. ``` curl "http://127.0.0.1:1250/sdapi/v1/loras?model=sdxl" -H "Authorization: Bearer qm-..." ``` **`POST /v1/images/upscale`** - standalone ESRGAN upscale. Not a loaded model: it runs the ncnn upscaler once per request. `image` is a data URL or bare base64, `scale` is 2 to 4 (default 4), `model` is optional. Returns `{"image": "data:image/png;base64,..."}`. ``` curl http://127.0.0.1:1250/v1/images/upscale \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"image":"data:image/png;base64,<...>","scale":4}' ``` **`POST /v1/3d/generations?model=`** - image to 3D mesh (TRELLIS.2). The **image is the request body** (`--data-binary`), which is why the model id is a query parameter here and not a JSON field. Answers with the GLB itself (`model/gltf-binary`), or with `{"file","path","bytes","seconds"}` when `keep=1` / `format=json` is given. Query options: `steps`, `texture_size`, `pipeline`, `shape_only`, `seed`. Setup is more involved than most backends - see *3D generation*. ``` curl -X POST "http://127.0.0.1:1250/v1/3d/generations?model=trellis-2-4b" \ -H "Authorization: Bearer qm-..." -H "Content-Type: image/png" \ --data-binary @subject.png -o subject.glb ``` **`POST /v1/segment`** - SAM segmentation. `image` is bare base64 with no `data:` prefix. Prompt with `text` (a concept, can match several instances), or with `box` as `[x0,y0,x1,y1]` and/or `points` as `[[x,y,label]]` where label 1 is foreground. Returns `{width, height, masks:[{instance_id, score, iou_score, box, png}]}`, each `png` a white-on-black mask. ``` curl http://127.0.0.1:1250/v1/segment \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"sam3","image":"","text":"the dog"}' ``` ## Speech and audio **`POST /v1/audio/speech`** - text to speech. Returns audio bytes, so redirect them to a file. ``` curl http://127.0.0.1:1250/v1/audio/speech \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"kokoro","input":"Hello there","voice":"af_heart","response_format":"wav"}' \ -o out.wav ``` **`POST /v1/audio/transcriptions`** - speech to text, `multipart/form-data` with the audio as `file`. ``` curl http://127.0.0.1:1250/v1/audio/transcriptions \ -H "Authorization: Bearer qm-..." \ -F model=whisper-large -F file=@recording.wav ``` **`GET /v1/audio/voices?model=`** - the voices that model can speak with. The model has to be loaded already. ``` curl "http://127.0.0.1:1250/v1/audio/voices?model=kokoro" -H "Authorization: Bearer qm-..." ``` **`POST /v1/audio/voices`** - register a cloned voice from a WAV, for engines that support cloning. `ref_text` is the transcript of the sample and is optional. ``` curl http://127.0.0.1:1250/v1/audio/voices \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"model":"qwentts","name":"my-voice","wav_b64":"","ref_text":"the words in the sample"}' ``` **`DELETE /v1/audio/voices/{name}?model=`** - remove a cloned voice. ``` curl -X DELETE "http://127.0.0.1:1250/v1/audio/voices/my-voice?model=qwentts" \ -H "Authorization: Bearer qm-..." ``` ## Tools Web search and YouTube executors, so your own agent does not have to wire up SearXNG or yt-dlp. Any valid key works and model scopes do not apply. Full field reference in *Tools API*. **`POST /v1/tools/search`** - a web search, provider passed per call. ``` curl http://127.0.0.1:1250/v1/tools/search \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"query":"llama.cpp release","limit":5,"providers":[{"id":"duckduckgo"}]}' ``` **`POST /v1/tools/youtube/transcript`** - the full transcript with `[m:ss]` timestamps. ``` curl http://127.0.0.1:1250/v1/tools/youtube/transcript \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"url":"https://youtu.be/dQw4w9WgXcQ"}' ``` **`POST /v1/tools/youtube/search`** - search videos, or list a channel with `{"channel":"@handle"}`. ``` curl http://127.0.0.1:1250/v1/tools/youtube/search \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"query":"gguf quantization","limit":5}' ``` **`POST /v1/tools/youtube/comments`** - top-level comments for a video. ``` curl http://127.0.0.1:1250/v1/tools/youtube/comments \ -H "Authorization: Bearer qm-..." -H "Content-Type: application/json" \ -d '{"url":"https://youtu.be/dQw4w9WgXcQ","limit":20}' ``` ## Operations These are **not** API-key routes. They are part of the admin surface, so they answer to this host (and to anything `-admin-allow` names) rather than to a key. **`GET /running`** - which models are loaded right now, with state and launch command. ``` curl http://127.0.0.1:1250/running ``` **`GET /unload`** - unload everything and free the VRAM. ``` curl http://127.0.0.1:1250/unload ``` **`POST /api/models/unload/{model}`** - unload one model, leaving the rest loaded. ``` curl -X POST http://127.0.0.1:1250/api/models/unload/qwen3 ``` **`GET /upstream/{path}`** - proxy straight to the loaded backend's own HTTP server, bypassing the model chain. For reaching a llama-server endpoint Quartermaster does not route itself. ``` curl http://127.0.0.1:1250/upstream/qwen3/props ``` **`GET /logs`**, **`GET /logs/stream`** - the server log, as a snapshot or as a live stream. ``` curl http://127.0.0.1:1250/logs ``` ## Errors Errors are OpenAI-shaped, `{"error": {"message": "..."}}`. `400` is a bad request body, `401` a missing or wrong API key, `404` an unknown model, `501` a route that needs `-generate` mode, and `502` / `503` / `504` mean the backend for that model crashed, was evicted, or is still loading. A retry after a moment is the right response to the last group.