Skip to main content
A workflow is a graph of typed nodes joined by edges. The Workflow Studio (/workflows in the app) draws it; the backend validates it, compiles it to a Google ADK Workflow and runs it, streaming one event per node. No user code runs inside a workflow: routing rules and {{...}} references are evaluated by the engine itself.

In the Studio

  1. New workflow — start from scratch (a manual trigger) or from the Tool → Router → Agents template, and give it a name.
  2. Drag nodes from the palette, or click one to add it. Link them; on a routing node, pick the route of each edge — an edge without a route is drawn red.
  3. Select a node to configure it. Every text field offers the upstream nodes as chips: one click inserts the right {{...}} reference.
  4. Test opens the run panel: enter a payload (JSON, text or form), Run, and follow each node live — status, duration, route taken, output. Replay steps through the last run on the canvas.
  5. Publish once the validation badge reads Graph valid. This arms the trigger.
Changes are saved automatically. Versions lists every revision and restores any of them.

Import and export a JSON file

Export in the Studio downloads the workflow as a JSON file named after it:
To import one, choose New workflow → Import a JSON file. The file’s name and description fill the empty fields, and Create stays disabled until the file is read. Two shapes are accepted: the file above, or a bare graph {version, nodes, edges} — the body the Definitions API stores. A file without nodes and edges is refused. An imported workflow always starts as a draft. The file carries no trigger URL or HMAC secret: publishing the copy issues new ones. Agent, tool and workflow ids are kept as they are: in a graph from another account, point them at your own — an agent that is not yours stops the run with agent_not_found.

Lifecycle of a workflow

Every save creates a revision (edit, publish, unpublish, restore). Restoring a revision always puts the workflow back to draft. Automatic triggers only ever run the published version, on behalf of the workflow owner.

Nodes

Rule operators for router, condition and a loop’s until: eq, ne, gt, gte, lt, lte (numeric), contains (case-insensitive text), in (value in a list), exists. A rule whose field is empty tests the node’s input.

Structure rules

  • Node id: a letter, then up to 63 letters, digits, _ or -. Ids are unique.
  • At most one trigger per workflow. Without one, the workflow is manual.
  • No cycles: repeat work with a loop, not a back edge.
  • Only router, classifier, condition and try put a route on their outgoing edges, and only a route they declare. condition and try allow one edge per route.
  • An output node ends the flow: it cannot have outgoing edges.
  • A loop or try body is its own graph with exactly one trigger. A try body cannot contain another try or a loop.
  • A subworkflow cannot call its own workflow; chains stop at depth 3.

Final output of a run

The run output is the value of the output node reached, or, without one, the output of the last node reached. A run may end on several nodes — parallel branches from a fan-out, or two output nodes — and the output is then an object keyed by node id, like a merge. Only the nodes actually reached appear: the branch a router, classifier, condition or try did not take contributes nothing. An output node that is reached decides alone: the other branches’ last nodes are then left out of the result. A route that is chosen but has no outgoing edge ends the run normally; the output is then empty.

References between nodes

A string in a node’s config can read the output of an upstream node with {{node_id}} or {{node_id.path.to.value}}. Path segments are object keys or list indexes ({{items.0.name}}).
  • Alone in the field, the reference keeps the value’s type: "{{trigger.amount}}" gives the number 1500, an object stays an object.
  • Inside text, it is converted to text: objects and lists become JSON, a missing value becomes an empty string.
  • The referenced node must be an ancestor of the node that reads it, otherwise the graph is refused (“is not upstream, its output does not exist yet”).
{{node_id}} always works. A path is checked against what the engine guarantees: A path that crosses a plain value at run time (for example {{agent1.amount}} when the agent answered with text) stops the run with template_ref_invalid instead of silently producing nothing. Inside a loop body, the body’s trigger receives {item, index, previous}previous is the previous iteration’s output, or the loop input on the first turn. An until condition reads {{iteration.output...}} and {{iteration.index}} only.

Example

A webhook receives an order; orders above 1,000 are reviewed by an agent.
AGENT_ID is the id of one of your agents. With the payload {"customer": "ACME", "amount": 1500}, order.amount stays the number 1500, the condition takes the true route and the agent receives Review the order from ACME for 1500 EUR.

Triggers

The trigger node’s config.kind decides how a published workflow starts. The payload is what {{trigger...}} reads. Email and file triggers use the account connected under Integrations; without it the trigger stays inactive (integration_missing). The first scan of a watched folder only records the files already there; later scans start one run per new or modified file. A schedule tick is skipped while the previous scheduled run is still running. An agent_tool workflow becomes a tool named workflow:<tool_name> that agents can call. The call waits for the run (120 s at most) and returns {run_id, output}, or an error. Workflows that call each other through agent_tool or workflow_done stop at a chain of 5, and never loop back to a workflow already in the chain.

Webhook and form URLs

Publishing a webhook or form workflow issues a secret URL, shown in the Studio and by GET /api/workflows/{workflow_id}/triggers:
  • webhook: POST /api/hooks/workflows/{token} — answers 202 {"run_id": "..."};
  • form: /forms/{token} in the app, submitted to POST /api/hooks/forms/{token}.
POST /api/workflows/{workflow_id}/triggers/rotate issues a new token (the old URL stops working). With hmac: true, the HMAC secret is shown only when it is generated or rotated; each call must then carry X-Apowerb-Signature: sha256=<hex>, the HMAC-SHA256 of the raw body with that secret.
An unknown token or an unpublished workflow answers 404, a bad signature 401, a body over 256 KiB 413, and more than 60 calls a minute per workflow 429. A public form accepts 10 submissions a minute per visitor; fields that the form does not declare are dropped.

Running and following a run

POST /api/workflows/defs/{workflow_id}/run with {"payload": ...} runs the saved version and streams Server-Sent Events: Nodes run one at a time, in dependency order: branches after a fan-out are correct but serialized. Each node output is capped at 1 MiB (node_output_too_large); HTTP responses too. Stop a run with POST /api/workflows/{wid}/cancel, wid being the run id carried by run_started: the current node finishes, no further node starts. Runs are kept with a status — running, success, error, cancelled — and listed newest first by GET /api/workflows/runs; each entry carries its trigger, the cause of its failure and input_available, which tells whether its input is still on disk and the run can therefore be replayed. GET /api/workflows/runs/{run_id} returns one run. Per-node outputs are only in the event stream, not stored. POST /api/workflows/runs/{run_id}/replay starts a new run from the stored input; a successful run needs ?force=true, since its side effects already happened.

Error codes

Errors carry a stable code and params (never the raw exception). The most common:

Security notes

  • The http node refuses private and internal addresses and sets Host itself. A 4xx or 5xx answer does not fail the node: test {{node.status}} downstream. Authorization, Proxy-Authorization, Cookie and X-API-Key headers cannot be written in a graph, since anyone who reads or exports it would see them; authenticated HTTP calls are not available yet.
  • Notifications are limited to 30 sends an hour per owner, all channels together. app goes to the workflow owner, teams to the owner’s Teams webhook.
  • A subworkflow or workflow_done source must belong to the same owner; other owners’ workflows answer as not found.

Definitions API

Runs and triggers

All endpoints require authentication and only see the caller’s workflows.