Skip to content

Chat completion ​

chatCompletion routes text (and optionally image) inputs through large language models. Any model available on OpenRouter is supported, plus Civitai-hosted AIR models. The request and response shapes follow the OpenAI Chat Completions API.

Access paths ​

Two ways to use chat completion, depending on your use case:

PathWhen to use
POST /v1/chat/completionsDrop-in replacement for the OpenAI API. Accepts stream: true for SSE streaming.
chatCompletion workflow stepChain with other steps (imageGen, convertImage, etc.) in a multi-step workflow.

Both paths share the same input schema and produce the same output format.

Basic text completion ​

Via the OpenAI-compatible endpoint ​

http
POST https://orchestration.civitai.com/v1/chat/completions
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "model": "openai/gpt-4o-mini",
  "messages": [
    { "role": "system", "content": "You are a helpful assistant." },
    { "role": "user", "content": "What is the capital of France?" }
  ]
}

Via SubmitWorkflow ​

http
POST https://orchestration.civitai.com/v2/consumer/workflows?wait=60
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "steps": [{
    "$type": "chatCompletion",
    "input": {
      "model": "openai/gpt-4o-mini",
      "messages": [
        { "role": "system", "content": "You are a helpful assistant." },
        { "role": "user", "content": "What is the capital of France?" }
      ]
    }
  }]
}
POST/v2/consumer/workflows
Set your Civitai API token via the Token button in the navbar to enable Try It.
Request body — edit to customize (e.g. swap the image URL or prompt)
Valid JSON

Vision (image inputs) ​

Pass images in user message content parts. Any vision-capable model (e.g. openai/gpt-4o, google/gemini-2.0-flash) can process them.

json
{
  "model": "openai/gpt-4o",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Describe this image in detail." },
      {
        "type": "image_url",
        "image_url": {
          "url": "https://image.civitai.com/.../photo.jpeg",
          "detail": "auto"
        }
      }
    ]
  }],
  "max_tokens": 300
}

detail can be "auto" (default), "low", or "high". The image source can be a public URL, a data URL (data:image/jpeg;base64,...), or raw Base64 — the orchestrator uploads it to blob storage before dispatching the job.

POST/v2/consumer/workflows
Set your Civitai API token via the Token button in the navbar to enable Try It.
Request body — edit to customize (e.g. swap the image URL or prompt)
Valid JSON

Image generation ​

Set "modalities": ["image", "text"] on the request to generate images through /v1/chat/completions. The response carries an images array on the assistant message, where each entry is a base64 data URI — the same shape OpenRouter uses, so existing OpenRouter-style SDK code works unmodified.

http
POST https://orchestration.civitai.com/v1/chat/completions
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "model": "google/gemini-2.5-flash-image",
  "messages": [
    { "role": "user", "content": "A cat in a teacup, soft window light" }
  ],
  "modalities": ["image", "text"],
  "image_config": {
    "aspect_ratio": "1:1",
    "image_size": "1K"
  }
}

Response:

json
{
  "id": "chatcmpl-...",
  "model": "google/gemini-2.5-flash-image",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "",
      "images": [{
        "type": "image_url",
        "image_url": { "url": "data:image/png;base64,iVBOR..." }
      }]
    },
    "finish_reason": "stop"
  }]
}

Image editing (multi-turn) ​

Pass a prior generated image (or any image URL / data URI) as a content part on a user message and the request routes through the engine's edit operation:

