#Workflows
A workflow is an ordered set of jobs; each job runs a sequence of actions (HTTP calls, entity reads/writes, file processing, LLM inference, …). Workflows react to triggers — an entity event, a webhook, a cron tick, or a direct dispatch. See Core Concepts for the mental model.
A workflow lives in two layers:
- Definition — the design-time spec: the jobs, actions, and triggers. Authored
under
/definition/*and shareable to other tenants. - Activation — a tenant turning a definition on: its env, trigger secrets,
bound policy, and the runs it produces. Driven under
/workflows/*.
Authenticate with a Personal Access Token (HTTP Basic auth) — enter it once in the Authorize bar at the top of this page and every Try it below uses it. Each example body is pre-filled; the console targets staging by default. Full request/response schemas are in the linked API reference.
#Build a workflow
The flow is: define the workflow → activate it → trigger a run → watch it.
#1. Define the workflow
A workflow binds triggers to job steps. Author each step's logic inline —
embed its actions directly in the step. Inline is the recommended default: one
definition, one version, nothing else to publish or keep in sync. Each action has a
stepId, a name (from the action catalog), and a config; see
jobs & actions for the conditional fields (if, errorWhen,
errorPolicy, onError). dependencies order steps into a DAG.
POST/definition/workflowsCreate a workflow (inline job)API docs ↗Try it
A definition is draft until the first activation flips it to active. The write is
validated against the workflow JSON schema; per-action config is validated again at
run time, after smart values resolve. Pass source_yaml instead of definition_json
to author in YAML.
Reuse the same actions across workflows? Publish them as a named job definition (
POST /definition/jobswith a(name, version)) and reference it from a step:{ "stepId": "remind", "name": "notify-overdue-task", "version": "1.0.0" }. Reach for a named job only when you genuinely need to share one action sequence across several workflows or pin its version independently — otherwise keep it inline.
#2. Activate it
Activation is the consumer tenant turning the definition on: it merges env, reconciles trigger rows, binds an access policy the run acts under, and (for cron) provisions schedules. It is idempotent — re-activating with the same body produces the same state.
POST/workflows/{id}/activateActivate a workflow in your tenantAPI docs ↗Try it
The response returns each trigger with its webhook_url (what external callers
POST to) and config_status. Cron and webhook-secret details are under
Triggers below.
#3. Trigger a run
Four sources fire a run: an entity event, a webhook POST, a cron tick,
or a direct dispatch. Direct dispatch is the ad-hoc path — pass the trigger's
definition id (e.g. "manual"), not the row UUID:
POST/workflows/wrDispatch a runAPI docs ↗Try it
Returns 202 with the workflow_run.id. Webhook callers instead POST to the
webhook_url from activation; cron and event triggers fire automatically.
#4. Monitor the run
List runs (filter by workflow_id, status_in, entity_id, …), then drill into
one for its per-job and per-action errors.
GET/workflows/runs/workflowsList recent runsAPI docs ↗Try it
GET/workflows/runs/workflows/{id}Inspect a run (job + action detail)API docs ↗Try it
The workflow-run error is null unless an orchestrator-level error fired;
per-step failures live on job_runs[].error (and action_runs[].error). Statuses
are pending → dispatched → running → success | error | cancelled | skipped | timeout.
#Jobs & actions
Inline and named jobs run identically — both resolve to the same shape before
execution. Prefer inline (actions directly on the workflow step): it keeps a
workflow self-contained, with one definition and one version to manage. Reach for a
named job definition (POST /definition/jobs, referenced by (name, version))
only when you need to share one action sequence across several workflows or pin its
version independently.
#Step-level control (per job step)
A workflow job step accepts a condition gate and error/rerun policies:
| Field | Values | Meaning |
|---|---|---|
dependencies |
string[] |
Step ids that must finish first (defines the DAG). |
condition |
bare JSONata | Evaluated against context_data; if false, see conditionPolicy. |
conditionPolicy |
continue | abort | fail |
On false condition: continue skips the step, abort ends the workflow as success, fail ends it as error. Default fail. |
errorPolicy |
continue | abort | fail |
On step error: same three outcomes. Default fail. |
rerunPolicy |
continue | allow | abort |
On a parent retry: reuse prior output, re-run, or end. Default allow. |
#Action-level control (per action, inside a job)
Each action carries optional conditional and recovery fields. This is what lets a single job branch and self-heal without a separate step:
| Field | Values | Meaning |
|---|---|---|
if |
bare JSONata | Evaluated before the action. A falsy result skips the action and its onSuccess and moves on. Mirrors a bridge step's if gate. |
errorWhen |
bare JSONata | Evaluated after the action on its result; a truthy value marks the action failed (for http, defaults to result.status >= 400). |
onSuccess |
action sub-sequence | Actions run after this action succeeds, before the parent sequence continues. |
onError |
action sub-sequence | Recovery actions run after this action fails, before errorPolicy is applied. |
errorPolicy |
fail | continue |
After the action fails (and its onError runs): fail (default) ends the job with the error; continue consumes the error and resumes the parent sequence at the next action. |
Action
errorPolicy(fail/continue) is distinct from the job-steperrorPolicy(continue/abort/fail). The action field governs flow inside a job; the step field governs the workflow outcome when the whole step errors. In the workflow above, thenotifyaction setserrorPolicy: "continue"and anonErrorthat flags the task — so a failed notification is recorded and the job still completes.
Authoring fields like config, body, if, and errorWhen are smart values:
literals, {{ }} JSONata expressions (a whole-string {{ … }} stringifies), or
$-directives for raw values. The full model — bindings (input, outputs,
context_data, env, secrets), directives, and failure modes — is in
Smart Values.
To reshape a row set inline — filter, map, join, sort, or dedupe a prior step's
output before writing it — wrap it in a { $rows, $pipe } node in any action
config / input / output. The $pipe vocabulary is the same one apps use; see
Shaping a row set.
#Action catalog
The orchestration layer ships these action types. Every config is a smart value;
see the API reference for each action's exact config schema.
| Action | Kind | Purpose |
|---|---|---|
http |
external | Synchronous HTTP request |
httpCallback |
external | Async HTTP request; resumes on callback |
llmCallback |
external | Async LLM inference (Anthropic / OpenAI) |
awaitInput |
workflow | Pause until an operator submits structured input |
invokeWorkflow |
workflow | Spawn a child workflow run (fire-and-forget) |
queryEntity |
entity | Read one or many entities |
upsertEntity |
entity | Create / update entities (single or bulk) |
removeEntities |
entity | Delete entities by id |
upsertRelationship |
entity | Create / update relationship instances |
removeRelationships |
entity | Delete relationship instances by id |
syncRelationships |
entity | Set-replace one entity's links under a rule |
uploadArtifact / downloadArtifact / listEntityArtifacts / createArtifactDownloadURL |
artifact | Store, fetch, list, and sign artifact access |
zipCompress / zipDecompress / archiveDecompress |
file | Compress and extract archives |
splitPdf / mergePdf / chunkPdf |
file | Split, concatenate, and chunk PDFs |
csvFromZipStream / transformCsvStream / mergeCsvStreams |
csv | CSV extract, per-row transform, and join |
importEntitiesFromCsv / exportEntitiesToCsv |
csv | Bulk import/export entities via CSV |
#Triggers
A workflow's definition_json.triggers[] declares when it runs. Each entry
has an id plus exactly one of source (event), cron, or webhook; a webhook
with no verification is the "API-only / manual" form. Triggers are reconciled on
every activate. Optional condition (a bare JSONata gate), contextData (shapes
context_data.input), and idempotencyKey apply to any type.
#Event trigger
Fires when an entity is created, updated, or deleted — optionally narrowed to one entity, one relationship rule, or matching attribute values.
{
"id": "on-task-updated",
"source": {
"entityTypeId": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"eventAction": "updated", // created | updated | deleted
"relationshipRuleId": "<uuid?>", // optional — only this rule
"entityId": "<uuid?>", // optional — only this entity
"attributeFilters": [ { "operator": "eq", "path": ["status"], "value": "open" } ]
}
}
#Cron trigger
A standard five-field cron expression. Timezone comes from the activation's
env.TZ (default UTC).
{ "id": "nightly", "cron": "0 0 * * *" }
#Webhook trigger
External callers POST to the trigger's webhook_url (returned at activation).
verification checks request authenticity; challenge answers a
validation handshake (e.g. Microsoft Graph) without dispatching a run.
{
"id": "inbound",
"webhook": {
"methods": ["POST"],
"verification": {
"location": "header", // header | body | query
"key": "x-hub-signature-256",
"method": "hmac-sha256" // exact | hmac-sha256 | hmac-sha1
},
"challenge": { "enabled": true, "queryParam": "validationToken", "responseType": "text/plain" }
}
}
A webhook secret exists only when verification is declared. Mint it (returned
once as whsec_…; rotates any prior secret); the gateway then verifies every
POST /workflows/hooks/:triggerId and rejects a bad signature with 401.
POST/workflows/{id}/triggers/{triggerId}/secretMint a webhook secret (returned once)API docs ↗Try it
After activation, toggle a trigger without re-activating via
PUT /workflows/{id}/triggers/{triggerId}/{enable,disable}.
#Policy & secrets
- Policy — an activation binds an access policy that authorizes everything the run does. See Provisioning.
- Secrets — wire scoped secret refs into
config.env; never put a ref in the definition. See Secrets.
#Inline jobs without a workflow
Apps (bridge) can run the same action sequences inline — synchronously, with no workflow run — for request handling. See Apps (Bridge).
#Share a workflow with another tenant
Publishing a definition for another tenant to install is its own topic — see Shares & Grants.
#API reference
Every workflow, job, trigger, and run operation — with full request/response schemas and an interactive console — is in the API reference.