Source: https://docs.sogni.ai/api-reference/workflows/

# Creative Workflows

Durable multi-step creative jobs with an explicit `steps[]` dependency graph. Use them when your application has already decided what to do: storyboards, image-to-video, batch generation. The API executes, persists state, streams SSE events, and supports cancel, resume, and reseed. User-facing Sogni apps call these **cloud workflows**.

### POST /v1/creative-agent/workflows

Provide an inline `input.steps` plan, or invoke a saved template with `workflow_id` and `inputs`. The two are mutually exclusive. Requires an API key. Unknown fields return `400`. Plans may contain up to 12 steps.

### [#](https://docs.sogni.ai/api-reference/workflows/#headers)Headers

| Name | Type | In | Description |
| --- | --- | --- | --- |
| Idempotency-Key | string | header | Optional. `X-Idempotency-Key` is also accepted. Header only. Keys longer than 192 characters are truncated. Reusing a key returns the original workflow with `200` and `idempotent: true`, even if the body differs. |

### [#](https://docs.sogni.ai/api-reference/workflows/#body)Body

| Name | Type | In | Description |
| --- | --- | --- | --- |
| input | object | body | Inline workflow plan. Allowed fields: `title` and `steps`. Required when `workflow_id` is absent. |
| input.title | string | body | Optional human-readable title for the run. |
| input.stepsrequired | array | body | Array of step inputs. Each step has `id`, `toolName`, `arguments`, and an optional `dependsOn` array linking it to upstream artifacts. |
| workflow\_id | string | body | Optional saved-template ID. Compiled server-side into a fresh `steps[]` before execution. |
| inputs | object | body | Typed input values when invoking a template. Keys match `WorkflowTemplate.inputs[].name`. |
| token\_type | string | body | `spark`, `sogni`, or `auto` (default). Picks which token pays when tokens are billed. It does not choose between an Unlimited plan and tokens; that is `billing_mode`. |
| billing\_mode | string | body | `auto` (default), `subscription`, or `tokens`. Under `auto`, an active Unlimited plan covers every step it can and no tokens are spent on them. `subscription` bills eligible steps against the plan only; a step the plan can't cover fails instead of billing tokens. `tokens` always bills your token balance, even with an active plan; send it to spend Premium Spark for fastest-priority queue access. Vendor models always bill Premium Spark. Other values return `400`. See [Plan or tokens](https://docs.sogni.ai/sogni-intelligence/creative-agent-workflows/#plan-or-tokens). |
| app\_source | string | body | Optional caller label. |
| max\_estimated\_capacity\_units | number | body | Optional ceiling on estimated capacity units. A plan above it is rejected with `400` before anything is saved; `details.estimatedCapacity` carries the estimate. |
| confirm\_cost | boolean | body | Send `false` to get an estimate without starting: a plan with a positive estimate is rejected with `400`, and `details.estimatedCapacity` and `details.confirmationRequired: true` hold the numbers. Resubmit with `true` (or omit the field) to start. Template runs still pause for approval. |
| media\_references | array | body | Optional request media references available to `$input_media` bindings and negative media indices. Each must be an `http(s)` URL; inline `data:` URIs are rejected. |
| safe\_content\_filter | boolean | body | Optional Sensitive Content Filter setting for this workflow. Defaults to `true`. `false` requires an eligible account and is checked when each step renders. The setting is kept for retries, resume, and reseed. |

### [#](https://docs.sogni.ai/api-reference/workflows/#request)Request

```bash
curl https://api.sogni.ai/v1/creative-agent/workflows \
  -H "Authorization: Bearer $SOGNI_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "input": {
      "title": "Robot sketch to video",
      "steps": [
        {
          "id": "keyframe",
          "toolName": "generate_image",
          "arguments": { "prompt": "A graphite robot sketch on a drafting table" }
        },
        {
          "id": "clip",
          "toolName": "animate_photo",
          "arguments": { "prompt": "Slow dolly-in on the keyframe.", "videoModel": "ltx25", "duration": 5 },
          "dependsOn": [{
            "sourceStepId": "keyframe",
            "sourceArtifactIndex": 0,
            "targetArgument": "sourceImageIndex",
            "transform": "image_index",
            "required": true
          }]
        }
      ]
    },
    "token_type": "auto",
    "max_estimated_capacity_units": 25,
    "confirm_cost": true
  }'
```