json
{
  "model": "google/gemini-2.5-flash-image",
  "messages": [{
    "role": "user",
    "content": [
      { "type": "text", "text": "Make it a dog instead of a cat." },
      { "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
    ]
  }],
  "modalities": ["image", "text"]
}

Supported models ​

modelEngineOperations
google/gemini-2.5-flash-imageGemini 2.5 Flash Imagecreate, edit
openai/gpt-image-1OpenAI gpt-image-1create, edit
openai/dall-e-3OpenAI DALL·E 3create
openai/dall-e-2OpenAI DALL·E 2create, edit
black-forest-labs/flux.2-devFlux 2 Devcreate, edit
black-forest-labs/flux.2-flexFlux 2 Flexcreate, edit
black-forest-labs/flux.2-proFlux 2 Procreate, edit
black-forest-labs/flux.2-maxFlux 2 Maxcreate, edit
black-forest-labs/flux.2-kleinFlux 2 Kleincreate, edit

The provider prefix (google/, openai/, black-forest-labs/) is optional — short names like gemini-2.5-flash-image, gpt-image-1, flux-2-dev are also accepted. Unknown model names with modalities: ["image"] return 400 with the supported list.

Civitai AIR URNs ​

Pass a Civitai AIR URN as model to use a community checkpoint. The ecosystem segment of the AIR (sd1, sdxl, flux1, anima) selects the engine; the AIR is forwarded as the checkpoint:

json
{
  "model": "urn:air:sdxl:checkpoint:civitai:101055@128078",
  "messages": [{ "role": "user", "content": "A cyberpunk samurai" }],
  "modalities": ["image", "text"],
  "image_config": { "aspect_ratio": "1:1", "image_size": "1K" }
}
EcosystemEngineOperationsNotes
sd1SD 1.5 (sd-cpp)create, variantPass an input image to trigger img2img variant.
sdxlSDXL (sd-cpp)create, variantSame — img2img variant when an input image is supplied.
flux1Flux 1 (sd-cpp)create, editEdit operation accepts up to 2 input images; width/height clamped to 832–1216.
animaAnima (sd-cpp)createAnima checkpoints; no img2img path through chat-completions.

Other ecosystems (zimage, qwen, wan, flux2) hardcode their checkpoints — pass the matching named model instead (e.g. flux-2-dev) and use the imageGen workflow step directly when you need to override the checkpoint.

image_config ​

FieldValuesEffect
aspect_ratio1:1, 16:9, 9:16, 4:3, 3:4, 21:9Sets width/height ratio. OpenAI engines snap to their nearest allowed size. Gemini ignores (always 1024×1024).
image_size0.5K, 1K, 2K, 4KApproximate megapixel target. Engines clamp to their supported range.
n1–10Number of images. Falls back to the top-level n. Engines clamp to their supported max.

For full per-engine knobs (samplers, LoRAs, guidance scales, advanced operations), use the imageGen workflow step directly instead — chat-completions is a thin facade tuned for SDK compatibility, not a full passthrough of every engine parameter.

Multi-turn conversations ​

Include prior turns as assistant messages to maintain context:

json
{
  "model": "openai/gpt-4o-mini",
  "messages": [
    { "role": "system",    "content": "You are a concise assistant." },
    { "role": "user",      "content": "Write a haiku about the ocean." },
    { "role": "assistant", "content": "Waves crash endlessly,\nSalt..." },
    { "role": "user",      "content": "Now write one about mountains." }
  ],
  "temperature": 0.7
}
POST/v2/consumer/workflows
Set your Civitai API token via the Token button in the navbar to enable Try It.
Request body — edit to customize (e.g. swap the image URL or prompt)
Valid JSON

Streaming ​

Via /v1/chat/completions ​

Set "stream": true and handle Server-Sent Events (SSE). The response is a stream of data: {...} lines ending with data: [DONE]:

http
POST https://orchestration.civitai.com/v1/chat/completions
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "model": "openai/gpt-4o-mini",
  "messages": [{ "role": "user", "content": "Tell me a short story." }],
  "stream": true
}

Via workflow step ​

Set stream: true in the step metadata field:

json
{
  "steps": [{
    "$type": "chatCompletion",
    "metadata": { "stream": true },
    "input": {
      "model": "openai/gpt-4o-mini",
      "messages": [{ "role": "user", "content": "Tell me a short story." }]
    }
  }]
}

