With deployment profiles, chat model
selects a configured alias or exact instance ID. x-werk-session-id binds a
conversation to a replica; busy profiles return HTTP 429 with a zero-capacity
queue policy. GET /werk/v1/deployments provides authenticated inventory, plans,
slot counts and worker diagnosis. Runtime-control routes require an exact
x-werk-deployment header in this mode.
Werk1112 exposes OpenAI-compatible and Anthropic Messages subsets together with Werk-native media, discovery, output and job contracts. It also provides small compatibility surfaces for ComfyUI’s hosted OpenAI nodes and AUTOMATIC1111 clients.
Not every route is OpenAI wire-compatible. This page labels each contract explicitly.
This document describes the current repository. Until a tested OpenAPI file is added, the Axum router, request structs and API tests are the executable source of truth.
Current surface:
The default server address is:
http://127.0.0.1:11434
OpenAI-compatible clients normally configure:
http://127.0.0.1:11434/v1
Generate a key and start the server:
werk auth api-key generate
export WERK_API_KEY="replace-with-generated-key"
werk serve --model CHAT_MODEL --image-model IMAGE_MODEL
Authentication is required by the CLI unless the server is explicitly started
with --allow-unauthenticated.
| Class | Meaning |
|---|---|
| OpenAI-compatible subset | Common OpenAI request and response shapes are implemented, but only documented fields are supported. |
| Anthropic Messages subset | Text/images, streaming, client tool cycles and native token counting; documented backend and protocol limits apply. |
| OpenAI-inspired | The path or general purpose resembles OpenAI, but Werk adds or changes request/response behavior. |
| Werk-native | The route exposes Werk tasks, routing, estimates, plans, jobs or outputs directly. |
| Werk Protocol 1.0 | Versioned runtime-control envelope with typed capability, state, memory and prefill/decode semantics. |
| Comfy alias | A compatibility path used by ComfyUI hosted nodes. |
| A1111 subset | A deliberately small AUTOMATIC1111-compatible surface. |
Do not infer compatibility with an entire upstream API from the path prefix.
| Class | Method | Path | Success behavior |
|---|---|---|---|
| OpenAI-compatible | GET | /v1/models |
Installed model list |
| OpenAI-compatible | GET | /v1/models/{id} |
One model summary |
| OpenAI-compatible subset | POST | /v1/chat/completions |
JSON completion or SSE stream |
| Anthropic Messages subset | POST | /v1/messages |
JSON message or named SSE events, including client tool calling |
| Anthropic Messages subset | POST | /v1/messages/count_tokens |
Backend-native input token count |
| OpenAI / Anthropic Files subsets | POST | /v1/files |
Multipart upload; principal-scoped persistent file |
| OpenAI / Anthropic Files subsets | GET | /v1/files |
Paginated file inventory |
| OpenAI / Anthropic Files subsets | GET | /v1/files/{id} |
File metadata |
| OpenAI / Anthropic Files subsets | DELETE | /v1/files/{id} |
Delete uploaded bytes and metadata |
| OpenAI Files subset | GET | /v1/files/{id}/content |
Uploaded bytes; Anthropic input files are not downloadable |
| OpenAI-compatible extended | POST | /v1/images/generations |
Synchronous Base64 or persisted Werk URL |
| Werk JSON | POST | /v1/images/edits |
Synchronous JSON image edit/inpaint |
| Comfy alias | POST | /proxy/openai/images/generations |
Same handler as image generation |
| Comfy alias | POST | /proxy/openai/images/edits |
Authenticated 501; multipart is not implemented |
| Werk-native | POST | /v1/videos/generations |
202 persisted job |
| Werk-native | POST | /v1/audio/generations |
202 persisted job |
| OpenAI-inspired | POST | /v1/audio/speech |
Raw audio or 202 job |
| OpenAI-inspired | POST | /v1/audio/transcriptions |
Synchronous Werk JSON |
| OpenAI-inspired | POST | /v1/audio/translations |
Synchronous Werk JSON |
| Werk-native | GET | /v1/capabilities |
Model/task/runtime discovery |
| Werk-native | GET | /v1/tools |
Function schemas for all tasks and job controls |
| Werk-native | POST | /v1/tools/call |
Explicit function invocation; chat response or correlated media job |
| Werk-native | GET | /v1/parameters |
Task/model parameter schema |
| Werk-native | GET | /v1/outputs/{id} |
Persisted bytes |
| Werk-native | POST | /v1/jobs |
202 generic canonical task |
| Werk-native | GET | /v1/jobs/{id} |
Persisted JobRecord |
| Werk-native | DELETE | /v1/jobs/{id} |
Cooperative cancellation record |
| A1111 subset | POST | /sdapi/v1/txt2img |
Synchronous embedded images |
| A1111 subset | GET | /sdapi/v1/sd-models |
Installed image models |
| A1111 subset | GET | /sdapi/v1/options |
Current compatibility model selection |
| A1111 subset | POST | /sdapi/v1/options |
Update compatibility model selection |
| A1111 subset | GET | /sdapi/v1/progress |
Coarse idle/active state |
| Werk Protocol 1.0 | GET | /werk/v1/info |
Version, active backend and protocol limits |
| Werk Protocol 1.0 | GET | /werk/v1/capabilities |
Truthful runtime capability statuses |
| Werk Protocol 1.0 | GET | /werk/v1/memory |
Live host/accelerator telemetry and managed accounting |
| Werk Protocol 1.0 | GET | /werk/v1/states |
Principal-scoped runtime-state page |
| Werk Protocol 1.0 | POST | /werk/v1/states/{id}/actions |
Dry-run or explicit state action |
| Werk Protocol 1.0 | POST | /werk/v1/states/prune |
Explicitly selected state-prune summary |
| Werk Protocol 1.0 | GET | /werk/v1/experts |
Capability-gated expert metadata page |
| Werk Protocol 1.0 | POST | /werk/v1/experts/actions |
Capability-gated explicit expert action |
| Werk Protocol 1.0 | POST | /werk/v1/prefill |
Capability-gated prefill and opaque handoff |
| Werk Protocol 1.0 | POST | /werk/v1/decode |
Single-use handoff decode |
The /werk/v1 routes are additive and do not replace or reinterpret any
existing /v1, /proxy/openai or /sdapi/v1 route. Their complete envelopes,
DTOs, limits, errors and examples are in the
Werk Protocol 1.0 reference. Architecture and
the exact production-backend capability matrix are in
Runtime persistence and memory architecture.
Protocol clients should send Accept: application/json and
X-Werk-Protocol-Version: 1.0. Responses declare the same protocol version,
disable caching and MIME sniffing, and vary on Accept.
All routes use the same configured API keys.
Normal Werk and OpenAI-style routes accept:
Authorization: Bearer sk-werk-example
or:
X-API-Key: sk-werk-example
A1111 routes additionally accept HTTP Basic authentication with username
werk and the API key as password.
Authentication properties:
CORS is disabled when no browser origins are configured. When enabled, Werk
allows only the configured exact origins, methods GET/POST/DELETE, content and
authentication headers, the OpenAI SDK request headers, and
anthropic-version/anthropic-beta registered by the
router. Response exposure includes x-werk-output-id and the
Anthropic request-id.
The following JSON routes default to 128 MiB and can be configured up to
512 MiB with WERK_API_BODY_LIMIT_BYTES:
POST /v1/chat/completionsPOST /v1/messagesPOST /v1/messages/count_tokensPOST /v1/jobsPOST /v1/audio/transcriptionsPOST /v1/audio/translationsOther JSON routes retain Axum’s smaller default, approximately 2 MiB. The larger chat limit permits inline vision data URLs but remains a bound on the complete JSON body, not just the decoded image.
Werk Protocol mutation, expert, prefill and decode routes have their own fixed
1 MiB body limit. They additionally enforce the smaller typed limits returned
by GET /werk/v1/info.
Base64 expands binary data by roughly one third. Prefer server-local paths when the client and server intentionally share a trusted filesystem.
Every typed image, video and audio JSON request accepts its route-specific fields plus these common fields:
| Field | Type | Purpose |
|---|---|---|
routing |
object | Nested RoutingOverrides object |
backend |
string | Backend preference or auto |
accelerator |
string | Accelerator target such as CUDA, ROCm, MPS or CPU |
device |
string | Concrete device target where supported |
precision |
string | Requested dtype/precision |
quantization |
string | Requested quantization |
profile |
string | User/runtime profile |
quality |
string | Quality preference |
performance_preference |
string | quality, balanced, speed, latency, throughput or memory |
fallback_policy |
string | none, backend or degrade |
parameter_policy |
string | strict, warn or permissive |
allow_cpu_offload |
boolean | Permit model CPU offload |
allow_sequential_offload |
boolean | Permit sequential offload |
allow_component_offload |
boolean | Permit component offload |
allow_disk_offload |
boolean | Permit disk-backed offload |
attention_backend |
string | Requested attention implementation |
compile |
boolean | Request backend compilation |
timeout_seconds |
integer | Execution timeout |
parameters |
object | Canonical parameter overrides |
user |
string | Accepted OpenAI transport field; not a backend parameter |
Flat routing fields override the same values in the nested
routing object.
Parameters may be supplied in several equivalent forms.
Nested:
{
"parameters": {
"video": {
"frames": 121,
"fps": 24
}
}
}
Qualified:
{
"parameters": {
"video.frames": 121,
"video.fps": 24
}
}
Route namespace shorthand:
{
"frames": 121,
"fps": 24
}
An unqualified extra field is prefixed with the current task namespace. A field
already containing a dot remains qualified. A namespace object named
image, video, audio,
tts or stt is unfolded.
Precedence is:
parameters;size, n
and response_format.Under the default strict parameter policy, an explicit field unsupported by the selected adapter rejects the request instead of being silently ignored.
Convenience routes accept a media input as a string:
"./input.wav"
a data URL:
"data:audio/wav;base64,UklGR..."
or an object containing exactly one source:
{"path": "/server/path/input.wav", "mime_type": "audio/wav"}
{"base64": "UklGR...", "mime_type": "audio/wav"}
{"url": "https://example.invalid/input.wav", "mime_type": "audio/wav"}
The transport schema accepts HTTP URLs, but the bundled offline media companion does not fetch them. Server-local paths and Base64 are the currently executable forms for that adapter.
The generic job route uses a tagged canonical input:
{
"modality": "audio",
"role": "input_audio",
"source": {
"kind": "base64",
"data": "UklGR..."
},
"mime_type": "audio/wav"
}
Source kinds are path, url, base64 and
text. Required roles are task-specific.
The generic job route parses every canonical task. Submission still requires the installed model to declare that task. Later execution additionally requires an accepted runtime and successful model load.
| Task | Dedicated route | Generic job | Generic companion status |
|---|---|---|---|
| text-generation | Chat completions | Syntactically accepted | Text runtime dependent |
| text-embedding | None | Syntactically accepted | Text runtime dependent; no OpenAI embeddings endpoint |
| image-understanding | Chat with image parts | Syntactically accepted | VLM/backend dependent |
| image-generation | Images generations | Yes | Executable for registered image pipelines |
| image-editing | Images edits | Yes | Executable for registered image pipelines |
| image-inpainting | Images edits with mask | Yes | Executable for registered image pipelines |
| image-variation | None | Yes | Executable for registered image pipelines |
| image-outpainting | None | Yes | Executable for registered image pipelines |
| image-upscaling | None | Yes | Executable for registered image pipelines |
| video-generation | Videos generations | Yes | Executable for registered video pipelines |
| image-to-video | Videos generations with initial image | Yes | Executable for registered video pipelines |
| video-to-video | None | Yes | Executable only when a registered adapter accepts the model |
| video-inpainting | None | Yes | Executable only when a registered adapter accepts the model |
| video-extension | None | Yes | Executable only when a registered adapter accepts the model |
| video-upscaling | None | Yes | Executable only when a registered adapter accepts the model |
| frame-interpolation | None | Yes | Executable only when a registered adapter accepts the model |
| audio-generation | Audio generations | Yes | Diffusers/Transformers model dependent |
| music-generation | Audio generations | Yes | Transformers/Diffusers model dependent |
| text-to-speech | Audio speech | Yes | Generic Transformers or architecture-specific adapter |
| speech-to-text | Audio transcriptions | Yes | ASR model dependent |
| speech-translation | Audio translations | Yes | Translation-capable ASR model dependent |
| audio event/voice/speaker/language/emotion detection | None | Yes | Whole-clip audio-classification model dependent |
| audio-classification | None | Yes | Audio-classification model dependent |
| audio-captioning | None | Yes | Any-to-any audio model dependent |
| audio-understanding | None | Yes | Any-to-any audio model dependent |
| audio-embedding | None | Yes | Registered processor/model dependent |
| song continuation/variation | None | Yes | Prepared; no generic adapter |
| speaker-diarization | None | Yes | Prepared; no generic adapter |
| voice-conversion | None | Yes | Prepared; no generic adapter |
| stem generation/separation | None | Yes | Prepared; no generic adapter |
| audio-enhancement | None | Yes | Prepared; no generic adapter |
| audio-editing | None | Yes | Prepared; no generic adapter |
“Generic job: yes” means the typed API contract exists. It does not promise an execution adapter.
Returns OpenAI-style model summaries:
{
"object": "list",
"data": [
{
"id": "model-name",
"object": "model",
"created": 0,
"owned_by": "local"
}
]
}
It intentionally does not return the full Werk manifest. Use
werk inspect MODEL for local diagnostics.
Returns the same summary shape for one installed ID, or 404.
Anthropic-compatible text/images/documents, named SSE streaming and complete client-side tool
cycles share the existing model/backend path. Require anthropic-version:
2023-06-01 and the usual Werk API key. See Anthropic clients
for the field matrix, SDK/Curl examples, errors, streaming limits and model tests.
Uses the selected backend tokenizer and chat template for model, messages,
optional system, tools and tool_choice. Returns {"input_tokens": N}
without generating a completion or appending session history. The same
Anthropic authentication/version headers apply. Backend/media restrictions
are listed in Anthropic clients;
unsupported counters return an error rather than a guessed count.
Both chat routes and native counting expand inline documents and file_id
references in a shared layer, before any model/backend execution. Text extraction
works with every text backend, including CUDA; PDF rendering follows actual
vision readiness. werk.documents selects local auto/text/vision processing
and optional OCR. See Documents and file references
for /v1/files contracts, supported formats, installation, quotas and cleanup.
Accepted top-level fields:
| Field | Required | Type | Behavior |
|---|---|---|---|
model |
Conditional | string | May be omitted when the server has a default chat model |
messages |
Yes | array | Chat messages |
stream |
No | boolean | Default false |
stream_options.include_usage |
No | boolean | With streaming, request a final usage chunk before [DONE] |
temperature |
No | number | Backend dependent |
top_p |
No | number | Backend dependent |
max_tokens |
No | integer | Legacy response budget |
max_completion_tokens |
No | integer | Wins over max_tokens |
stop |
No | string or string array | Added to model/template stops |
seed |
No | integer | Backend dependent |
tools |
No | array | OpenAI function-tool definitions; all chat/vision adapters |
tool_choice |
No | string or object | none, auto, required, or a named function selection |
parallel_tool_calls |
No | boolean | Native forwarding or generic protocol validation |
response_format |
No | object | text, json_object, or json_schema; native structured-output support required |
frequency_penalty, presence_penalty |
No | number | [-2, 2]; native adapter support required |
n |
No | integer | 1..16; multiple choices retained through native adapters that support them |
logprobs, top_logprobs |
No | boolean / integer | Native token logprobs; top count 0..20 requires logprobs: true |
logit_bias |
No | object | Token-ID keys with biases in [-100,100]; native support required |
reasoning_effort |
No | string / number | Native effort value; accepted types and levels depend on the backend/model, separately from werk.omlx |
store |
No | boolean | Only false/null supported; no provider completion-storage service |
werk |
No | object | Local omlx runtime controls and documents.mode / documents.ocr |
Unknown top-level fields fail explicitly. Extended requests use a native rich
response path so choice indices, logprobs and usage survive both JSON and SSE.
The ordinary path remains in place when these options are absent. Extended
requests reuse the native worker and its own prefix cache; they do not pass
through Werk’s simple ChatGenerationSession snapshot path.
For tool-bearing requests and the Anthropic endpoint, a context estimate that
exceeds the configured limit is checked against the selected runtime’s native
chat tokenizer when available. Tool schemas, tool-result pairs and request
reasoning options are preserved; no generation is performed for counting. A
real overflow returns HTTP 400 before SSE with code: "context_length_exceeded",
prompt_tokens, max_tokens, context_length, and token_count_method so clients
can trigger compaction. If native counting is unavailable, the conservative
fallback is explicitly labeled token_count_method: "estimate". Small requests
that fit the estimate do not add tokenizer round trips.
Adapter support is checked before an HTTP response or generation begins. An
unsupported extended option returns HTTP 400 with code unsupported_api_options
for both JSON and streaming requests, rather than an error inside an HTTP 200
stream. This applies through automatic and preferred-backend routing as well.
reasoning_effort is forwarded without coercing native values to strings or
forcing them into a global list. llama.cpp accepts strings; vLLM accepts none,
minimal, low, medium, high, xhigh, and max; oMLX accepts strings and
numbers, including numeric strengths used by templates such as Inkling. The
selected runtime/model defines which values are meaningful. Omission or null
preserves runtime defaults.
For the standard levels minimal, low, medium, high, xhigh, and max,
llama.cpp, vLLM and oMLX enable thinking for that request; none disables it.
The worker defaults remain unchanged for later requests. Other native strings
and numbers retain their native semantics and do not implicitly toggle thinking.
oMLX keeps the effort override consistent between the request field and
chat-template kwargs. A standard top-level effort takes precedence over
werk.omlx.thinking when both are supplied.
Reasoning validation errors identify param: "reasoning_effort" and use code
invalid_reasoning_effort. Known closed value sets are included in the message
and error.supported_values; values_scope: "backend" identifies the source of
that list. supported_types describes the expected JSON types. For open model
vocabularies, no exhaustive value list is invented. values_depend_on_model
reminds clients that accepting a value does not guarantee every model implements
its effect. These hints are returned in HTTP 400 JSON before starting SSE, even
when stream: true was requested. For example, an unsupported vLLM effort:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_reasoning_effort",
"param": "reasoning_effort",
"message": "Invalid reasoning_effort for vLLM. Supported values: [\"none\",\"minimal\",\"low\",\"medium\",\"high\",\"xhigh\",\"max\"]. Their effect depends on the model/template.",
"supported_values": ["none", "minimal", "low", "medium", "high", "xhigh", "max"],
"supported_types": ["string"],
"values_scope": "backend",
"values_depend_on_model": true
}
}
| Native adapter | JSON/schema, penalties | Multiple choices / logprobs / logit bias | Anthropic top_k |
Anthropic matched stop_sequences |
|---|---|---|---|---|
| vLLM | Forwarded | Forwarded, actual choices/logprobs checked | Forwarded | Requires actual native stop_reason |
| llama.cpp server | Forwarded | Forwarded, actual choices/logprobs checked | Forwarded | Explicitly unsupported by this adapter |
| oMLX | Forwarded; grammar-fallback warning fails | Explicitly unsupported | Forwarded | Explicitly unsupported by this adapter |
| Other adapters | Explicit error for extended options | Explicit error | Explicit error | Explicit error |
This matrix concerns extended output controls, not document support.
The installed runtime version/model must support the requested field or schema;
native validation failures remain errors. Provider thinking signatures, cache
TTLs/billing and server-side tools are not synthesized from Werk settings.
OpenAI stop retains its existing behavior without claiming a matched-stop
string. Anthropic output_config.format maps JSON Schema into the same native
structured-output path. Structured output is enforced by the native runtime,
not by a second Werk JSON-Schema validator.
Text requests can configure oMLX from a workflow without changing the server’s environment:
{
"model": "mlx-community/DeepSeek-V4-Flash-2bit-DQ",
"messages": [{"role": "user", "content": "Antworte bitte auf Deutsch: Was ist MoE?"}],
"max_completion_tokens": 128,
"werk": {
"omlx": {"thinking": false, "expert_cache_mb": 8192}
}
}
thinking is a JSON boolean: false corresponds to WERK_OMLX_THINKING=0.
reasoning_effort accepts "low", "high", or "max", overriding
WERK_OMLX_REASONING_EFFORT for this request. It is sent to native oMLX and
the chat template. GLM honors this control even though it ignores the thinking
switch; low effort still permits reasoning. Omission preserves the server/model
default. Like thinking, this override reuses the worker and does not reload
weights. Other models must support the corresponding native template argument.
expert_cache_mb is an integer from 0 to 1048576 MiB; 8192 enables the
experimental SSD expert loader with an 8 GiB RAM cache, and 0 explicitly
disables expert offload. ngram_cache_mb independently accepts "auto" or the same integer range:
Auto starts small and grows with row demand within shared memory limits; a positive value caps the cache for supported N-gram tables, and 0 keeps
those tables resident. It requires a verified table layout when positive;
models without N-gram tables cannot use a positive override. Omitted fields inherit the server’s captured
environment settings. Empty werk or omlx objects do not override anything.
Unknown members or invalid types/ranges inside werk are rejected.
Explicit oMLX options select the compatible oMLX route when Werk runs with
--backend auto, and configure the existing oMLX adapter with --backend omlx.
Other explicitly selected backends and image-bearing requests reject these
options instead of ignoring them. The installed model/runtime must still pass
oMLX compatibility checks; expert offload retains its experimental architecture
and version requirements. No environment variables are changed for other
requests. Thinking is applied per request without reloading weights. Changing
either cache budget selects a separate worker configuration and can require a
model load; it does not resize a worker serving another request.
Updated servers advertise api.chat.omlx_options through
GET /werk/v1/capabilities. This capability confirms that the API understands
the fields, not that a model is compatible or loaded. The n8n Text and ComfyUI
Text nodes check it before sending explicit overrides; update and restart an
older Werk server if it is absent. Unchanged workflows that inherit settings
continue to use their existing requests. After generation, the existing expert
nodes address the worker most recently used for that model; already prepared
expert actions remain bound to their original worker. These options create no
named Prefill/KV persistence capability.
The response-token default is 256 when neither field is present. Werk does not silently clamp an explicit response budget; the selected model context and backend remain authoritative and return an actionable error when they cannot satisfy it.
Message content is either a string or an ordered array of content parts. Supported parts are:
| Part type | Fields | Behavior |
|---|---|---|
text |
string text |
Text at this position in the message |
image_url |
image_url URL string, or object with string url and optional detail |
Image input for an image-capable model/backend |
input_image |
same image_url field |
Accepted image-input alias |
The source can be an inline data URL or a URL that the selected runtime can
read. The API preserves content-part order and the detail value. llama.cpp
and vLLM forward that multipart structure to their chat endpoints; the current
MLX-VLM subprocess accepts the image list but does not preserve arbitrary
interleaving or detail. A hint such as detail: "high" does not guarantee an
identical resolution policy across model processors.
Tool-capable messages use the standard OpenAI chat shapes. An assistant
message can have content: null and a tool_calls array. Each call contains
an ID, type: "function", a function name and arguments encoded as a JSON
string. A following message uses role: "tool", the matching tool_call_id
and its result in content. Text, ordered multimodal content arrays and null
content retain their existing representations.
Each tools item contains type: "function" and a function object with a
name, optional description, arbitrary JSON Schema in parameters and optional
strict. A named tool choice has this form:
{"type":"function","function":{"name":"get_weather"}}
All implemented chat and vision adapters support tool requests. llama.cpp and
vLLM use their native OpenAI transport. oMLX uses native parsing for supported
options and a generic protocol otherwise. Candle, Burn, ONNX Runtime, MLX,
MLX-VLM, Transformers and legacy llama adapters render the schemas and full
history in an explicit protocol and validate structured calls in the response.
Generic tool-enabled streaming buffers output before emitting validated calls.
The generic protocol validates choice, function name, JSON arguments and
parallel-call limits; it rejects strict: true because it cannot guarantee
constrained JSON Schema decoding. Native runtime options remain subject to
that runtime’s actual API contract. Tool support is an adapter capability and
does not certify any particular model’s tool-use quality.
Chat requests never execute model-selected functions automatically. Clients
can explicitly invoke Werk media functions through /v1/tools/call; the same
authentication and inference validation apply. Tool-bearing conversations
include schemas in admission estimates and are rejected if oversized instead
of silently breaking assistant/result pairs. See Tool calling.
Image input additionally requires a model manifest that advertises
image-understanding and an eligible vision runtime. Current routes
are compatible llama.cpp VLM GGUF models with one multimodal projector,
optional vLLM for the explicitly supported Qwen-VL/GLM4V architectures, and
the supported MLX-VLM Gemma4 path. Candle remains text-only. See
Vision and visual quality assurance for the exact
matrix, routing behavior and a complete rendered-output inspection example.
Example:
curl -fsS http://127.0.0.1:11434/v1/chat/completions \
-H "Authorization: Bearer $WERK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "local-model",
"messages": [{"role": "user", "content": "Explain runtime routing briefly."}],
"max_completion_tokens": 256
}'
Multimodal example (replace the abbreviated data with a complete Base64 PNG):
curl -fsS http://127.0.0.1:11434/v1/chat/completions \
-H "Authorization: Bearer $WERK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "visual-qa-model",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Report visible clipping and alignment defects."},
{"type": "image_url", "image_url": {
"url": "data:image/png;base64,BASE64_PNG",
"detail": "high"
}}
]
}],
"max_completion_tokens": 512
}'
Tool-call example for a vLLM-backed Werk server:
curl -fsS http://127.0.0.1:11434/v1/chat/completions \
-H "Authorization: Bearer $WERK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen3-coder",
"messages": [{"role": "user", "content": "What is the weather in Berlin?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {"location": {"type": "string"}},
"required": ["location"]
}
}
}],
"tool_choice": "auto"
}'
A compatible model/runtime can return a choice like this. The value of
function.arguments is intentionally a string containing JSON:
{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_weather_1",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Berlin\"}"
}
}]
},
"finish_reason": "tool_calls"
}
After executing the function, send the prior assistant message back unchanged and append its result:
{"role":"tool","tool_call_id":"call_weather_1","content":"{\"temperature_c\":18}"}
With --backend auto, image input participates in server-side
runtime selection. backend, accelerator and Werk media
routing are not chat request fields; constrain the backend when
starting Werk when an exact route is required.
Audio/video chat content remains unsupported. Structured formats, multiple choices, log probabilities and penalties use the explicit native-adapter matrix above. Unknown top-level fields fail validation rather than being ignored.
With stream: true, the response content type is
text/event-stream. The event sequence is:
stream_options: {"include_usage": true} was requested;data: [DONE].The usage chunk has an empty choices array and backend-reported prompt_tokens,
completion_tokens, and total_tokens. It is omitted when generation fails
without a completion event. Cached-token counts and phase timings are not exposed
in this chunk; use backend diagnostics for those measurements.
Output chunks are buffered text pieces rather than necessarily one token each.
Tool-call deltas preserve each call’s index, optional ID and type, partial
function name and partial argument string. Multiple calls and the upstream
finish reason remain distinct; tool arguments are not concatenated into
assistant content. Clients must assemble partial fields by call index as they
would for an OpenAI chat stream.
If generation fails after the HTTP stream starts, the failure is emitted as an error event inside the already successful HTTP response.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Conditional | string | May resolve from the configured/default single image model |
prompt |
Yes | string | Positive prompt |
negative_prompt |
No | string | Negative prompt |
n |
No | integer | Maps to image.num_images |
size |
No | string | WIDTHxHEIGHT or auto |
response_format |
No | string | b64_json/base64 default, or url |
output_format |
No | string | Image encoding requested from the adapter |
style |
No | string | vivid or natural; appended as a prompt hint |
background |
No | string | auto/opaque accepted; transparent rejected |
moderation |
No | string | auto/low accepted as transport compatibility only |
output_compression |
No | integer | Validated 0–100; not forwarded by the current adapter |
partial_images |
No | integer | Must be zero/omitted |
stream |
No | boolean | true is rejected |
The default response embeds Base64 and then cleans up the temporary result.
With response_format: url, the returned relative output URL is
authenticated and retained subject to output retention.
This is a Werk JSON endpoint, not OpenAI multipart.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Yes | string | Installed image model |
prompt |
Yes | string | Edit prompt |
image |
Yes | media input | Source image |
mask |
No | media input | Presence selects image-inpainting |
negative_prompt |
No | string | Negative prompt |
n |
No | integer | Output count |
size |
No | string | WIDTHxHEIGHT |
response_format |
No | string | b64_json/base64 or url |
ComfyUI’s hosted OpenAI multipart edit proxy reaches
/proxy/openai/images/edits, which currently returns 501 with
guidance to use this JSON route.
Always returns 202 with a JobRecord.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Yes | string | Installed video model |
prompt |
Yes | string | Positive prompt |
initial_image |
No | media input | Alias: image; presence selects image-to-video |
negative_prompt |
No | string | Negative prompt |
n |
No | integer | Maps to video.num_videos |
size |
No | string | WIDTHxHEIGHT |
response_format |
No | string | Maps to video.output_format |
Example:
curl -fsS http://127.0.0.1:11434/v1/videos/generations \
-H "Authorization: Bearer $WERK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "wan22-ti2v-5b",
"prompt": "A bird takes flight through warm forest sunbeams.",
"size": "1280x704",
"parameters": {
"video.frames": 121,
"video.fps": 24,
"video.steps": 50,
"video.guidance": 5
},
"precision": "bf16",
"allow_cpu_offload": true
}'
Always returns 202 with a JobRecord.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Yes | string | Installed audio model |
prompt |
Yes | string | Generation prompt |
negative_prompt |
No | string | Model dependent |
task |
No | string | audio-generation default or music-generation |
n |
No | integer | Maps to audio.variations |
response_format |
No | string | Maps to audio.output_format |
Other audio tasks are rejected by this convenience route and should use their
dedicated endpoint or /v1/jobs.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Yes | string | Installed TTS model |
input |
Yes | string | Alias: text |
voice |
No | string | Model/adapter dependent |
speed |
No | number | Model/adapter dependent |
response_format |
No | string | Maps to tts.output_format |
async |
No | boolean | Aliases: background, job |
By default Werk waits for generation, then returns the completed audio bytes with:
Content-TypeContent-Lengthx-werk-output-idThis is completed-file transfer, not incremental speech generation. The temporary result is deleted when the response stream ends or is abandoned, so the output ID header is primarily diagnostic.
With async: true, the route returns a normal persisted JobRecord.
Both routes use Werk JSON rather than OpenAI multipart.
| Field | Required | Type | Notes |
|---|---|---|---|
model |
Yes | string | Installed ASR model |
file |
Yes | media input | Aliases: audio, input_audio |
prompt |
No | string | Maps to stt.initial_prompt |
language |
No | string | Source language hint |
temperature |
No | number | Model dependent |
response_format |
No | string | text/json/srt/vtt/tsv where supported |
Translation fixes stt.operation to translate and
requires a model that actually supports speech translation. A generic CTC ASR
model is not automatically translation-capable.
The synchronous response is the direct media envelope with text embedded in
data[].text. Its temporary output is cleaned after response
construction.
Returns installed declarations and runtime probe information. Declared tasks must be distinguished from available/executable tasks.
Each model includes a task_statuses object keyed like the serialized task
enum (for example music_generation). Every value is a structured readiness
record with status, detail, optional adapter, required_backend,
install_command, fallback_backend, missing_dependencies, and
missing_dependency_groups fields. Dependency groups use any_of routes
whose individual all_of lists distinguish alternatives from packages that
must all be installed.
Only a probe-supplied install command is returned. available_tasks and the
dynamic tools list continue to include only tasks with a genuinely executable
runtime candidate.
Query parameters:
| Parameter | Required | Meaning |
|---|---|---|
task |
Yes | Canonical task name |
model |
No | Installed model for constraints and runtime probing |
backend |
No | Filter runtime candidates; auto leaves all |
Example:
curl -fsS --get http://127.0.0.1:11434/v1/parameters \
-H "Authorization: Bearer $WERK_API_KEY" \
--data-urlencode "task=text-to-speech" \
--data-urlencode "model=qwen3-tts-voice-design"
The response includes:
task_readiness for a model-specific probe;Required body:
| Field | Required | Type |
|---|---|---|
model |
Yes | string |
task |
Yes | canonical task string |
prompt |
Task-dependent | string |
negative_prompt |
No | string |
inputs |
Task-dependent | canonical input array |
| common Werk options | No | routing and parameters |
Example:
curl -fsS http://127.0.0.1:11434/v1/jobs \
-H "Authorization: Bearer $WERK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ast-audioset",
"task": "audio-classification",
"inputs": [{
"modality": "audio",
"role": "input_audio",
"source": {"kind": "path", "path": "/shared/field-recording.wav"},
"mime_type": "audio/wav"
}],
"parameters": {"audio.top_k": 5}
}'
A 202 response means the request resolved and was queued. It does not guarantee that a runtime plan will exist, a dependency will import, the model will load or accelerator memory will suffice.
Returns the complete persisted JobRecord.
Marks a nonterminal job cancelled and returns its JobRecord. It does not delete the record or output files. Cancellation is cooperative: a third-party model call already in progress may continue, but a later result is discarded.
queued
loading
running
encoding
completed
failed
cancelled
The current encoding state is a short lifecycle transition after
service execution, not an encoder progress meter.
Nonterminal jobs found after a server restart are marked failed. They are not resumed.
{
"id": "job-...",
"status": "queued",
"request": {
"model": "model-name",
"task": "video_generation",
"prompt": "...",
"negative_prompt": null,
"inputs": [],
"parameters": {},
"routing": {}
},
"result": null,
"error": null,
"created_unix": 0,
"updated_unix": 0
}
On completion, result contains the complete InferenceResult.
Task parsing accepts hyphenated and underscored input names. Serialized Rust
enum fields currently use snake_case. For example a request may send
music-generation while a JobRecord returns
music_generation. Clients should currently tolerate both.
Synchronous image/edit/transcription/translation routes return:
{
"created": 0,
"data": [
{
"id": "out-...-0",
"url": "/v1/outputs/out-...-0",
"mime_type": "image/png",
"size_bytes": 123,
"width": 1024,
"height": 1024,
"duration": null
}
],
"werk": {
"id": "out-...",
"task": "image_generation",
"model": "model-name",
"runtime": "media-companion-cuda",
"outputs": [],
"effective_request": {},
"estimate": {},
"plan": {},
"backend_metadata": {},
"timings": {},
"warnings": [],
"created_unix": 0
}
}
Depending on route and response format, each data item contains one of:
b64_jsonurltextThe werk member intentionally exposes diagnostics including the
effective request, estimate, plan, runtime attempts and warnings. It is much
larger than a normal OpenAI response extension.
An InferenceResult includes:
Output metadata includes:
The path takes an output ID, not a job ID or result ID. It returns the whole file with its content type and length.
HTTP Range requests and object-storage redirects are not implemented.
Persisted output retention defaults to:
Configure these with:
WERK_OUTPUT_RETENTION_SECONDS
WERK_OUTPUT_MAX_BYTES
Retention is enforced before later inference. Oldest/expired output entries can therefore disappear while an older completed JobRecord still retains stale output metadata.
There is no output deletion endpoint today.
Most established /v1, proxy and compatibility routes use:
{
"error": {
"message": "human-readable explanation",
"type": "invalid_request_error",
"param": null,
"code": null
}
}
Common statuses:
| Status | Typical meaning |
|---|---|
| 200 | Synchronous success, poll success or cancellation record |
| 202 | Job validated and queued |
| 400 | Invalid task/model/parameter, no accepted runtime, or execution/model-load failure |
| 401 | Missing or invalid credentials |
| 404 | Missing model, job or output |
| 413 | Request body over the route limit |
| 500 | Internal task, persistence or response construction failure |
| 501 | Unsupported Comfy multipart image edit proxy |
Current caveats:
invalid_request_error type;code is currently null;Werk Protocol routes do not use this legacy error object. Every /werk/v1
failure retains the protocol version and opaque request ID and returns a typed
code, public message and retryability flag. HTTP status mapping includes 409
for incompatible state, 406 with incompatible_protocol for an incompatible
version or JSON representation, 410 for an expired/consumed handoff, 428 when
an experimental operation lacks opt-in, 429 for bounded resource exhaustion,
501 for an unsupported adapter operation and 503 for a temporarily unavailable
prerequisite. See Werk Protocol errors
for the complete mapping.
The supported compatibility routes are:
POST /sdapi/v1/txt2img
GET /sdapi/v1/sd-models
GET /sdapi/v1/options
POST /sdapi/v1/options
GET /sdapi/v1/progress
txt2img maps the useful core fields:
Sampler, scheduler and ETA compatibility fields can be accepted with warnings while Werk/runtime selection remains authoritative.
Explicitly unsupported features include high-resolution fix, face restoration, seamless tiling, named styles, refiners, scripts/always-on scripts, image saving by the A1111 server and nondefault subseed behavior.
Progress reports coarse idle/active state. It does not invent a percentage or preview image when the backend has no progress callback.
The established inference diagnostic contracts are intentionally transparent but can contain sensitive information:
werk;Werk Protocol runtime state has a narrower public summary. It does not expose
raw credentials, prompts, backend handles, external cache keys or snapshot
paths. Each accepted API key maps through HMAC-SHA-256 to a separate opaque
state namespace and prompt-fingerprint scope. Handoffs are random,
principal-bound, single-use secrets and responses carry Cache-Control:
no-store. These protections do not turn plain HTTP into a secure remote
transport.
Do not expose Werk directly to an untrusted network. Use TLS termination, access controls and a trusted reverse proxy. Prefer dedicated store roots for different trust domains.
The following are not currently implemented:
The generic job route provides typed access to many tasks in the meantime, but it is not a substitute for every ergonomic or upstream-compatible endpoint.
The API documentation roadmap is:
Until then, clients should use capability/parameter discovery, tolerate the documented task-name forms and treat 202 as queued validation rather than execution success.
GET /werk/v1/observability returns a Werk Protocol 1.0 envelope containing
schema-versioned server-wide inference telemetry, bounded recent/active request rows,
physical memory observations and available native worker metrics. It requires
normal Werk authentication and x-werk-protocol-version: 1.0.
GET /metrics exposes the same observations in Prometheus text format 0.0.4.
It requires normal API-key authentication, without the Werk protocol header.
Both endpoints share a two-second backend sampling cache and never load or
reconfigure models. These operational endpoints expose model IDs and aggregate
activity across server clients, but no prompts, generated content or credentials.
Generation and text-analysis requests share the same record type. The optional
requests[].analysis object adds task/runtime/device/precision, weight-cache hit,
load/inference/worker durations, result count and attempt count. Non-applicable
generation fields remain null. Analysis is visible while executing and contributes
to existing request counters; additional Prometheus gauges use the existing
per-model label convention. werk top view customizations affect only the client.
See metric semantics and coverage.
These routes use the normal werk serve authentication and installed model store.
They cannot be used through chat deployment profiles (--deployments). A request
can provide model, or use the server’s --model default. The model’s architecture
and task must match the endpoint. Sending a pooling/decision model to chat returns
an error naming the correct endpoint.
| Endpoint | Contract | Main input |
|---|---|---|
POST /v1/embeddings |
OpenAI-compatible float embedding subset | input: string or string array |
POST /v1/rerank, POST /rerank |
Reranking scores, descending relevance | query, documents: strings or { "text": "..." } objects |
POST /v1/classifications, POST /v1/systemone |
Werk-native classification; Laya-compatible question structure | Laya: state, questions; XLM-RoBERTa classifier: input |
Embedding input is text only. encoding_format must be float; token-ID arrays
and base64 output are not supported. EmbeddingGemma input_type is query or
document (default); Werk applies the corresponding search/document prefix.
dimensions supports 128, 256, 512 or 768 (default). Truncated vectors are
renormalized in FP32. The response has object, data, model, usage and werk.
Reranking accepts top_n and return_documents. results contains original
index and normalized relevance_score; ties preserve input order. texts is an
alias for documents. These endpoints return scores, not generated text.
Laya accepts typed noul, choice and score questions. For choice, criteria
is an object mapping labels to descriptions; for score, it is an ordered list
of descriptions. Each question requires instructions. werk.checkpoint may be
auto, english, multilingual or typed-decisions. Auto prefers a local
multilingual subfolder when present, otherwise the root checkpoint; it does not
change checkpoints based on request language. Standalone local fine-tunes work
without the original bundle’s repository name.
curl http://localhost:11434/v1/rerank \
-H "Authorization: Bearer $WERK_API_KEY" -H 'Content-Type: application/json' \
-d '{"model":"BAAI/bge-reranker-v2-m3","query":"Rust installieren",
"documents":["cargo install installiert Rust-Programme.","Tomaten brauchen Licht."]}'
curl http://localhost:11434/v1/embeddings \
-H "Authorization: Bearer $WERK_API_KEY" -H 'Content-Type: application/json' \
-d '{"model":"google/embeddinggemma-2","input":["Rust installieren"],
"input_type":"query","dimensions":256}'
curl http://localhost:11434/v1/classifications \
-H "Authorization: Bearer $WERK_API_KEY" -H 'Content-Type: application/json' \
-d '{"model":"convaiinnovations/laya","state":{"text":"Die Rechnung wurde doppelt abgebucht."},
"questions":{"billing":{"type":"noul","instructions":"Geht es um eine Zahlung?"}}}'
Optional werk request fields:
| Field | Default | Meaning |
|---|---|---|
backend |
auto |
auto, transformers, vllm, candle; server constraints take precedence |
device |
auto |
CUDA when available, otherwise CPU; explicit cuda/cpu are hard constraints |
fallback_policy |
compatible |
Compatible runtime retries; none disables retries |
batch_size |
16 | 1–128; reduce after out-of-memory errors |
max_length |
512 | 8–8192 tokens, bounded by the checkpoint’s context |
truncate |
false | Explicit opt-in to input truncation; vLLM requires inputs to fit without truncation |
checkpoint |
auto |
Laya checkpoint selection only |
Bodies are limited to 1 MiB. Embeddings/classifiers accept at most 128 texts;
rerankers accept at most 128 documents; Laya accepts 1–32 questions. Invalid input
returns 400, missing models 404, capacity exhaustion 429 and runtime failures 503.
Failure messages include the attempted runtime and mitigation, such as installing
text-analysis, selecting the correct Python, reducing batches or freeing VRAM.
werk.diagnostics records fallbacks, while werk.runtime, device, dtype,
model_cache_hit and timings identify the actual successful execution.
These routes use the existing server bearer authentication. They preserve vendor/task schemas; existing OpenAI-compatible routes retain their contracts.
| Route | Contract |
|---|---|
GET /werk/v1/model-support |
Exact identities, capabilities, sources and honest inference verification status. |
POST /werk/v1/images/flux-3 |
FLUX 3 Image prompt/references/aspect-ratio/resolution → binary PNG/JPEG. |
POST /werk/v1/audio/dialogue |
Eleven v4 text/voice pairs → audio; stream:true streams binary chunks. |
POST /werk/v1/actions/predictions |
Registered DROID policy, instruction, state and synchronized RGB observations → typed 32×8 action targets with units. |
Registered Mistral Large 4 uses /v1/chat/completions, including tools, structured
output and SSE. Vendor keys remain on the server. See examples and limitations.
GET /werk/v1/chat/limits?model=MODEL reports the configured per-request context
and output ceilings without running inference. Clients can increase an initial
output allowance after truncation while respecting those ceilings and the input
token count. Unknown limits remain null; generation defaults are not ceilings.
See the generation limits contract.
Use GET /werk/v1/chat/reasoning?model=MODEL&max_output_tokens=4096 to read the
running model’s tiers, effective default and limits. Send
"werk":{"reasoning":{"tier":"default"}} (or an available tier ID) in chat
requests. Werk1112 resolves the tier for the actual output limit; the same
resolution is used by /werk/v1/chat/count_tokens. Unknown runtimes preserve
the native default and do not advertise fabricated choices. Explicit native
reasoning_effort cannot be combined with this control. See the
reasoning protocol contract
for budget mappings, availability and backward compatibility.