werk1112

HTTP API reference and coverage

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.

Stability and source of truth

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:

Base URL

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.

Compatibility classes

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.

Complete route inventory

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.

Authentication

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

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.

Request body limits

The following JSON routes default to 128 MiB and can be configured up to 512 MiB with WERK_API_BODY_LIMIT_BYTES:

Other 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.

Common Werk media options

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.

Parameter normalization

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:

  1. nested routing values;
  2. flat routing values;
  3. values in parameters;
  4. flat extra parameters;
  5. explicit route transport fields such as 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.

Media input forms

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.

Task coverage

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.

Models

GET /v1/models

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.

GET /v1/models/{id}

Returns the same summary shape for one installed ID, or 404.

Chat completions

POST /v1/messages

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.

POST /v1/messages/count_tokens

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.

Documents and files

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.

POST /v1/chat/completions

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.

oMLX chat options

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.

Messages and token budget

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.

Chat SSE

With stream: true, the response content type is text/event-stream. The event sequence is:

  1. assistant-role chunk;
  2. zero or more content and/or tool-call delta chunks;
  3. finish chunk;
  4. optional usage chunk when stream_options: {"include_usage": true} was requested;
  5. literal 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.

Image generation

POST /v1/images/generations

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.

Image editing and inpainting

POST /v1/images/edits

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.

Video generation

POST /v1/videos/generations

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
  }'

Generated audio and music

POST /v1/audio/generations

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.

Text to speech

POST /v1/audio/speech

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:

This 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.

Speech transcription and translation

POST /v1/audio/transcriptions

POST /v1/audio/translations

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.

Capabilities and parameters

GET /v1/capabilities

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.

GET /v1/parameters

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:

Generic jobs

POST /v1/jobs

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.

GET /v1/jobs/{id}

Returns the complete persisted JobRecord.

DELETE /v1/jobs/{id}

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.

Job states

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.

JobRecord schema

{
  "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.

Direct media response

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:

The 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.

InferenceResult and output metadata

An InferenceResult includes:

Output metadata includes:

Output download and retention

GET /v1/outputs/{id}

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.

Error response

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:

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.

AUTOMATIC1111 subset

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.

Security and privacy considerations

The established inference diagnostic contracts are intentionally transparent but can contain sensitive information:

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.

Known API gaps

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.

Planned contract hardening

The API documentation roadmap is:

  1. add a checked OpenAPI file;
  2. compare registered router operations against OpenAPI in CI;
  3. normalize serialized task names;
  4. separate a compact public response from optional verbose diagnostics;
  5. redact or omit server paths and inline media from persisted/public records;
  6. add explicit job/output retention and deletion behavior;
  7. add architecture-specific API fixtures for image, video, TTS, ASR and generic analysis tasks.

Until then, clients should use capability/parameter discovery, tolerate the documented task-name forms and treat 202 as queued validation rather than execution success.

Observability

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.

Text-analysis endpoints

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.

Official provider and action extensions

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.

Managed reasoning tiers

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.