When streaming is enabled, the orchestrator stores the raw NDJSON chunks in a streaming blob and assembles them into the standard ChatCompletionOutput shape for the workflow output.

Tool use (function calling) ​

Define tools as JSON Schema function definitions. The model decides when and how to call them:

json
{
  "model": "openai/gpt-4o",
  "messages": [
    { "role": "user", "content": "What is the weather in Paris?" }
  ],
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get current weather for a city",
      "parameters": {
        "type": "object",
        "properties": {
          "city": { "type": "string" }
        },
        "required": ["city"]
      }
    }
  }],
  "tool_choice": "auto"
}

When the model calls a tool, the assistant message in the response contains a tool_calls array instead of (or alongside) content. Submit the tool result back as a tool message:

json
{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "{\"temperature\": 18, \"condition\": \"sunny\"}"
}

Add a tool of type civitai:web_search and the orchestrator runs the tool loop for you: the marker expands into two function tools — web_search(query, limit?) (SearXNG-backed web search) and fetch_page(url) (page content as markdown) — and when the model calls them, the orchestrator executes the searches, feeds the results back, and returns the final grounded answer as the workflow output. No client-side tool handling is needed.

json
{
  "model": "google/gemini-2.5-flash",
  "messages": [
    {
      "role": "user",
      "content": "Does the name 'Ada Lovelace' refer to a real person? Search the web before answering."
    }
  ],
  "tools": [
    { "type": "civitai:web_search" }
  ]
}

Notes:

  • Not supported with stream: true — the request is rejected.
  • Available on OpenRouter-routed models; AIR-routed models don't support it.
  • The loop is bounded (up to 4 tool rounds); each round costs a model call, so expect a few seconds per round. The final response's usage includes tokens from the intermediate rounds.
  • Each executed web_search / fetch_page call adds 1 Buzz on top of the model-token cost, billed after the run completes — a whatif=true preview cannot include it because the number of tool calls is up to the model.
  • civitai:web_search can be combined with your own function tools, but if the model calls one of your tools the loop stops and the tool_calls response is returned to you as usual.
  • For direct (non-LLM) access to the same capability, see the webSearch and webScrape steps.

Model selection ​

model accepts any string that identifies a model on OpenRouter or a Civitai AIR URI:

FormatExampleNotes
OpenRouter IDopenai/gpt-4o-miniAny model from openrouter.ai/models.
OpenAI shorthandgpt-4o, gpt-4o-miniOpenRouter also accepts bare OpenAI model names.
AIR URIurn:air:llm:model:civitai:<modelId>@<versionId>Routes to a Civitai-hosted model.

Parameters reference ​

FieldDefaultNotes
model— ✅Model ID (OpenRouter) or AIR URI.
messages— ✅Array of role-discriminated messages (at least 1).
temperature10–2. Higher = more random output.
topP10–1. Nucleus sampling. Alternative to temperature; usually set one or the other.
maxTokensnullMax output tokens, 1–128 000. Unlimited when omitted.
n1Number of completions to generate, 1–128.
stopnullUp to 4 stop sequences.
presencePenalty0-2 to 2. Positive values discourage repeating topics.
frequencyPenalty0-2 to 2. Positive values discourage repeating exact tokens.
seednullInteger seed for deterministic output (beta).
usernullEnd-user identifier for abuse monitoring.
logprobsnullReturn log probabilities for generated tokens.
topLogprobsnull0–20. Number of top log-prob candidates per token (requires logprobs: true).
toolsnullFunction definitions available to the model. A { "type": "civitai:web_search" } entry enables server-executed web search.
tool_choicenull"auto", "none", "required", or { "type": "function", "function": { "name": "..." } }.
chatTemplateKwargsnullExtra kwargs passed to the model's chat template (vLLM-specific).
modalitiesnullOutput modalities. Include "image" to route the request through the image-generation pipeline. See Image generation.
imageConfignullImage-generation parameters (aspect_ratio, image_size, n). Only consulted when modalities includes "image".

