Workflow Templates
Workflow templates are saved, parameterized creative-agent recipes. Use them when your builder UI or agent wants to create reusable workflows with typed inputs, then start durable /v1/creative-agent/workflows runs from those saved definitions.
compose_workflow_template in /v1/chat/completions can draft a template from a natural-language brief. The same tool also accepts an existing_template argument carrying the prior template JSON — pass it when the user asks to edit a saved template ("change my saved storyboard to 16:9", "add a music step to wf_X", "swap the model in my saved template") and the planner will preserve stage ids and bump only the requested change. The REST template endpoints below persist, list, edit, fork, and delete those drafts.
#Template Endpoints
| Endpoint | Method | Use |
|---|---|---|
/v1/creative-agent/workflows/templates |
POST |
Create a workflow template. |
/v1/creative-agent/workflows/templates |
GET |
List accessible templates. |
/v1/creative-agent/workflows/templates/:id |
GET |
Read one template. |
/v1/creative-agent/workflows/templates/:id |
PATCH |
Update an owned template. |
/v1/creative-agent/workflows/templates/:id |
DELETE |
Delete an owned template. |
/v1/creative-agent/workflows/templates/:id/fork |
POST |
Copy an accessible template into the caller's private templates. |
All template routes require authentication. Private templates are only visible to their owner. Public templates are readable and forkable by any authenticated caller. Update and delete treat "not owned" the same as "not found" so private template IDs cannot be probed.
#Template Shape
| Field | Use |
|---|---|
id |
Optional caller-supplied template ID. If omitted, the API mints one from the template name. |
version |
Optional semantic version. Defaults to 0.0.1; updates bump the patch version. |
name |
Required display name. |
description |
Optional description. |
brief |
Optional source brief or authoring prompt. |
category |
Optional category: portrait, video-social, makeover, cinematic, music, analysis, custom, or other. Defaults to custom. |
stability |
Optional production, beta, or experimental. Defaults to experimental. |
author |
Optional system or { "userId": "...", "displayName": "..." }. |
visibility |
Optional private or public. Defaults to private. |
inputs |
Typed input declarations used when compiling the template. |
stages |
Fixed, interactive, or batch stages that compile into workflow steps. |
graph |
Optional visual builder layout. |
previewArtifacts |
Optional image/video preview metadata for galleries. |
estimatedCapacityUnits |
Optional { "min": number, "max": number } estimate. |
exposeToLLM |
Optional boolean for template discovery surfaces. Defaults to false. |
llmPriority |
Optional ranking hint for LLM-facing template selection. |
tags, metadata |
Optional app-specific metadata. |
Templates are validated by the shared creative-agent validator. Invalid create or update requests return 422 with data.validation.issues.
#Binding Syntax In Stage Args
Stage args values use binding placeholders to pull from the run context. Two forms are supported:
- Pure binding — the entire string is
$root.path(e.g."$inputs.duration","$artifacts.script.items[0].selectedVersion.content"). The resolver returns the value at its original type: a number stays a number, an array stays an array, a selectedArtifactVersionstays the object. - Embedded binding —
$root.pathpatterns inside a longer string (e.g."Render in $inputs.aspect_ratio for $inputs.duration seconds.") are interpolated inline. Each match is stringified and substituted. Embedded paths that resolve toundefinedthrow aBindingErrorso typos surface during validation rather than shipping$inputs.footext to a model. - Wildcard expansion —
items[*]returns the full list. Example:"$artifacts.angles.items[*].selectedVersion.url"returnsstring[].
Roots: $inputs, $artifacts, $item (batch stage), $runtime, $run.
Convenience accessors on artifacts: .items[N].selectedVersion resolves to the currently-selected version of an ArtifactItem (the canonical way to pull a generated URL from a prior stage).
#Workflow Primitives
@sogni-ai/sogni-intelligence-client/workflows exposes a primitives namespace of pure helpers (prompt builders, parsers, predicate evaluators) that runtimes use to implement wf:*-prefixed workflow stage tools. Primitives are NOT chat tools — they are stage-only utilities that compose with the condition field on a stage to enable automated validate-and-regenerate loops.
| Primitive | Purpose |
|---|---|
wf:validate_with_rubric |
LLM judge with strict __PASS__ / __FAIL__: <reason> token protocol. Returns ToolErr with WORKFLOW_VALIDATION_FAILED on fail so a downstream stage with condition: { type: 'if_error_in', stageId: 'validate_X' } fires a retry. Supports text and image artifacts. The artifactUrl argument is checked against an allow-list before any HTTP fetch — loopback, private, and link-local addresses are rejected. |
wf:retry_until_condition |
Wraps another tool with a bounded retry loop until a predicate passes. Predicate kinds: metadata_field_truthy, metadata_field_equals, metadata_field_gte, metadata_field_lte over a dot-path into the wrapped tool's result. Preserves the wrapped tool's output_assets so downstream bindings continue to resolve. |
wf:expand_storyboard_script |
LLM expands a brief into a numbered TITLE / TOTAL_DURATION / BEATS script with structural validation (sum-of-seconds drift within 20%, every beat has VISUAL and ACTION fields, sequential numbering). |
wf:fit_dialogue_to_duration |
LLM rewrites dialogue to fit a target spoken duration at a configurable words-per-minute rate. Enforces mustKeep verbatim phrases. |
All four wf:* primitives now run durably on the hosted backend (POST /v1/creative-agent/workflows) as well as in client-side runtimes — the executor short-circuits wf:* steps to a server-side primitive service (pure compute for wf:fit_dialogue_to_duration, server-side LLM for wf:expand_storyboard_script / wf:validate_with_rubric, and a durable loop marker for wf:retry_until_condition). wf:validate_with_rubric enforces an SSRF allow-list on artifactUrl (loopback / private / link-local rejected) and maps a blocked input to WORKFLOW_VALIDATION_FAILED without echoing the raw URL.
#Create A Template
curl https://api.sogni.ai/v1/creative-agent/workflows/templates \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Two-shot product teaser",
"description": "Generate a product keyframe and animate it.",
"category": "video-social",
"visibility": "private",
"inputs": [
{
"name": "brief",
"type": "text",
"required": true,
"description": "Product and visual direction"
}
],
"stages": [
{
"id": "keyframe",
"type": "fixed",
"tool": "generate_image",
"args": {
"prompt": "$inputs.brief",
"model": "flux2",
"width": 1024,
"height": 576
}
},
{
"id": "clip",
"type": "fixed",
"tool": "generate_video",
"args": {
"prompt": "Slow cinematic product push-in",
"duration": 5
}
}
],
"exposeToLLM": false
}'
Representative response:
{
"status": "success",
"data": {
"template": {
"id": "wf_two-shot-product-teaser_a1b2c3d4",
"version": "0.0.1",
"name": "Two-shot product teaser",
"visibility": "private",
"inputs": [],
"stages": []
}
}
}
#List And Read
curl "https://api.sogni.ai/v1/creative-agent/workflows/templates?visibility=all&limit=20&offset=0" \
-H "Authorization: Bearer YOUR_API_KEY"
visibility can be:
| Value | Result |
|---|---|
all |
Caller-owned templates plus public templates. Default. |
own |
Only caller-owned templates. |
public |
Only public templates. |
Pagination uses offset and limit; limit is capped at 100. Responses return data.templates and data.next, where next is the next offset or null.
Read one template:
curl https://api.sogni.ai/v1/creative-agent/workflows/templates/wf_two-shot-product-teaser_a1b2c3d4 \
-H "Authorization: Bearer YOUR_API_KEY"
#Update, Delete, Fork
Patch an owned template:
curl -X PATCH https://api.sogni.ai/v1/creative-agent/workflows/templates/wf_two-shot-product-teaser_a1b2c3d4 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "visibility": "public", "tags": ["product", "video"] }'
Delete an owned template:
curl -X DELETE https://api.sogni.ai/v1/creative-agent/workflows/templates/wf_two-shot-product-teaser_a1b2c3d4 \
-H "Authorization: Bearer YOUR_API_KEY"
Fork a public or owned template into a new private template:
curl -X POST https://api.sogni.ai/v1/creative-agent/workflows/templates/wf_source_template/fork \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "My product teaser variant" }'
Forked templates reset to version 0.0.1, are private by default, and include clonedFromTemplateId.
#Run From A Template
Start a durable workflow from a saved template by sending workflow_id and inputs to /v1/creative-agent/workflows. Do not send input.steps in the same request.
curl https://api.sogni.ai/v1/creative-agent/workflows \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: template-run-001" \
-d '{
"workflow_id": "wf_two-shot-product-teaser_a1b2c3d4",
"inputs": {
"brief": "A translucent speaker on a glossy black table, neon rim light"
},
"token_type": "spark",
"confirm_cost": true
}'
The API compiles the template with the provided inputs. Compile failures return 422 with compileErrors and compileWarnings. A successful template start authorizes cost first: it persists the compiled workflow and returns HTTP 202 with { workflow, waitingForCostApproval: true, preview } in a cost_approval_required pause — release it with POST /:id/confirm-cost before any step dispatches. See Creative-Agent Workflows → Run From A Template.