Run-specs (YAML / TOML / JSON)
A run-spec is a YAML / TOML / JSON file that fully describes one workflow run — the workflow to run plus the inputs to give it. The same shape works as a checked-in template, an API request body, and a CLI argument; nothing about a run-spec is API-specific. This means one file works three ways:
# 1. Run it locally:fab-workflow --from-file examples/run-inputs/talking-head.yaml
# 2. Submit it over the wire (the file IS the request body):curl -X POST https://gofabric.dev/v1/workflows/runs?name=video/talking-head \ -H 'Authorization: Bearer fab_xxx' \ -H 'content-type: application/yaml' \ --data-binary @examples/run-inputs/talking-head.yaml
# 3. Commit it as a recurring job and let CI run it on a schedule.Format is selected by file extension on disk (.yaml, .yml,
.toml, .json) and by Content-Type on the wire. The wire-format
table and parser details live in
Submitting Jobs → Workflow Run Submission Body Formats.
Top-level keys
Section titled “Top-level keys”The full set of fields the engine accepts. workflow and input are
the only ones you need for most runs — everything else is optional.
| Key | Required? | Type | Purpose |
|---|---|---|---|
workflow | yes (file mode); ?name= query param in API mode | string | Workflow id. Match a name from the workflow catalog. For bundle runs use the sentinel bundle/ad-hoc. |
input | yes | object | The workflow-specific inputs. Shape is defined per-workflow — see the Input Schema table on each workflow’s reference page (e.g. video/talking-head). |
metadata | no | object | Free-form key/value annotations stored on the run row. Useful for tagging runs by experiment / batch / source — no engine semantics. |
priority | no | integer (default 50) | Higher wins in the claim loop’s ORDER BY priority DESC. Bump to 90+ for interactive submits, drop to 10–20 for batch backfills. |
state | no | object | Opaque caller-supplied state echoed back in the run’s terminal events (webhook + SSE). Lets a consumer correlate a completion to its own context without an extra API call. |
fail_fast | no (default true) | boolean | When true, any failed node cancels the whole run. Set false for fan-out / best-effort workflows where partial completion is acceptable. |
bundle | no | array of \{workflow, input\} | When set, the engine fans out one subprocess per spec running each spec’s named workflow with its own input. Mutually exclusive with input.variants. |
parent | no | object | Marks this run as a regeneration of an earlier run. Carries run_id and optional variant_index; the engine persists both as lineage columns so parent_run_id queries don’t have to scan input JSON. |
The engine populates a few runtime keys on input itself
(_fabric_run_id, _fabric_organization_id, _fabric_variants,
_fabric_variant_index, output_dir) — leave those out of the
file, the engine injects them at dispatch time.
Minimal example
Section titled “Minimal example”The smallest valid run-spec — workflow id and input dict, nothing else:
workflow: video/talking-headinput: topic: overthinking script_formula: reframeWorked example (every key)
Section titled “Worked example (every key)”# A run-spec that exercises every top-level key. Useful as a# reference for the layered options.workflow: video/talking-headpriority: 70 # bump above the default 50fail_fast: false # variants run independentlymetadata: # free-form annotations experiment: "april-formula-sweep" source: "ci"state: # echoed in terminal events request_id: "req_abc123" customer_id: "cust_xyz"parent: # marks this as a regeneration run_id: "01J6N3YV2H3K8E7XR4M2YT5VAW" variant_index: 1input: topic: overthinking script_formula: reframe variants: 3 # universal: fan out 3 variants regenerate: # universal: regen direction direction: "more humor" keep: ["script", "voice"] style: punchy # extra="allow": passes through quality: highBundle mode
Section titled “Bundle mode”Bundle mode runs N independent workflows in parallel — each entry has
its own workflow and input. The run row itself uses the sentinel
workflow id bundle/ad-hoc:
workflow: bundle/ad-hocbundle: - workflow: video/talking-head input: { topic: overthinking, script_formula: reframe } - workflow: hooks/generate input: { topic: overthinking, count: 5 }Outputs aggregate into the run’s outputs: [...] envelope with one
entry per spec, each tagged with its sub-workflow name.
What goes inside input:
Section titled “What goes inside input:”The shape of input: is workflow-specific — the per-workflow
reference page is the source of truth. Every page has an Input
Schema table listing each field’s name, type, default, and
description.
Three layers compose into the final input::
- Workflow-specific fields — the ones in the per-workflow
schema (e.g.
topic,script_formula,roster_path). - Universal
WorkflowInputbase fields present on every workflow:variants: int(1–10) — fan out N variant runs.regenerate: object— run-lineage + direction (direction,keep,extra_instructions,parent_run_id,parent_variant_index).
- Pass-through hints —
WorkflowInputisextra="allow", so undeclared keys (style,quality,model, etc.) survive validation and reach downstream stages that look for them.
Override layers (CLI)
Section titled “Override layers (CLI)”When running a file with fab-workflow --from-file, fields merge
from lowest to highest precedence; the rightmost layer wins:
--from-file FILE < --input-file FILE < --input k=v (base) (mid layer) (highest)So a single committed run-spec is a sensible default and CLI flags override it for ad-hoc tweaks:
# Run with everything from the file:fab-workflow --from-file examples/run-inputs/daily-drop.yaml
# Same file, but override slot_offset for today's run:fab-workflow --from-file examples/run-inputs/daily-drop.yaml \ --input slot_offset=$(date +%j)API consumers do the file-then-overrides merge client-side and POST the merged dict — the engine itself sees one already-resolved request body.
Recommended path: scaffold first
Section titled “Recommended path: scaffold first”Don’t hand-write a run-spec for a workflow you haven’t run before.
fab-workflow --scaffold reads the workflow’s input schema and
emits a populated template with field descriptions and constraints
inline:
# YAML (default) with field descriptions as inline comments:fab-workflow --scaffold video/quick-shorts > my-run.yaml
# Other formats:fab-workflow --scaffold video/quick-shorts -o my-run.toml --format tomlfab-workflow --scaffold video/quick-shorts -o my-run.json --format json
# Show the OUTPUT side — declared aliases + schema, useful when wiring# this workflow into a workflow set:fab-workflow --scaffold hooks/generate --show-output
# Required-fields-only template:fab-workflow --scaffold video/quick-shorts --no-optionalRequired fields land uncommented under input:; optional fields
appear as comment blocks the operator copies in. The result is a
valid run-spec — fab-workflow --from-file <file> runs it directly.
Everything in the rest of this page is the reference for what the
scaffold output means and how to extend it.
Runnable examples
Section titled “Runnable examples”Real run-specs you can run directly live at
examples/run-inputs/:
talking-head-builder-smoke.yaml— single-drop sanity test; use this first to confirm the environment.daily-drop-fabric-team.yaml— full character roster across three speakers.talking-head-builder-with-variants.yaml— exercises every layer (workflow-specific fields, universalvariants/regenerate,extra="allow"pass-through).
Bundling multiple runs into a set
Section titled “Bundling multiple runs into a set”If you want one declarative document that runs N stages with inter-run wiring (output of stage A feeding stage B), use a workflow set instead — sets live one layer up and have their own format reference.
Plan references
Section titled “Plan references”- Plan 075 — portable run inputs (the YAML/TOML/JSON loader,
--from-file, the file format) - Plan 077 — API content-type negotiation (
POST /v1/workflows/runsaccepts the same formats over the wire) - Plan 078 — scaffolding + workflow sets (
--scaffold,--run-set)