Messages reference ​

Messages are discriminated by the role field:

system ​

json
{ "role": "system", "content": "You are a helpful assistant.", "name": "optional" }

user ​

Content can be a plain string or an array of content parts:

json
{ "role": "user", "content": "Plain text" }
json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "What's in this image?" },
    { "type": "image_url", "image_url": { "url": "https://...", "detail": "auto" } }
  ]
}

assistant ​

json
{ "role": "assistant", "content": "Prior response text." }

Or with tool calls (as returned by the model):

json
{
  "role": "assistant",
  "content": null,
  "tool_calls": [{
    "id": "call_abc123",
    "type": "function",
    "function": { "name": "get_weather", "arguments": "{\"city\":\"Paris\"}" }
  }]
}

tool ​

json
{
  "role": "tool",
  "tool_call_id": "call_abc123",
  "content": "{\"temperature\": 18}"
}

Reading the result ​

The output is an OpenAI-compatible chat.completion object:

json
{
  "status": "succeeded",
  "steps": [{
    "name": "0",
    "$type": "chatCompletion",
    "status": "succeeded",
    "output": {
      "id": "chatcmpl-...",
      "object": "chat.completion",
      "created": 1748000000,
      "model": "openai/gpt-4o-mini",
      "choices": [{
        "index": 0,
        "message": {
          "role": "assistant",
          "content": "The capital of France is Paris."
        },
        "finish_reason": "stop"
      }],
      "usage": {
        "prompt_tokens": 24,
        "completion_tokens": 9,
        "total_tokens": 33
      }
    }
  }]
}

The /v1/chat/completions endpoint returns the output object directly (not wrapped in a workflow envelope).

Cost ​

Cost depends on whether the model routes through OpenRouter or is a Civitai AIR model.

OpenRouter models ​

Cost is computed in Buzz from actual token usage (minimum 1 Buzz). Before execution, the orchestrator estimates the cost from the model's per-token rates. After execution, the final Buzz charge is based on the tokens actually consumed by the model.

Per-token rates vary widely between models. Representative examples:

ModelTypical single call
openai/gpt-4o-mini< 1 Buzz
openai/gpt-4o2–15 Buzz
anthropic/claude-3-5-sonnet4–20 Buzz
meta-llama/llama-3.3-70b-instruct< 1 Buzz

Use whatif=true on your first request to get an exact preview before committing.

AIR models (Civitai-hosted) ​

Flat-rate pricing based on image count and number of completions requested:

total = 1 × (imageCount × 2) × n

Known limitation

For text-only requests to AIR models (imageCount = 0), the images factor collapses the product to 0 Buzz. This is a known bug — expect it to be corrected in a future release. For now, AIR model text-only calls cost 0 Buzz.

Runtime ​

Most chat completions finish in 5–30 seconds depending on model and output length. Use wait=60 for simple requests; add wait=0 + polling for long outputs, large n, or slow models. The /v1/chat/completions endpoint waits up to 60 seconds before timing out with 504.

Troubleshooting ​

SymptomLikely causeFix
400 with "messages must not be empty"Empty messages arrayInclude at least one message.
400 with "model is required"Missing model fieldmodel is always required.
504 Gateway Timeout (via /v1)Slow model or long outputRetry with wait=0 via SubmitWorkflow + polling.
400 with "topLogprobs requires logprobs"Sent topLogprobs without logprobs: trueSet "logprobs": true alongside topLogprobs.
Response truncated mid-sentencemaxTokens reachedRaise maxTokens or omit it to let the model decide.
Tool call in response instead of contentExpected behaviourThe model chose to call a tool — feed the tool_calls back as a tool message in the next turn. Exception: civitai:web_search tool calls are executed server-side and never reach you (see Server-executed web search).
Step failed, reason = "no_provider_available"AIR model offline or no worker availableRetry shortly.

Civitai Developer Documentation