-
Notifications
You must be signed in to change notification settings - Fork 15
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. |
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. |
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"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.
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.
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 } }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.
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.
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".
Read-only β only the engine writes these. Statuses: running, success, failed, skipped (conditions didn't match), aborted (loop protection stepped in).
One workflow's runs, newest first, paginated. Filters: status, trigger_event, started_since (ISO 8601).
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.
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.
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.
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.
- 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
abortedexecutions, 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
firepermission is separate fromupdateprecisely 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 β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- Theming & Dark Mode
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
- MobileβFriendly
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- Problem Management
- Tasks
- Assets
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
- Forms
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)