Pipeline Specs
Reproducible pipelines. A pipeline spec is a small JSON file that declares a pipeline's inputs and its steps. Save any finished run as a spec, re-run it on new files, keep it in your own repo, and trace every result back to the exact tools and inputs that produced it. Requires the tools scope.
What a spec is
The predefined pipelines are specs too — a spec is simply a pipeline you own. It has two parts:
- inputs — the values that change between runs (the files to process, a sample name, a reference).
- steps — a graph of tool invocations. Each step names a registered tool, its parameters, and the steps it waits for.
Saved specs live in your workspace at pipelines/specs/<name>.pipeline.json. They are ordinary files: open them in the viewer, download them, edit them by hand, or commit them to your own repository and run them from there.
Worked example
A two-step pipeline: sort a BAM with samtools sort, then index the sorted file with samtools index. The first step reads the inputBam input; the second reads the first step's output.
{
"specVersion": "1.0",
"id": "sort-and-index",
"name": "sort-and-index",
"description": "Coordinate-sort a BAM file and build its index",
"category": "custom",
"executionMode": "process-manager",
"inputs": [
{ "name": "inputBam", "type": "file", "description": "Unsorted BAM file" },
{ "name": "sampleName", "type": "string", "description": "Sample identifier used to name output files", "default": "sample" }
],
"steps": [
{
"stepId": "sort",
"toolName": "samtools",
"description": "Sort BAM by coordinate",
"parameters": {
"samtoolsCommand": "sort",
"inputFile": "${inputBam}",
"outputFile": "${sampleName}_sorted.bam",
"outputFormat": "bam",
"threads": 4
},
"dependsOn": []
},
{
"stepId": "index",
"toolName": "samtools",
"description": "Index the sorted BAM",
"parameters": {
"samtoolsCommand": "index",
"inputFile": "${sort.outputFile}",
"outputFile": "${sampleName}_sorted.bam.bai",
"outputFormat": "bai"
},
"dependsOn": ["sort"]
}
]
}Save it as pipelines/specs/sort-and-index.pipeline.json, check it, and run it:
# 1. Check the spec — same checks a run applies
curl -X POST https://api.smarts.bio/v1/pipelines/validate \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "workspace_id": "ws_abc123", "spec_file": "pipelines/specs/sort-and-index.pipeline.json" }'
# → { "valid": true, "errors": [], "warnings": [] }
# 2. Run it on a sample
curl -X POST https://api.smarts.bio/v1/pipelines \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"spec_file": "pipelines/specs/sort-and-index.pipeline.json",
"workspace_id": "ws_abc123",
"input": { "inputBam": "input/sample1.bam", "sampleName": "sample1" }
}'
# → { "data": { "id": "pipe_abc123", "status": "queued", ... } }
# 3. Poll until it finishes
curl "https://api.smarts.bio/v1/pipelines/pipe_abc123?workspace_id=ws_abc123" \
-H "Authorization: Bearer sk_live_..."Spec format
Top-level fields
| Field | Type | Description |
|---|---|---|
| specVersion | string | Spec format version. Saved specs are written with "1.0". |
| id | string | Identifier for the pipeline. Saved specs get the name plus a short suffix so they never collide with a built-in ID. |
| name | string | Display name. A run started from a spec file is named after the file. |
| description | string | What the pipeline does. |
| category, tags, estimatedDuration | string / string[] | Optional metadata shown in listings and the viewer. |
| executionMode | string | Must be "process-manager" when present. Agent-driven workflows ("agent-workflow") can't be saved or re-run as specs yet. |
| inputs | object[] | Values supplied per run. See below. |
| steps * | object[] | Non-empty list of steps, at most 12. See below. |
| provenance | object | Written by save-spec: sourceExecutionId, sourcePipelineId, sourceRunStatus, extractedAt, and previousValues — the inputs the source run used. Informational only; never applied to a new run. |
inputs[]
| Field | Type | Description |
|---|---|---|
| name * | string | Starts with a letter or underscore; letters, digits and underscores only. Unique, and must not equal any stepId. |
| type * | string | file (a workspace path), string, or select. file[] is reserved for batch runs and is not supported yet. |
| description | string | Shown in the viewer run form and by the CLI / MCP inspect commands. |
| default | string | Used when a run omits the input. An input without a default is required. |
| options | string[] | Allowed values — required, and non-empty, for select. |
file inputs take workspace-relative paths (input/sample1.bam); the platform expands them to the full storage key at run time.
steps[]
| Field | Type | Description |
|---|---|---|
| stepId * | string | Unique; letters, digits, _ and - only. |
| toolName * | string | A registered compute tool, e.g. samtools, bwa, gatk, fastqc, trimmomatic, bowtie2, hisat2, picard, bedtools, annovar. Unknown names are rejected with the full list. |
| parameters | object | Tool parameters. The subcommand goes in <toolName>Command (e.g. samtoolsCommand) or command, and is checked against the tool's known commands — for samtools: view, sort, index, merge, stats, flagstat, idxstats, depth, coverage, tabix. Name outputs with outputFile / outputFile2. |
| dependsOn | string[] | Step IDs that must finish first. Must reference existing steps and must not form a cycle. Steps with no pending dependencies run in parallel. |
| description | string | Shown in progress output and the viewer. |
| timeout | number (ms) | Per-step time limit. Values above 2 hours (7,200,000 ms) are clamped to 2 hours, with a warning. |
| executionType | string | ecs, external-api or batch. Registered tools always run on ECS — any other value is ignored with a warning. |
Interpolation
Any string parameter can reference values with ${…}:
| Reference | Resolves to |
|---|---|
| ${inputName} | The value supplied for a declared input (or its default). |
| ${stepId.outputFile} | A file produced by an earlier step (outputFile2 for its second output). The producing step must be in this step's dependsOn, directly or transitively. |
Every reference must resolve — a typo such as ${inputBamm} is a validation error, not a silently missing argument. If a step reads ${producer.outputFile} but the producer declares no outputFile, you get a warning: the file is then matched by position, which can pick the wrong one when a tool writes several outputs.
Save a run and re-run it on different files
You rarely need to write a spec by hand. Run a pipeline once — a predefined one, or one the agent designed for you in chat — then save the run. Saving turns it into a reusable spec:
- Workspace files the run read become declared
fileinputs; the values it used are kept inprovenance.previousValuesfor reference. - Files produced by an earlier step are wired as
${stepId.outputFile}— never turned into inputs. - A shared output-filename prefix (e.g.
S1_sorted.bam,S1_sorted.bam.bai) becomes asampleNameinput. - File inputs never keep a default, so a re-run can't silently reuse the previous run's data.
- Anything that needs a human look — a list of files left as literals, a run that ended as
failed— comes back as a warning.
A name that is already taken is refused (409) unless you ask to overwrite. From any surface:
Web app
In the Processes panel, open a finished (completed or failed) pipeline run and click Save as pipeline. The spec is saved to pipelines/specs/; open it to see its inputs and steps, fill in new inputs, and click Run pipeline.
VS Code & JupyterLab
Open any *.pipeline.json from the smarts.bio file explorer. The pipeline viewer shows the inputs (with the values the last run used), the step graph, and a run form — fill in the required inputs and click Run pipeline.
API & CLI
# 1. Save a finished run
curl -X POST https://api.smarts.bio/v1/pipelines/pipe_abc123/save-spec \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{ "workspace_id": "ws_abc123", "name": "sort-and-index" }'
# → 201 { "path": "pipelines/specs/sort-and-index.pipeline.json", "spec": { ... }, "warnings": [] }
# 2. Re-run it on a different sample
curl -X POST https://api.smarts.bio/v1/pipelines \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"spec_file": "pipelines/specs/sort-and-index.pipeline.json",
"workspace_id": "ws_abc123",
"input": { "inputBam": "input/sample2.bam", "sampleName": "sample2" }
}'The SDKs don't wrap spec_file, spec, save-spec or validate yet — call the HTTP endpoints directly or use the CLI. Full endpoint details are in the Pipelines reference.
AI assistants (MCP)
The hosted and local MCP servers expose the same flow, so you can simply ask your assistant to "save that run as a pipeline and run it on sample2":
smarts_list_pipelines— built-in pipelines, plus the specs saved in a workspace whenworkspace_idis given.smarts_get_pipeline— a pipeline's inputs (required or not, defaults) and steps, bypipeline_idorspec_file.smarts_run_pipeline— run by exactly one ofpipeline_idorspec_file.smarts_save_pipeline— save a finished run (run_id, optionalnameandoverwrite).
Run a spec from your own repo
A spec doesn't have to live in the workspace. Send it inline as spec — handy when specs are versioned alongside your analysis code. The CLI does this automatically when --spec points at a local file (prefix a path with @ to force the workspace copy instead).
curl -X POST https://api.smarts.bio/v1/pipelines \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d "$(jq -n --slurpfile spec pipelines/sort-and-index.pipeline.json '{
spec: $spec[0],
workspace_id: "ws_abc123",
input: { inputBam: "input/sample3.bam", sampleName: "sample3" }
}')"Provenance: run.json
Every pipeline run writes a run.json manifest into its output folder, pipelines/<run folder>/run.json (the folder is a timestamp such as 2026-09-19_14-03-22). It is written when the run starts and updated when it finishes, so a set of results always records what produced it. The manifest embeds the spec exactly as executed — editing a saved spec later never rewrites the history of past runs.
| Field | Type | Description |
|---|---|---|
| schemaVersion | integer | Manifest format version (currently 1). |
| executionId, pipelineId, pipelineName | string | Which run, and which pipeline it ran. |
| status | string | Final (or current) run status. |
| workspaceId, organizationId, outputFolder | string | Where the run belongs and where its outputs landed. |
| startedAt, completedAt, durationMs | string / number | Run timing (ISO 8601 timestamps). |
| stepsTotal, stepsCompleted, stepsFailed | integer | Step counts. |
| parameters | object | The concrete input values the run used, defaults applied. |
| inputChecksums | object | Per file input: storage key, checksum and size — what the inputs pointed at, by content. |
| pipelineDefinition | object | The spec exactly as executed. |
| steps[] | object[] | Per step: stepId, stepNumber, toolName, toolVersion, imageDigest (exact container pin), taskDefinitionArn, status, dependsOn, timing, outputFiles (filename, key, size, checksum) and error if it failed. |
{
"schemaVersion": 1,
"executionId": "…",
"pipelineId": "sort-and-index",
"pipelineName": "sort-and-index",
"status": "completed",
"workspaceId": "ws_abc123",
"organizationId": "…",
"outputFolder": "2026-09-19_14-03-22",
"startedAt": "2026-09-19T14:03:22.000Z",
"completedAt": "2026-09-19T14:09:51.000Z",
"durationMs": 389000,
"stepsTotal": 2,
"stepsCompleted": 2,
"stepsFailed": 0,
"parameters": { "inputBam": "…/input/sample2.bam", "sampleName": "sample2" },
"inputChecksums": { "inputBam": { "s3Key": "…/input/sample2.bam", "checksum": "…", "size": 1843200 } },
"pipelineDefinition": { "…": "the spec exactly as executed" },
"steps": [
{
"stepId": "sort",
"stepNumber": 1,
"toolName": "samtools",
"toolVersion": "…",
"imageDigest": "sha256:…",
"taskDefinitionArn": "…:12",
"status": "completed",
"dependsOn": [],
"startedAt": "…",
"completedAt": "…",
"durationMs": 241000,
"outputFiles": [{ "filename": "sample2_sorted.bam", "s3Key": "…", "size": 1790000, "checksum": "…" }]
}
]
}Limits
| Limit | Value | Behaviour |
|---|---|---|
| Steps per spec | 12 | More is a validation error. |
| Step timeout | 2 hours | Longer timeout values are clamped to 2 hours, with a warning. |
| Concurrent compute tasks per organization | 5 | Further steps stay queued and start as slots free up — they never fail because of the limit. A 9-step pipeline still completes, just paced. |
| Spec file size | 1 MB | Larger spec files are not read. |