Appearance
Workflows
A workflow is an automated process made of nodes (steps) connected by edges. Each node does one thing: query the API, branch on a condition, call a URL, send a notification, wait for a person, run a Lua script, and so on. Data flows along the edges, and each node can read the workflow input and the output of the nodes before it.
You build workflows in the console's visual editor (Workflows, /#/workflows) or write the definition as YAML or JSON and import it through the API. Runs are listed under Executions (/#/workflow-executions).
This section has four pages:
- Workflows (this page): definitions, versions, running and executions
- Expressions: how node settings reference data
- Node reference: every node type
- Triggers: starting workflows on a schedule or on events
A workflow definition
A definition has three lists: nodes, edges and, optionally, variables. This workflow loads a job, checks its quantity and logs a different message for large and small runs:
yaml
nodes:
- id: fetch_job
name: Fetch job
type: graphql
config:
operation: query
query: 'query GetJob($id: JobID!) { getJob(id: $id) { id name quantity status } }'
variables:
id: "{{ input.jobId }}"
maxRetries: 2
timeoutSeconds: 15
- id: is_large
name: Large run?
type: condition
config:
expression: nodes.fetch_job.output.data.getJob.quantity > 1000
- id: log_large
name: Log large run
type: log
config:
level: warning
message: "Large run: {{ nodes.fetch_job.output.data.getJob.name }} ({{ nodes.fetch_job.output.data.getJob.quantity }} copies)"
- id: log_small
name: Log small run
type: log
config:
message: "Small run: {{ nodes.fetch_job.output.data.getJob.name }}"
edges:
- id: e1
fromNodeId: fetch_job
toNodeId: is_large
- id: e2
fromNodeId: is_large
toNodeId: log_large
condition: "true"
- id: e3
fromNodeId: is_large
toNodeId: log_small
condition: "false"Run it with the input {"jobId": "job_..."}. The workflow needs a service account that can read jobs, or the graphql node fails with an authentication error.
Service account
A workflow acts as its service account, not as the person or trigger that starts it. The graphql, script, custom_workflow_action and integration_action nodes, and the data query of an event trigger, all run with that account's permissions. A workflow with no service account can't make authenticated calls.
Create a service account in the console (Service accounts), attach an IAM policy with the permissions the workflow needs (job:read for the example above), and pick it under Service account on the workflow's Settings tab. Through the API, set botUserId on the workflow.
Nodes
| Field | Required | Meaning |
|---|---|---|
id | yes | Unique within the workflow. Other nodes refer to this node's output by its id. |
name | yes | Display name in the editor and in execution steps |
type | yes | The node type, for example graphql or condition. See the node reference. |
config | depends on type | The node's settings. Each type has its own config schema. |
timeoutSeconds | no | Time limit for this node. Without it, the workflow's defaultNodeTimeoutSeconds applies. |
maxRetries | no | How often a failing node is retried before it counts as failed |
continueOnError | no | true lets the workflow carry on when this node fails, instead of failing the whole execution |
position | no | {x, y} position in the visual editor |
Edges
An edge connects fromNodeId to toNodeId. Edges leaving a branching node carry a condition label that names the branch they belong to. For a condition node the labels are "true" and "false". For a switch node they are the case names.
Variables
The optional variables list declares workflow variables with a name, a type, an optional defaultValue and a description. Declared variables start with their default value, and a set_variable node can change them during the run. Any node can read them as variables.<name>.
How data flows
- The workflow input is a JSON object, readable everywhere as
input.<field>. - Each node's result is readable by later nodes as
nodes.<id>.output.<field>. - Node settings use expressions to reference both.
Nodes run in waves: a node starts once every node before it has finished, and independent branches run in parallel.
Branching and joining
A plain node has one input and one output: at most one edge in and one edge out. Validation rejects a plain node with two outgoing or two incoming edges. Fan-out and fan-in are always explicit:
| Node | In | Out | Use |
|---|---|---|---|
split | 1 | many | Run all outgoing branches in parallel |
condition | 1 | many | Take the true or the false branch |
switch | 1 | many | Take the first matching case, or the default |
logic | 1 | many | Combine several conditions (and, or, xor…) into one true/false branch |
join | many | 1 | Wait for all live branches (AND) and combine their outputs |
merge | many | 1 | Combine whichever branches arrive (OR) |
Branches not taken are ignored by join and merge. The two differ only when a branch fails under continueOnError: merge drops the failed branch and goes on, while join applies its onPartialFailure setting (fail, the default, or skip).
Loops are not built with edges. The for_each node runs an embedded body node once per array item and outputs the collected results.
Versions and activation
A workflow (WorkflowState) holds settings such as its name, description and defaultNodeTimeoutSeconds. The definition lives in numbered versions (WorkflowVersionState).
- Every saved definition is a new version. Versions record whether they passed validation (
isValid,validationErrors). - The workflow runs the version named by its
currentVersionId. Creating a version does not change it: you activate a version explicitly. - In the console editor, Save Version (on the Flow tab) creates a version and Publish makes the selected version the active one.
- A workflow with no active version cannot run.
executeWorkflowfails with "No active version set for workflow".
With the API:
graphql
mutation CreateWorkflow($input: CreateWorkflowInput!) {
createWorkflow(input: $input) {
workflow { id name currentVersionId }
errors { path message }
}
}
mutation ImportDefinition($input: ImportWorkflowInput!) {
importWorkflow(input: $input) {
version { id version isValid validationErrors }
errors { path message }
}
}
mutation Activate($workflowId: WorkflowID!, $versionId: WorkflowVersionID) {
setActiveVersion(workflowId: $workflowId, versionId: $versionId) {
workflow { id currentVersionId }
errors { path message }
}
}importWorkflow takes the definition as a YAML or JSON string:
json
{
"input": {
"workflowId": "wfl_...",
"format": "YAML",
"content": "nodes:\n - id: hello\n name: Hello\n type: log\n config:\n message: Hello\nedges: []\n"
}
}createWorkflowVersion does the same with a structured definition object instead of a string. To check a definition without storing it, use validateWorkflow. To get a stored version back as YAML or JSON, use exportWorkflow:
graphql
mutation Validate($content: String!) {
validateWorkflow(input: { content: $content, format: YAML }) {
isValid
errors { path message }
}
}
mutation Export($versionId: WorkflowVersionID!) {
exportWorkflow(input: { versionId: $versionId, format: YAML }) {
content
errors { path message }
}
}Validation checks the graph (unknown node types, arity, dangling edges), each node's config against its type's schema, and every expression.
Running a workflow
Start a run from the console, from a trigger, or with executeWorkflow. The input is a JSON string containing an object. Omit versionId to run the active version.
graphql
mutation Run($input: ExecuteWorkflowInput!) {
executeWorkflow(input: $input) {
execution { id status startedAt }
errors { path message }
}
}json
{
"input": {
"workflowId": "wfl_...",
"input": "{\"jobId\": \"job_...\"}"
}
}The mutation returns as soon as the execution is queued. Follow its progress with getExecution:
graphql
query Execution($id: WorkflowExecutionID!) {
getExecution(id: $id) {
id
status
input
output
durationMs
waitingNodeId
error { code message nodeId }
steps {
nodeId
nodeName
nodeType
wave
status
durationMs
output
error
}
}
}An execution is PENDING, RUNNING, WAITING, COMPLETED, FAILED or CANCELLED (ExecutionStatus). WAITING means it is paused at waitingNodeId, for example on a human task node. input, output and each step's input and output are JSON strings.
List recent runs of a workflow:
graphql
query RecentRuns($workflowId: ID!) {
listExecutions(
first: 20
filter: { workflowId: { eq: $workflowId } }
sort: [{ field: STARTED_AT, direction: DESC }]
) {
edges {
node { id status startedAt durationMs }
}
}
}cancelExecution(id:) stops a running execution. resumeWorkflowExecution(id:) resumes one that is waiting for an external signal.