#Start a workflow
POST
/v1/creative-agent/workflows
Auth required
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.
#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. |
#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. |
| 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. |
#Request
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
}'
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(),
});
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,
},
)
#Response
{
"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.
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.