---
url: /orchestration/guide/submitting-work.md
---

> Documentation index: https://developer.civitai.com/llms.txt — every page on this site has a markdown version at its URL + `.md`.

# Submitting Work

There are two ways to submit a workflow:

1. **Generic** — [`POST /v2/consumer/workflows`](https://developer.civitai.com/orchestration/reference/operations/SubmitWorkflow.md) with a `steps` array and polymorphic `$type` on each step. Use it to chain steps, mix step types, attach callbacks, tags, metadata or `upgradeMode`, or when your SDK handles the discriminator itself.
2. **Per-recipe** — `POST /v2/consumer/recipes/{recipe}` with a single typed body (no `$type`). Each recipe wraps a single-step workflow. Use it when you only run one step type and want the simplest typed body — e.g. [`InvokeChatCompletionStepTemplate`](https://developer.civitai.com/orchestration/reference/operations/InvokeChatCompletionStepTemplate.md), [`InvokeComfyStepTemplate`](https://developer.civitai.com/orchestration/reference/operations/InvokeComfyStepTemplate.md), etc.

The generic path returns a [`Workflow`](https://developer.civitai.com/orchestration/reference/operations/GetWorkflow.md); a recipe returns only its step's output. You can mix them freely.

## The generic path

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

{
  "steps": [
    {
      "$type": "imageGen",
      "input": {
        "engine": "flux",
        "prompt": "A cat astronaut floating through neon space",
        "width": 1024,
        "height": 1024
      }
    }
  ]
}
```

### Workflow-level body fields

Everything in the table lives alongside `steps`. See the full [`WorkflowTemplate` schema](https://developer.civitai.com/orchestration/reference/operations/SubmitWorkflow.md) for types.

| Field | Purpose |
|-------|---------|
| `steps` | Required. One or more step objects, each with its own `$type` and `input`. |
| `callbacks` | Webhooks for lifecycle events — see [Results & webhooks](https://developer.civitai.com/orchestration/guide/results-and-webhooks.md). |
| `tags` | Indexed string tags for later lookup via [`QueryWorkflows`](https://developer.civitai.com/orchestration/reference/operations/QueryWorkflows.md). Great for tenant / campaign / user IDs. |
| `metadata` | Arbitrary JSON attached to the workflow. Not indexed — use for notes, correlation IDs, UI hints. |
| `arguments` | Values steps can read via `{ "$ref": "$arguments" }` — see [Workflow arguments](https://developer.civitai.com/orchestration/guide/workflows.md#workflow-arguments). |
| `allowMatureContent` | Controls mature-content gating and which Buzz currency pays — see [Payments (Buzz)](#payments-buzz) below. |
| `experimental` | Marks the workflow as experimental; may relax some guardrails. |
| `upgradeMode` | How to handle a workflow paid with blue/green Buzz that turns out to produce mature content — see [Payments (Buzz)](#payments-buzz). |
| `currencies` | Restrict which Buzz currencies may settle this workflow (see [Payments (Buzz)](#payments-buzz)). |
| `tips` | Optional tip amount attached to the workflow. |

### Query parameters

| Param | Default | Purpose |
|-------|---------|---------|
| `wait` | `0` | Seconds to block waiting for the workflow to finish; keep it below [your HTTP client's timeout](https://developer.civitai.com/orchestration/guide/getting-started.md#_3-poll-if-you-didn-t-wait-inline). If it is still running then, you get a `202`. |
| `whatif` | `false` | If `true`, validates and resolves the workflow (provider, estimated cost, required resources) without actually running it. Great for CI smoke tests and cost previews. |
| `hideMatureContent` | `false` | If `true`, mature blobs on the response won't include a `url`. Useful for rendering in user-facing UIs without re-checking policy per-blob. |

### Status codes

| Code | Meaning |
|------|---------|
| `200 OK` | The workflow reached a terminal state within your `wait` budget. |
| `202 Accepted` | The workflow is still running. The body is the current workflow; continue via webhooks or [`GetWorkflow`](https://developer.civitai.com/orchestration/reference/operations/GetWorkflow.md). |
| `400 Bad Request` | Body failed validation — see [Errors & retries](https://developer.civitai.com/orchestration/guide/errors-and-retries.md) for the response shape. |
| `401 Unauthorized` | Missing or invalid bearer token. |
| `403 Forbidden` | Token is valid but not allowed to use this recipe / resource / mature content flag. |
| `429 Too Many Requests` | Rate limited. Wait for the `Retry-After` seconds, then retry — see [Rate limits & quotas](https://developer.civitai.com/orchestration/reference.md#rate-limits-quotas). |

## Payments (Buzz)

Workflows are paid for with **Buzz**, Civitai's on-platform currency, from the balances on your civitai.com account; you never pass dollar amounts in the API. There are three currencies:

| Currency | Earned / bought | Valid for |
|----------|-----------------|-----------|
| **Blue** (`blue`) | Earned by interacting on civitai.com | SFW workflows only |
| **Green** (`green`) | Purchased | SFW workflows only |
| **Yellow** (`yellow`) | Purchased | SFW **and** NSFW workflows |

### Default charging

If you don't set `currencies`, the orchestrator charges your account in order **blue → green → yellow**, splitting across currencies if needed (all your blue first, the remainder from green, then yellow).

### Restricting to specific currencies

Set `currencies` to limit which accounts can settle the workflow:

```json
{
  "steps": [ /* ... */ ],
  "currencies": ["green", "yellow"]
}
```

Only currencies you list will be drawn on. If none of them cover the cost, the submission is rejected with a payment error.

### Mature content and currency interaction

* Setting `allowMatureContent: true` forces payment in **yellow** Buzz (the only NSFW-capable currency).
* Setting `allowMatureContent: false` restricts to SFW and allows blue/green (plus yellow) to pay.
* Leaving `allowMatureContent` unset means the orchestrator decides *after* seeing which currency was charged: if the workflow settled with blue or green, it's treated as SFW; if it settled with yellow, mature content is allowed.

### `upgradeMode` — when the output turns out mature

If the workflow was paid with blue or green Buzz (SFW) but the generated content is classified as mature, `upgradeMode` controls what happens:

* `"manual"` — the mature output is withheld and the workflow waits on you. To release it, call [`UpdateWorkflow`](https://developer.civitai.com/orchestration/reference/operations/UpdateWorkflow.md) (`PUT`) or [`PatchWorkflow`](https://developer.civitai.com/orchestration/reference/operations/PatchWorkflow.md) (`PATCH`) with `allowMatureContent: true`. The orchestrator refunds the blue/green Buzz and charges yellow. If your yellow balance is insufficient, the update returns `400`.
* `"automatic"` — the orchestrator does that swap for you inline, charging the difference in yellow Buzz and delivering the mature output (or failing the workflow if yellow is insufficient).

### Previewing cost

`whatif=true` returns a cost estimate, broken down per currency, without running the workflow.

## The per-recipe path

```http
POST https://orchestration.civitai.com/v2/consumer/recipes/chatCompletion?whatif=false
Authorization: Bearer <your-token>
Content-Type: application/json

{
  "model": "openai/gpt-oss-120b",
  "messages": [
    { "role": "user", "content": "Summarize this release note: ..." }
  ]
}
```

Each `/v2/consumer/recipes/{recipe}` endpoint builds a single-step workflow for you, so:

* The request body is the step's `input` schema directly — no `$type`, no `steps` array.
* The query parameters are `whatif`, `allowMatureContent`, `experimental`, `ephemeral` and `tier`. There is no `wait` or `hideMatureContent`, and no callbacks, tags, metadata or `upgradeMode` — use the generic path for those.
* The call blocks until the workflow finishes (there is no `202`), and the workflow is canceled if the connection drops first. Use the generic path for anything that may outlast [your HTTP client's timeout](https://developer.civitai.com/orchestration/guide/getting-started.md#_3-poll-if-you-didn-t-wait-inline).
* The response is the step's output (e.g. `ChatCompletionOutput`), not a `Workflow`.

## Updating and querying

After submitting, you can:

* [`GetWorkflow`](https://developer.civitai.com/orchestration/reference/operations/GetWorkflow.md) — fetch by ID
* [`QueryWorkflows`](https://developer.civitai.com/orchestration/reference/operations/QueryWorkflows.md) — list by tag, status, date range
* [`UpdateWorkflow`](https://developer.civitai.com/orchestration/reference/operations/UpdateWorkflow.md) / [`PatchWorkflow`](https://developer.civitai.com/orchestration/reference/operations/PatchWorkflow.md) — amend metadata / tags, or raise [`downloadPriority`](https://developer.civitai.com/orchestration/guide/workflows.md#download-priority)
* [`AddWorkflowTag`](https://developer.civitai.com/orchestration/reference/operations/AddWorkflowTag.md) / [`RemoveWorkflowTag`](https://developer.civitai.com/orchestration/reference/operations/RemoveWorkflowTag.md) — tag maintenance
* [`DeleteWorkflow`](https://developer.civitai.com/orchestration/reference/operations/DeleteWorkflow.md) — cancel / remove

`UpdateWorkflow` (`PUT`) returns `200` with the full workflow body, and accepts `?whatif=true` to
price a change without applying it.

Resource `availability.status` includes `queued`: a resource waiting in a download queue (it carries a
`queuePosition`) reports `queued`, while `unavailable` means "eligible, but nothing is fetching it".
Clients that exhaustively match on `status` must handle both.

[`UpdateWorkflowStep`](https://developer.civitai.com/orchestration/reference/operations/UpdateWorkflowStep.md) / [`PatchWorkflowStep`](https://developer.civitai.com/orchestration/reference/operations/PatchWorkflowStep.md) update a step's `metadata` (the only field they accept) in any state; a step's `input` can't be changed.
