Workflow YAML
Workflow files live in .workflows/ and contain actions, tasks, triggers, connections, and secrets. This page documents every available field.
Top-Level Structure
Section titled “Top-Level Structure”secrets: MY_SECRET: "{{ env.MY_SECRET }}"
connection_types: <name>: { ... }
connections: <name>: { ... }
actions: <name>: { ... }
tasks: <name>: { ... }
triggers: <name>: { ... }
on_success: - action: <name>on_error: - action: <name>All top-level keys are optional. A file typically defines actions and tasks.
The top-level on_success and on_error hooks act as workspace-level fallbacks — they fire for top-level jobs when the task itself has no hooks defined for that event type.
Actions
Section titled “Actions”Actions are the smallest execution unit. Each action defines what runs on a worker.
Common fields
Section titled “Common fields”| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | Action type: script, docker, pod, task, agent, or approval |
name |
string | — | Human-readable display name |
description |
string | — | What this action does |
input |
map | {} |
Input parameter schema (see Input fields) |
output |
object | — | Output schema (see Output fields) |
env |
map | — | Environment variables. Values support Tera templates |
tags |
list | [] |
Worker routing tags for step claiming |
workdir |
string | — | Working directory override |
resources |
object | — | CPU/memory requests (see Resources) |
retry |
object | — | Default retry config. Step-level retry overrides action-level (see Retry) |
type: script
Section titled “type: script”Runs a script in a runner environment with workspace files mounted.
| Field | Type | Default | Description |
|---|---|---|---|
script |
string | — | Inline script content. Mutually exclusive with source |
source |
string | — | Path to script file (relative to workspace root). Mutually exclusive with script |
runner |
string | local |
Execution environment: local, docker, or pod |
language |
string | shell |
Script language: shell, python, javascript (or js), typescript (or ts), go |
dependencies |
list | [] |
Packages to install before running. Requires non-shell language |
interpreter |
string | — | Override auto-detected interpreter binary (e.g., python3.11) |
manifest |
object | — | K8s pod manifest overrides. Only valid with runner: pod |
Either script or source is required. Cannot use image, cmd, or entrypoint.
Toolchain preferences (auto-detected when interpreter is not set):
- Python:
uv>python3>python - JavaScript:
bun>node - TypeScript:
bun>deno - Shell:
bash>sh
actions: run-tests: type: script language: python dependencies: [pytest, requests] script: | import pytest pytest.main(["-v", "tests/"])type: docker
Section titled “type: docker”Runs a container image as-is, without workspace mounting.
| Field | Type | Default | Description |
|---|---|---|---|
image |
string | required | Docker image URI |
cmd |
string | — | Shell command to run in the container |
entrypoint |
list | — | Container entrypoint override |
command |
list | — | Container command args |
Cannot use runner, script, source, language, dependencies, or interpreter.
actions: run-migration: type: docker image: myapp/migrations:latest command: ["migrate", "--target", "latest"]type: pod
Section titled “type: pod”Runs a Kubernetes pod. Same fields as type: docker, plus:
| Field | Type | Default | Description |
|---|---|---|---|
manifest |
object | — | Raw JSON/YAML deep-merged into the generated pod spec |
The manifest field supports service accounts, node selectors, tolerations, resource limits, annotations, sidecars, and other pod-level configuration.
actions: gpu-inference: type: pod image: myapp/inference:latest manifest: spec: containers: - name: main resources: limits: nvidia.com/gpu: "1" tolerations: - key: nvidia.com/gpu operator: Exists effect: NoScheduletype: task
Section titled “type: task”References another task, creating a child job. The step is dispatched server-side (workers never claim it).
| Field | Type | Default | Description |
|---|---|---|---|
task |
string | required | Name of the task to execute. May be another workspace’s task (ws.task) |
ref |
string | — | Branch, tag or full commit SHA of the task’s owner workspace to run the task from — see Git Refs. Only valid on type: task actions |
Cannot use any other execution fields (cmd, script, source, image, runner, language, manifest, etc.).
actions: run-deploy: type: task task: deploy-pipeline
tasks: orchestrate: flow: deploy: action: run-deploy input: env: "production"
deploy-pipeline: input: env: { type: string } flow: # ... stepsChild jobs have a max nesting depth of 10 levels.
With ref, the task is looked up at that ref of its owner workspace and the
child runs pinned to the commit the ref resolved to when the parent job was
created. A task may call itself at another ref (bounded by the nesting
depth). A type: task action carrying ref cannot be used as a hook.
type: agent
Section titled “type: agent”Calls an LLM as a workflow step. The step is dispatched server-side (workers never claim it).
| Field | Type | Default | Description |
|---|---|---|---|
provider |
string | required | Provider ID from server config |
prompt |
string | required | Tera template for the user message |
system_prompt |
string | — | Tera template for system/instruction message |
model |
string | — | Override provider’s default model |
max_tokens |
integer | — | Override provider’s max tokens |
temperature |
number | — | Override provider’s temperature (0–2) |
output |
object | — | Output schema (converted to JSON Schema at dispatch) |
Cannot use any other execution fields (cmd, script, source, image, runner, language, manifest, task, etc.).
ref is not supported with agents yet: a flow step that names an agent
action with ref is rejected with 400, and a tools: [{task: …}] entry
with ref is a validation error (a tool call naming it is rejected). Agent
steps inside a job that runs on a git ref work and use
that commit’s definitions.
actions: classify-ticket: type: agent provider: anthropic-main system_prompt: "You are a support ticket classifier." prompt: "Classify this ticket: {{ input.ticket_body }}" output: category: type: string required: true options: [bug, feature_request, question] confidence: type: number
tasks: handle-support: input: ticket_body: { type: string } flow: classify: action: classify-ticket input: ticket_body: "{{ input.ticket_body }}" route-bug: action: escalate depends_on: [classify] when: "{{ classify.output.category == 'bug' }}"type: approval
Section titled “type: approval”Pauses execution for human approval/rejection. The step is dispatched server-side (workers never claim it).
| Field | Type | Default | Description |
|---|---|---|---|
message |
string | — | Tera template for the approval message shown to approver |
Cannot use any other execution fields (cmd, script, source, image, runner, language, manifest, task, provider, prompt, etc.).
actions: approve-deployment: type: approval message: | Deploy to production? Version: {{ input.version }} Environment: {{ input.env }}
tasks: deploy-workflow: input: version: { type: string } env: { type: string } flow: manual-approval: action: approve-deployment input: version: "{{ input.version }}" env: "{{ input.env }}" deploy: action: run-deployment depends_on: [manual-approval] input: version: "{{ input.version }}"When approved, the step’s output is { "approved": true, "approved_by": "user@example.com", "approved_at": "2025-03-21T10:30:00Z", "input": {...} }. When rejected, the step fails and downstream steps are skipped.
Input Fields
Section titled “Input Fields”Input parameters define the schema for action and task inputs.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | string, text, integer, number, boolean, date, datetime, json, file, or a connection type name. Alias: bool is accepted as a shorthand for boolean and normalized on load |
name |
string | — | Human-readable label for the UI |
description |
string | — | Help text |
required |
bool | false |
Whether the field must be provided |
secret |
bool | false |
Mask value in UI and logs |
default |
any | — | Default value when not provided |
options |
list | — | Predefined dropdown choices |
allow_custom |
bool | false |
Allow values outside options list |
multiple |
bool | false |
Accept multiple values (requires options). Submitted value is a JSON array; see Multi-select |
order |
integer | — | Display order in UI (lower = earlier). Fields without order appear last |
input: environment: type: string description: Target environment required: true options: [staging, production] version: type: string default: "latest" dry_run: type: boolean default: false order: 99A file field (task or action level) takes an uploaded file; {{ input.<field> }} renders to a read-only path. It cannot have default, options, allow_custom, multiple or secret. file is a reserved name: a connection type cannot be called file. See File Inputs.
When type is not a primitive, it references a connection type. The input value (a connection name) is resolved to the full connection object at execution time.
Output Fields
Section titled “Output Fields”Output schemas describe the structured output of an action. Script actions emit output by printing OUTPUT: {json} to stdout. Agent actions use output to instruct the LLM to respond with structured JSON.
actions: build: type: script script: | echo "Building..." echo 'OUTPUT: {"artifact": "dist/app.tar.gz", "size": 1024}' output: artifact: { type: string } size: { type: integer }Output fields are defined directly under the output: key as a flat map of field names to field definitions.
Output field properties:
| Field | Type | Default | Description |
|---|---|---|---|
type |
String | Required | string, integer, number, boolean, array, object. Alias: bool is accepted for boolean |
description |
String | — | Human-readable description |
required |
Boolean | false |
Whether the field must be present |
default |
Any | — | Default value |
options |
Array | — | Allowed values (maps to JSON Schema enum) |
For agent actions, output is converted to JSON Schema at dispatch time and injected into the system prompt.
Resources
Section titled “Resources”CPU and memory requests for runner containers.
| Field | Type | Description |
|---|---|---|
cpu |
string | CPU request (e.g., "500m", "1") |
memory |
string | Memory request (e.g., "256Mi", "1Gi") |
actions: heavy-compute: type: script runner: pod resources: cpu: "2" memory: "4Gi" script: "..."Tasks compose actions into a DAG of steps.
| Field | Type | Default | Description |
|---|---|---|---|
name |
string | — | Human-readable display name |
description |
string | — | What this task does |
mode |
string | distributed |
Execution mode: distributed |
folder |
string | — | UI folder path (e.g., deploy/staging). Use / for nesting |
input |
map | {} |
Input parameter schema (see Input fields) |
flow |
map | required | Steps — map of step name to flow step |
timeout |
duration | — | Job-level timeout. Max 7d (604800s). Cancels entire job when exceeded |
retry |
object | — | Retry entire task on failure, creating a new job (see Retry) |
on_success |
list | [] |
Hooks to run when the job succeeds |
on_error |
list | [] |
Hooks to run when the job fails |
tasks: deploy-pipeline: name: Deploy Pipeline description: Build, deploy, and verify folder: deploy timeout: 30m input: env: { type: string, default: "staging" } flow: build: action: build-app deploy: action: deploy depends_on: [build] on_error: - action: notify input: message: "Deploy failed: {{ hook.error_message }}"Flow Steps
Section titled “Flow Steps”Each entry in a task’s flow map defines a step. Steps can reference a named action or define one inline.
Step fields
Section titled “Step fields”| Field | Type | Default | Description |
|---|---|---|---|
action |
string | required | Action name to execute. Omit when using inline action |
ref |
string | — | Run action from this branch, tag or full commit SHA of its owner workspace — see Git Refs. The action need not exist on the default branch. Not allowed when the action is type: agent |
name |
string | — | Human-readable step display name |
description |
string | — | What this step does |
depends_on |
list | [] |
Dependencies that must reach a terminal state before this one is decided (see Dependencies for the full {step, accept} / all / any shape) |
input |
map | {} |
Input values passed to the action. Values support Tera templates |
continue_on_failure |
bool | false |
Self-scoped only: if this step fails, its failure does not fail the job. Has no effect on whether anything downstream runs — that’s depends_on’s accept sets on each dependent (see Dependencies) |
timeout |
duration | — | Step execution timeout. Max 24h (86400s) |
when |
string | — | Tera condition. Falsy values: empty string, "false", "null", "none" (case-insensitive), "[]", "{}" and any number equal to zero ("0", "0.0") |
for_each |
string or list | — | Tera expression or literal JSON array. Creates one instance per item |
sequential |
bool | false |
Run for_each instances one at a time instead of in parallel |
retry |
object | — | Retry configuration for this step (see Retry) |
Inline actions
Section titled “Inline actions”Instead of referencing a named action, define one inline by adding action fields directly on the step:
flow: greet: type: script script: "echo Hello {{ input.name }}" input: name: "{{ input.name }}"All action fields are valid on inline steps (e.g., type, script, source, image, runner, language, env, tags).
Dependencies
Section titled “Dependencies”flow: a: action: step-a b: action: step-b c: action: step-c depends_on: [a, b] # waits for both a and b to completeSteps without depends_on start immediately. Each entry in a depends_on list is one of four shapes; the list itself is an implicit all group:
| Shape | Meaning |
|---|---|
plain-name |
Sugar for {step: plain-name, accept: [completed]} — today’s default: the dependency must complete |
{step: name, accept: [...]} |
Satisfied iff name’s outcome is in the accept list |
{step: name, accept: any} |
Sugar for all five outcomes below — “wait for it, don’t care how it ends” |
{all: [...]} |
Satisfied iff every nested entry is satisfied (an explicit, nestable AND) |
{any: [...]} |
Satisfied iff at least one nested entry is satisfied (OR) |
accept is a non-empty list drawn from five outcomes, or the literal string any (any outcome — not to be confused with the any: group, which needs any one of its entries satisfied):
| Outcome | Meaning |
|---|---|
completed |
The dependency’s status is completed |
failed |
The dependency’s status is failed |
cancelled |
The dependency’s status is cancelled |
skipped |
The dependency was skipped by its own choice — its own when was false, or its own for_each was empty |
omitted |
The dependency was itself blocked by its own depends_on tree — not its own choice |
Readiness is uniform: the gate waits until every step named anywhere in the tree — including nested inside all/any — has reached a terminal state, then evaluates the whole tree exactly once. This holds even when the tree is already conclusively satisfied or unsatisfiable by one child — there’s no fail-fast, and no short-circuiting an any ahead of a still-running sibling. A step whose tree is not satisfied is skipped with skip_reason: unreachable.
all/any nest arbitrarily (an any may contain an all, a step entry, or a bare name). The same step name may appear more than once across different branches with different accept sets — that’s a legitimate way to express an OR over different outcomes of the same dependency, and is accepted. It’s rejected only when the same name is a direct sibling of the same all/any node (including the implicit top-level group): depends_on: [a, a]-style duplicates must be deduplicated.
continue_on_failure is unrelated to any of this — it only controls whether a step’s own failure fails the job (see the continue_on_failure row above), never whether anything downstream runs. See the Conditionals guide for worked examples, and the 0.18 upgrade guide if migrating from continue_on_failure/continue_when_skipped-based gating.
Every skipped step records a skip_reason (condition, empty, or unreachable; a legacy cascade reason can still appear on rows written before 0.18.0 and is read identically to unreachable); see the Conditionals guide.
Conditional steps (when)
Section titled “Conditional steps (when)”flow: deploy: action: deploy-app when: "{{ input.env == 'production' }}" input: env: "{{ input.env }}"Evaluated before for_each. If the condition errors, the step fails (not silently skipped). See the Conditionals guide for details.
For-each loops
Section titled “For-each loops”flow: deploy: action: deploy-to-region for_each: ["us-east-1", "eu-west-1", "ap-south-1"] input: region: "{{ each.item }}" index: "{{ each.index }}"Creates deploy[0], deploy[1], deploy[2]. Inside instances, each.item is the current value and each.index is the zero-based position.
Downstream steps receive the aggregated output as an array via {{ step_name.output }}.
Limits: max 10,000 items. Step names must not contain [ or ]. See the Loops guide for sequential mode, error handling, and sub-job fan-out.
Timeouts
Section titled “Timeouts”Accepts human-readable durations: 30s, 5m, 1h30m, or a plain integer (seconds).
flow: build: action: build-app timeout: 10mEnforced both server-side (recovery sweeper) and worker-side (process cancellation).
Strøm supports two layers of retry: step-level retry (retries the step in-place with backoff) and task-level retry (retries the entire task by creating a new job).
Retry configuration fields
Section titled “Retry configuration fields”| Field | Type | Default | Description |
|---|---|---|---|
max_attempts |
integer | 3 |
Maximum number of attempts (including first attempt). Range: 1–10 |
delay |
duration | "30s" |
Delay before first retry. Max 1h (3600s). Accepts 30s, 5m, 1h, or integer seconds |
backoff |
string | "fixed" |
Strategy: "fixed" (constant delay) or "exponential" (delay doubles each attempt, capped at 64x) |
jitter |
bool | false |
Add 0–25% random jitter to delay (reduces thundering herd on mass retries) |
Step-level retry
Section titled “Step-level retry”Retries a single step in-place. When a step fails, the server waits for the configured delay, then resets the step and allows a worker to claim and re-execute it.
actions: fetch-data: type: script script: | curl https://api.example.com/data || exit 1 echo 'OUTPUT: {"result": "ok"}'
tasks: main: flow: get-data: action: fetch-data retry: max_attempts: 3 delay: "10s" backoff: exponentialHow it works:
- Attempt 1 fails
- Wait 10s, then retry (Attempt 2)
- If Attempt 2 fails, wait 20s, then retry (Attempt 3)
- If Attempt 3 fails, the step fails; its dependents run only if their own
depends_onedge to this step acceptsfailed— otherwise they’re skippedunreachable - All data from previous attempts is discarded; output is only captured from the final attempt
Step-level vs. action-level: If both action and step define retry, the step’s configuration takes precedence:
actions: api-call: type: script script: "curl https://api.example.com" retry: max_attempts: 2 # Action default delay: "5s"
tasks: main: flow: call: action: api-call retry: max_attempts: 4 # Step overrides action's max_attempts delay: "10s"Action-level retry defaults
Section titled “Action-level retry defaults”Actions can define default retry behavior that applies to all steps using that action, unless the step overrides it:
actions: unstable-task: type: script script: "some-flaky-operation" retry: max_attempts: 3 delay: "30s" backoff: exponential
tasks: main: flow: step1: action: unstable-task # Uses action's retry config: 3 max_attempts, 30s delay, exponential backoff
step2: action: unstable-task retry: max_attempts: 5 # Overrides action defaultTask-level retry
Section titled “Task-level retry”Retries the entire task by creating a new job when the original job fails. This is useful when you want to retry all steps from the beginning on failure.
tasks: deploy: retry: max_attempts: 2 delay: "60s" flow: build: action: build-app test: action: run-tests deploy: action: deploy-appHow it works:
- Original job fails at
deploystep - Server waits 60s, then creates a new job with the same task and input
- New job starts from
buildstep - If the new job also fails, no further retries (max_attempts: 2 exhausted)
- On final failure,
on_errorhooks fire only once (not after each retry attempt)
Retry chain: every retry job’s retry_of_job_id points at the first job of the chain; each failed attempt’s retry_job_id points at the attempt that replaced it:
Job 1 (failed) retry_job_id → 2 └─ Job 2 (failed) retry_of_job_id → 1, retry_job_id → 3 └─ Job 3 (success) retry_of_job_id → 1If the first job has since been deleted by retention, the pointer is cleared (ON DELETE SET NULL), and the next retry points at the attempt that just failed instead — not at the earliest attempt that still exists.
Task retry vs. step retry:
- Step retry: Faster, smaller scope, good for transient failures (network timeouts, temporary service degradation)
- Task retry: Slower, full job restart, good for cascading failures (dependency issues, state cleanup needed)
Backoff strategies
Section titled “Backoff strategies”Fixed backoff
Section titled “Fixed backoff”Constant delay between retries. Simple and predictable.
retry: delay: "30s" backoff: fixed # or omit (default)Delays: 30s, 30s, 30s, …
Exponential backoff
Section titled “Exponential backoff”Delay doubles with each attempt (up to a maximum of 64x the initial delay).
retry: delay: "10s" backoff: exponential jitter: true # Recommended for high concurrencyDelays without jitter: 10s, 20s, 40s, 80s (capped at 640s), 640s, …
With jitter, each delay gets ±0–25% random variance:
- 10s → 7.5s–12.5s
- 20s → 15s–25s
- 40s → 30s–50s
Jitter reduces synchronized retries across many workers (thundering herd problem).
Limits and constraints
Section titled “Limits and constraints”| Constraint | Limit | Example |
|---|---|---|
max_attempts |
1–10 | max_attempts: 5 (5 total attempts) |
delay |
0–3600s (1h) | delay: "30s" or delay: "1h" |
| Exponential cap | 64x initial delay | With delay: 10s, max is 640s |
| Step timeout vs. retry | Step timeout applies per attempt | Step with timeout: 5m and retry will try up to 5m per attempt |
Interactions
Section titled “Interactions”Retry with continue_on_failure
Section titled “Retry with continue_on_failure”Step retry exhausts first; continue_on_failure then decides whether the job still counts as succeeded, independent of whether next runs (which is next’s own accept set):
flow: unstable: action: flaky-operation retry: max_attempts: 3 continue_on_failure: true # job still completes even if all retries fail
next: action: next-step depends_on: - step: unstable accept: [completed, failed] # Runs even if all 3 retry attempts failRetry with for_each (loop instances)
Section titled “Retry with for_each (loop instances)”Loop instances inherit the step’s retry config:
flow: process: action: process-item for_each: [a, b, c] retry: max_attempts: 2 # Each instance (process[0], process[1], process[2]) gets max 2 attemptsRetry with when (conditional steps)
Section titled “Retry with when (conditional steps)”Retry applies only if the step actually runs:
flow: deploy: action: deploy-app when: "{{ input.env == 'prod' }}" retry: max_attempts: 3 # Retries only if condition is true; skipped if condition is falseRetry with timeouts
Section titled “Retry with timeouts”Step timeout applies per attempt. Task timeout applies to the entire retry chain:
tasks: main: timeout: 10m # Total time across all retry attempts flow: fetch: action: fetch-data timeout: 2m # 2m per attempt retry: max_attempts: 3 delay: "30s"If attempts are 2m, 2m, 2m (6m) plus delays (30s, 30s = 1m), total is 7m — well under the 10m task timeout.
Retry with hooks
Section titled “Retry with hooks”Hooks fire after all retry attempts are exhausted:
tasks: main: flow: unstable: action: flaky-operation retry: max_attempts: 3 on_error: - action: notify-ops # Fires only after all 3 attempts fail, not after each attemptViewing retry history
Section titled “Viewing retry history”Retried steps are visible in the job detail UI with:
- Retry badge showing attempt number (e.g., “Attempt 2 of 3”)
- Timestamp of each attempt
- Previous attempt’s output (if available)
- Backoff delay until next retry
Task-level retries create new jobs that appear in the job list, linked by retry_of_job_id.
Hooks fire when a job reaches a terminal state. Defined on tasks or at the top level of the workflow file.
| Field | Type | Default | Description |
|---|---|---|---|
action |
string | required | Action to execute. Can be a type: task action for full child job |
input |
map | {} |
Input values. Supports Tera templates with hook context |
ref is not supported on hooks yet: a hook with ref, or one whose action
is a type: task action carrying ref, is a validation error and is never
fired.
Hook template context
Section titled “Hook template context”| Variable | Type | Description |
|---|---|---|
hook.workspace |
string | Workspace name |
hook.task_name |
string | Task that completed/failed |
hook.job_id |
string | Job UUID |
hook.status |
string | "completed" or "failed" |
hook.is_success |
bool | true if completed |
hook.error_message |
string/null | All failed step errors combined |
hook.source_type |
string | Original job source ("api", "trigger", etc.) |
hook.source_id |
string/null | Original job source ID |
hook.started_at |
string/null | ISO 8601 timestamp |
hook.completed_at |
string/null | ISO 8601 timestamp |
hook.duration_secs |
number/null | Execution duration in seconds |
hook.failed_steps |
list | Failed step details (see below) |
hook.revision |
string/null | Workspace revision pinned on the job (git SHA or folder hash) |
hook.ref |
string/null | The git ref the job runs at; null unless it is a pinned job |
Each entry in hook.failed_steps:
| Field | Type | Description |
|---|---|---|
step_name |
string | Name of the failed step |
action_name |
string | Action that was executed |
error_message |
string/null | The step’s error message |
continue_on_failure |
bool | Whether this row’s own flow step has continue_on_failure set. A loop instance row reports its placeholder’s flag |
tolerated |
bool | Same value as continue_on_failure above — this failure does not by itself fail the job; other untolerated failures or a cancellation can still decide the outcome |
tasks: deploy: flow: # ... on_success: - action: notify input: message: "Deploy completed in {{ hook.duration_secs }}s" on_error: - action: notify input: message: "Deploy failed: {{ hook.error_message }}"Hook jobs use source_type = "hook" and never trigger further hooks (recursion guard).
Triggers
Section titled “Triggers”Scheduler (type: scheduler)
Section titled “Scheduler (type: scheduler)”| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | scheduler |
cron |
string | required | Cron expression (5-field, or 6-field with optional seconds) |
task |
string | required | Task to execute. May be another workspace’s task (ws.task) |
ref |
string | — | Branch, tag or full commit SHA of the task’s owner workspace — see Git Refs |
input |
map | {} |
Input values |
enabled |
bool | true |
Whether the trigger is active |
timezone |
string | UTC | IANA timezone name (e.g., Europe/Copenhagen) |
concurrency |
string | allow |
allow, skip, or cancel_previous |
triggers: nightly-backup: type: scheduler cron: "0 2 * * *" task: run-backup timezone: "Europe/Copenhagen" concurrency: skipWebhook (type: webhook)
Section titled “Webhook (type: webhook)”| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | webhook |
name |
string | required | URL-safe name (alphanumeric, -, _). Endpoint: /hooks/{name} |
task |
string | required | Task to execute. May be another workspace’s task (ws.task) |
ref |
string | — | Branch, tag or full commit SHA of the task’s owner workspace — see Git Refs |
secret |
string | — | Auth via ?secret=xxx or Authorization: Bearer xxx |
input |
map | {} |
Default input values. Request body/headers/query merged in |
enabled |
bool | true |
Whether the webhook is active |
mode |
string | async |
async (fire-and-forget) or sync (wait for job completion) |
timeout_secs |
integer | 30 |
Max wait in sync mode (1–300). Returns 202 on timeout |
triggers: github-push: type: webhook name: github-ci task: ci-pipeline secret: "whsec_your_secret" mode: sync timeout_secs: 120Event Source (type: event_source)
Section titled “Event Source (type: event_source)”Long-running queue consumer processes that create jobs via OUTPUT: prefix protocol.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | event_source |
task |
string | required | Consumer task that runs continuously |
target_task |
string | required | Task to create jobs for from emitted events |
env |
map | {} |
Tera-templated environment variables for consumer execution |
input |
map | {} |
Default input merged into each emitted job |
restart_policy |
string | always |
always, on_failure, or never |
backoff_secs |
integer | 5 |
Initial restart delay with exponential backoff |
max_in_flight |
integer | — | Max concurrent pending/running target jobs (optional, unlimited if omitted) |
enabled |
bool | true |
Whether the consumer is active |
triggers: sqs-events: type: event_source task: sqs-consumer target_task: process-order env: QUEUE_URL: "{{ secret.sqs_url }}" input: priority: high restart_policy: always backoff_secs: 5 max_in_flight: 10The consumer task runs as a normal job. Any step’s OUTPUT: lines (followed by valid JSON) create jobs for target_task. The input defaults are merged with the emitted JSON (emitted data wins on conflict).
ref is not supported on event-source triggers yet: it is a validation
error, and the server does not start such a consumer.
Connection Types
Section titled “Connection Types”Define schemas for reusable connection configurations.
| Field | Type | Description |
|---|---|---|
properties |
map | Map of property names to property definitions |
Property fields
Section titled “Property fields”| Field | Type | Default | Description |
|---|---|---|---|
type |
string | required | string, text, integer, number, boolean, date, or datetime. Alias: bool is accepted for boolean |
required |
bool | false |
Whether the property must be provided |
default |
any | — | Default value |
secret |
bool | false |
Mask in UI and logs |
connection_types: postgres: host: { type: string, required: true } port: { type: integer, default: 5432 } database: { type: string, required: true } username: { type: string, required: true } password: { type: string, required: true, secret: true }Connections
Section titled “Connections”Named instances of connection types.
| Field | Type | Default | Description |
|---|---|---|---|
type |
string | — | Connection type reference (for validation). Optional for untyped connections |
| (other keys) | any | — | Property values (flattened alongside type) |
connections: prod-db: type: postgres host: "db.example.com" port: 5432 database: myapp username: "{{ secrets.DB_USER }}" password: "{{ secrets.DB_PASS }}"Use connection names as input values when the input field’s type references a connection type. The name is resolved to the full connection object at execution time.
Secrets
Section titled “Secrets”Key-value map rendered through Tera at workspace load time. Typically sources values from environment variables:
secrets: DB_PASSWORD: "{{ env.DB_PASSWORD }}" API_KEY: "{{ env.MY_API_KEY }}" STATIC_VALUE: "hardcoded-is-fine-too"Secrets are available in connection values and action templates via {{ secrets.DB_PASSWORD }}.
Duration Format
Section titled “Duration Format”Timeout fields accept human-readable durations or plain integers (seconds):
| Format | Example | Seconds |
|---|---|---|
| Seconds | 30s |
30 |
| Minutes | 5m |
300 |
| Hours | 1h |
3600 |
| Combined | 1h30m |
5400 |
| Plain integer | 300 |
300 |