REST API Workflow - edmozley/freeitsm GitHub Wiki

โš™๏ธ 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.