```javascript
const wf = await sogni.workflows.start({
  input: {
    title: 'Robot sketch to video',
    steps: [
      {
        id: 'keyframe',
        toolName: 'generate_image',
        arguments: { prompt: 'A graphite robot sketch on a drafting table' },
      },
      {
        id: 'clip',
        toolName: 'animate_photo',
        arguments: { prompt: 'Slow dolly-in on the keyframe.', videoModel: 'ltx25', duration: 5 },
        dependsOn: [{
          sourceStepId: 'keyframe',
          sourceArtifactIndex: 0,
          targetArgument: 'sourceImageIndex',
          transform: 'image_index',
          required: true,
        }],
      },
    ],
  },
  maxEstimatedCapacityUnits: 25,
  confirmCost: true,
  idempotencyKey: crypto.randomUUID(),
});
```

```python
import os, requests, uuid

resp = requests.post(
    "https://api.sogni.ai/v1/creative-agent/workflows",
    headers={
        "Authorization": f"Bearer {os.environ['SOGNI_API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "input": {
            "title": "Robot sketch to video",
            "steps": [
                {
                    "id": "keyframe",
                    "toolName": "generate_image",
                    "arguments": {"prompt": "A graphite robot sketch on a drafting table"},
                },
                {
                    "id": "clip",
                    "toolName": "animate_photo",
                    "arguments": {"prompt": "Slow dolly-in on the keyframe.", "videoModel": "ltx25", "duration": 5},
                    "dependsOn": [{
                        "sourceStepId": "keyframe",
                        "sourceArtifactIndex": 0,
                        "targetArgument": "sourceImageIndex",
                        "transform": "image_index",
                        "required": True,
                    }],
                },
            ],
        },
        "max_estimated_capacity_units": 25,
        "confirm_cost": True,
    },
)
```

### [#](https://docs.sogni.ai/api-reference/workflows/#response)Response

```json
{
  "status": "success",
  "data": {
    "workflow": {
      "workflowId": "wf_durable_workflow_…",
      "status": "queued",
      "input": { "title": "Robot sketch to video", "steps": [ "…" ] },
      "events": []
    }
  }
}
```

Inline starts return `201 Created`. Template starts (`workflow_id`) return `202 Accepted` with `waitingForCostApproval: true` and a `preview`. An idempotent replay returns `200`. A plan that names a vendor model on an account without Premium Spark is rejected with `402` before any step runs. If a GPU worker drops a step before producing media, the step is retried once automatically; the first attempt stays in the event log.

**Active-workflow cap.** Each account can have a limited number of workflows queued or running at once; paused (`waiting_for_user`) workflows don't count. Over the cap, a start returns `409` with `details.activeWorkflowCount` and `details.activeWorkflowLimit`; cancel or finish one first. A `409` does not use up your start allowance. When overall workflow capacity is full, or starts arrive too quickly, a start returns `429`. A start-rate refusal carries the wait in the `Retry-After` header and, in seconds, in the body's `retryAfter`; wait at least that long, because a start sent sooner is refused again. When no wait is given, back off with jitter. Retry with the same `Idempotency-Key`, and follow running workflows with `/stream` or `GET /:id` rather than re-sending starts.

### GET /v1/creative-agent/workflows

Lists the caller's workflows, most recently updated first. `next` is the next offset, or `null` on the last page.

### [#](https://docs.sogni.ai/api-reference/workflows/#query-parameters)Query parameters

| Name | Type | In | Description |
| --- | --- | --- | --- |
| offset | integer | query | Default `0`. |
| limit | integer | query | Default `20`, max `100`. |

### [#](https://docs.sogni.ai/api-reference/workflows/#response-1)Response

```json
{
  "status": "success",
  "data": { "workflows": [ "…" ], "next": 20 }
}
```

### GET /v1/creative-agent/workflows/:id

Returns a single workflow snapshot. Status is `queued`, `running`, `waiting_for_user`, `completed`, `partial_failure`, `failed`, or `cancelled`. Paused snapshots also carry `waitingReason` (for example `cost_approval_required`, `safety_review_required`, `insufficient_credit`, or `other`) and `awaitingCostApproval`. Offer `confirm-cost` only when `awaitingCostApproval` is `true`.

### GET /v1/creative-agent/workflows/:id/events

Returns the persisted event log for a workflow.

### GET /v1/creative-agent/workflows/:id/events/stream

Server-Sent Events stream. Replays persisted events, then follows new ones. Pass `?after=<sequence>` or send `Last-Event-ID` to skip events you already have. The stream also sends `workflow_status` frames (`{ workflowId, status }`, plus `waitingReason` and `awaitingCostApproval` while paused) and heartbeat comments. It stays open through `waiting_for_user` and closes on `completed`, `failed`, `partial_failure`, or `cancelled`. If the stream emits `event: error`, reconnect with `Last-Event-ID`.

### [#](https://docs.sogni.ai/api-reference/workflows/#event-types)Event types

`workflow_started`, `workflow_progress`, `workflow_waiting_for_user`, `workflow_completed`, `workflow_failed`, `workflow_cancelled`, `step_started`, `step_completed`, `step_failed`, `step_waiting_for_user`, `artifact_created`, and `custom`. Ignore types you don't recognize.

### POST /v1/creative-agent/workflows/:id/resume

Resumes a paused or interrupted workflow from persisted state. Requires an API key. Returns `202` with `{ workflow, resumed: true }`. Finished workflows and cost-approval pauses return `409`; use `confirm-cost` for the latter.

### [#](https://docs.sogni.ai/api-reference/workflows/#body-1)Body

| Name | Type | In | Description |
| --- | --- | --- | --- |
| token\_type | string | body | Optional. Overrides which token pays for the rest of the run. |
| billing\_mode | string | body | Optional. `auto`, `subscription`, or `tokens`, as on [start](https://docs.sogni.ai/api-reference/workflows/#endpoint-post-v1-creative-agent-workflows). A new value replaces the stored one for every step not yet dispatched. |
| app\_source | string | body | Optional caller label. |
| safe\_content\_filter | boolean | body | Optional. Records a new Sensitive Content Filter setting before the run continues; it applies to every step not yet dispatched. Send `false` to release a `safety_review_required` pause. Account eligibility is still checked when each step renders. |

### POST /v1/creative-agent/workflows/:id/confirm-cost

Approves or rejects the cost preview for a workflow paused with `cost_approval_required`, such as a template run, which authorizes cost on start. `resume` does not release a cost-approval pause; use this endpoint. Rapid repeated calls return `429`.

### [#](https://docs.sogni.ai/api-reference/workflows/#body-2)Body

| Name | Type | In | Description |
| --- | --- | --- | --- |
| decisionrequired | string | body | `"confirm"` or `"cancel"`. |
| acceptedCostPreviewrequired | object | body | Required for `decision: "confirm"` (alias `accepted_cost_preview`). Echo the persisted preview `{ totalEstimatedCapacityUnits, tokenType, validityUntil }`. A stale or mismatched value returns `409`; a missing or malformed object returns `400`. |
| Idempotency-Key | string | header | Optional. `X-Idempotency-Key` or a body `idempotency_key` also work. A replay returns `{ workflow, idempotent: true }`; the same key with a different body returns `409`. |

### [#](https://docs.sogni.ai/api-reference/workflows/#response-2)Response

```json
{
  "status": "success",
  "data": { "workflow": { "…": "…" }, "decision": "confirm", "authorization": { "…": "…" } }
}
```

A `confirm` requires an API key and a preview no older than five minutes; re-read the workflow for a fresh preview. `cancel` returns `{ workflow, decision: "cancel" }`. A `409` for the wrong state carries `details: { retryable, workflowStatus, waitingReason, awaitingCostApproval }`. An account with no available balance gets `402`.

### POST /v1/creative-agent/workflows/:id/cancel

Cooperative cancel. Flips the workflow to `cancelled` and emits a cancel event for SSE replay. Takes no body. Returns `{ workflow, transitioned, inFlight }`; cancelling an already-finished workflow returns the snapshot with `transitioned: false`.

### POST /v1/creative-agent/workflows/:id/reseed

Clones a `completed` or `partial_failure` workflow with fresh seeds and starts it as a new workflow, for alternate takes without retyping a plan. Requires an API key. Returns `201` with `{ workflow, reseed: { cloned_from_run_id, steps } }`. The source's Sensitive Content Filter setting carries over, and the new run counts against the same active-workflow and start limits, with the same `409` and `429` responses as a start.

### [#](https://docs.sogni.ai/api-reference/workflows/#body-3)Body

| Name | Type | In | Description |
| --- | --- | --- | --- |
| Idempotency-Key | string | header | Optional. `X-Idempotency-Key` is also accepted. A reseed mints new seeds, so a retried request without a key starts a second take. Reusing a key returns `200` with `idempotent: true`, the workflow the first request started, and the seeds it received. A key already used for a different request returns `409`. |
| seed\_overrides | object | body | Optional per-step seed overrides. Omit to let the server generate fresh seeds for every step. |
| token\_type | string | body | Optional. Which token pays when tokens are billed. |
| billing\_mode | string | body | Optional. `auto`, `subscription`, or `tokens`, as on [start](https://docs.sogni.ai/api-reference/workflows/#endpoint-post-v1-creative-agent-workflows). Omit to keep the source run's setting. |
| app\_source | string | body | Optional caller label. |
