Skip to content

REST API Workflow

Ed Mozley edited this page Jul 4, 2026 · 1 revision

βš™οΈ REST API: Workflow

The complete usage guide for the Workflow module of the REST API β€” automation rules, test-firing, the engine's run history, and the machine-readable trigger/action catalogues. Mirrors the interactive documentation at System β†’ API β†’ Documentation (with its live "Try it" tester).

Note

New to the API? The basics table on REST API: Tickets covers base URL, authentication, the response envelope, error codes, rate limits and PATCH semantics β€” identical across all modules.

What's distinctive about Workflow:

πŸ“š The engine's catalogues are the contract trigger_event, condition operators and action types are validated at write time against the engine's own catalogues β€” nothing unexecutable can be stored. Unknown operators are a 422 (the UI stores them and the condition then silently fails at run time).
πŸ”₯ Test-fire over the API POST /workflows/{id}/fire is the editor's "Test fire" button: synthetic payload in, real actions out, the full per-step log back β€” and production run stats (run_count, last_run) stay untouched.
🧾 Run history that survives deletion Executions are the engine's audit trail. Deleting a workflow keeps its runs β€” they're detached (workflow.id becomes null) but stay attributable via a name snapshot taken at run time. ?orphaned=true finds them.
🐞 A debugging surface the UI only hints at A single execution read returns the complete step log: every condition with the actual value it saw and whether it passed, every action with its result or error. The editor's sidebar shows only the last 20 runs' summaries.
⚠️ Powerful grants Workflow actions run with engine privileges β€” they create tickets, send email from the ticket's mailbox, assign analysts. Treat workflows.create / workflows.update / workflows.fire as admin-level permissions and scope them to trusted keys.

πŸš€ Quick start β€” build a rule, test it, watch it run

B="https://your-server/api/v1"; K="Authorization: Bearer fitsm_…"

# What can I trigger on, and what can I do? (no guessing β€” ask the engine)
curl -s "$B/workflow-triggers" -H "$K"
curl -s "$B/workflow-actions"  -H "$K"

# Create a rule: note every urgent ticket
WF=$(curl -s -X POST "$B/workflows" -H "$K" -H "Content-Type: application/json" -d '{
  "name": "Flag urgent tickets",
  "trigger_event": "ticket.created",
  "conditions": [ { "field": "ticket.subject", "op": "contains", "value": "urgent" } ],
  "actions": [ { "type": "add_ticket_note",
                 "args": { "ticket_id": "{{ticket.id}}", "note": "Auto-flagged as urgent." } } ]
}' | jq '.data'); ID=$(echo "$WF" | jq -r '.id')

# Test-fire it with a synthetic payload β€” real actions, stats untouched
curl -s -X POST "$B/workflows/$ID/fire" -H "$K" -H "Content-Type: application/json" \
  -d '{ "payload": { "ticket": { "id": 123, "subject": "urgent: server down" } } }'

# Watch it run for real
curl -s "$B/workflows/$ID/executions" -H "$K"

βš™οΈ Workflows

🟒 GET /workflows Β  πŸ”‘ workflows.read

All rules, most recently updated first, with run stats and conditions_count / actions_count (the full rule bodies come on the single read). Filters: trigger_event, is_active=true|false, q (names + descriptions). Install-wide β€” workflows have no company scoping, like the module.

πŸ”΅ POST /workflows Β  πŸ”‘ workflows.create

name and trigger_event required. conditions is an array of {field, op, value} (AND semantics β€” empty means always fire); actions is an ordered array of {type, args}. Zero actions is allowed (draft-friendly, matching the editor). Action args support {{dot.path}} template variables resolved from the event payload β€” "ticket_id": "{{ticket.id}}" is the idiom that binds an action to the triggering ticket.

Everything is validated against the engine's catalogues: unknown triggers, operators and action types are a 422 pointing you at GET /workflow-triggers / GET /workflow-actions.

🟒 GET /workflows/{id} Β  πŸ”‘ workflows.read

The full rule β€” decoded conditions and actions, creator, run stats:

{ "data": { "id": 7, "name": "Flag urgent tickets", "trigger_event": "ticket.created",
    "is_active": true, "created_by": { "id": 1, "name": "Administrator" },
    "conditions": [ { "field": "ticket.subject", "op": "contains", "value": "urgent" } ],
    "actions": [ { "type": "add_ticket_note", "args": { "ticket_id": "{{ticket.id}}", "note": "…" } } ],
    "last_run": { "at": "2026-07-04T08:42:44Z", "status": "success" }, "run_count": 12 } }

🟠 PATCH /workflows/{id} Β  πŸ”‘ workflows.update

Partial update β€” sent fields are validated like create. {"is_active": false} pauses a rule without losing it; the engine only picks up active workflows on dispatch.

πŸ”΄ DELETE /workflows/{id} Β  πŸ”‘ workflows.delete

Hard delete. Execution history survives β€” runs are detached (workflow.id β†’ null) but keep the workflow's name via the snapshot, so the audit trail stays readable forever.

πŸ”΅ POST /workflows/{id}/fire Β  πŸ”‘ workflows.fire

Runs the workflow immediately with a synthetic payload β€” the editor's Test fire. The payload is whatever your conditions and {{templates}} read ({ "payload": { "ticket": { "id": 123, "subject": "…" } } }). Actions really execute (a create_ticket action really creates a ticket), but run_count / last_run are untouched so test runs don't pollute production stats. Returns the execution result inline:

{ "data": { "execution_id": 17, "status": "success",
    "step_log": [
      { "kind": "condition", "field": "ticket.subject", "op": "contains",
        "value": "urgent", "actual": "urgent: server down", "passed": true },
      { "kind": "action", "type": "add_ticket_note", "status": "success",
        "result": { "ticket_id": 123, "note_length": 24 } } ] } }

A run whose conditions don't match returns "status": "skipped" with the failing condition's actual value in the log β€” the fastest way to debug a rule that "never fires".


🧾 Executions

Read-only β€” only the engine writes these. Statuses: running, success, failed, skipped (conditions didn't match), aborted (loop protection stepped in).

🟒 GET /workflows/{id}/executions Β  πŸ”‘ workflow_executions.read

One workflow's runs, newest first, paginated. Filters: status, trigger_event, started_since (ISO 8601).

🟒 GET /workflow-executions Β  πŸ”‘ workflow_executions.read

The install-wide run history β€” including orphaned runs whose workflow has since been deleted (?orphaned=true), which the module's UI never surfaces. Same filters plus workflow_id.

🟒 GET /workflow-executions/{id} Β  πŸ”‘ workflow_executions.read

Full detail: the trigger_payload snapshot the engine saw, and the complete step_log β€” every condition evaluated (with the actual value read from the payload) and every action's result or error message.


πŸ“š Catalogues (reference data)

🟒 GET /workflow-triggers Β  πŸ”‘ reference.read

Every trigger event with its condition fields β€” each field's dotted payload path, its type (lookup / numeric / text) and the operators valid for it. Seven triggers today: ticket.created, ticket.status_changed, ticket.priority_changed, ticket.assigned, form.submitted, task.completed, change.approved.

🟒 GET /workflow-actions Β  πŸ”‘ reference.read

Every action type with its full args spec β€” labels, required flags, defaults, lookup sources and whether an arg supports {{template}} variables. Eight actions today: log_message, set_ticket_status, set_ticket_priority, assign_ticket, add_ticket_note, send_email, create_task, create_ticket.


πŸ“Œ Notes & gotchas

  • The engine runs synchronously β€” a fire request returns when the actions finish. Keep test payloads realistic but small.
  • Loop protection is request-scoped: a workflow can't re-enter itself, chains cap at depth 10, and 100 runs per request β€” blocked runs are recorded as aborted executions, visible in the history.
  • The AI compose endpoints stay UI-only (Workflow β†’ editor β†’ AI compose) β€” like every AI feature in the API so far.
  • The fire permission is separate from update precisely so you can hand a monitoring script the ability to trigger a runbook workflow without letting it rewrite the rule.

See also: REST API (how it all works) Β· Workflows (the module itself) Β· the other module guides linked from the sidebar.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally