Skip to content

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:

Terminal window
# 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.

The full set of fields the engine accepts. workflow and input are the only ones you need for most runs — everything else is optional.

KeyRequired?TypePurpose
workflowyes (file mode); ?name= query param in API modestringWorkflow id. Match a name from the workflow catalog. For bundle runs use the sentinel bundle/ad-hoc.
inputyesobjectThe workflow-specific inputs. Shape is defined per-workflow — see the Input Schema table on each workflow’s reference page (e.g. video/talking-head).
metadatanoobjectFree-form key/value annotations stored on the run row. Useful for tagging runs by experiment / batch / source — no engine semantics.
prioritynointeger (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.
statenoobjectOpaque 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_fastno (default true)booleanWhen true, any failed node cancels the whole run. Set false for fan-out / best-effort workflows where partial completion is acceptable.
bundlenoarray 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.
parentnoobjectMarks 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.

The smallest valid run-spec — workflow id and input dict, nothing else:

examples/run-inputs/talking-head-smoke.yaml
workflow: video/talking-head
input:
topic: overthinking
script_formula: reframe
# A run-spec that exercises every top-level key. Useful as a
# reference for the layered options.
workflow: video/talking-head
priority: 70 # bump above the default 50
fail_fast: false # variants run independently
metadata: # 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: 1
input:
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: high

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-hoc
bundle:
- 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.

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::

  1. Workflow-specific fields — the ones in the per-workflow schema (e.g. topic, script_formula, roster_path).
  2. Universal WorkflowInput base 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).
  3. Pass-through hintsWorkflowInput is extra="allow", so undeclared keys (style, quality, model, etc.) survive validation and reach downstream stages that look for them.

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:

Terminal window
# 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.

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:

Terminal window
# 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 toml
fab-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-optional

Required 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.

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, universal variants / regenerate, extra="allow" pass-through).

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.