trigger_intake, due_policy and transition actions in depth, see Automation. For every route, see API reference.
Spec structure
A spec missing
name or initial cannot be parsed at all — the validate endpoint returns 400, and create or patch fails. Every other problem shows up as a validation error (see Graph validation). Run the spec through the validate endpoint before you save it.
Labels
Each label is a state a ticket can be in. Declare labels inlabels.extra:
The label’s
class becomes the ticket’s system_class when the ticket enters that label. Use system_class to filter and report across workflows that use different label keys.
Imported labels
labels.import lists keys from the Project’s shared state-label catalog. Each imported key must already exist in that catalog, or creating the workflow fails. Imported labels take their class and color from the catalog and are never terminal, so they do not count toward the terminal-state rules below. There is no API to manage the catalog today — declare your labels in labels.extra.
Initial state
initial names the label a new ticket starts in. It must be a declared label key. If the workflow has trigger_intake entries, the initial label must be in labels.extra with class: open.
Transitions
Each entry intransitions is one allowed edge:
Each
from → to pair can appear only once. To move a ticket, call POST …/tickets/{ticket_id}/transition with {"to_label": "<key>"}. A target with no edge from the ticket’s current label returns 400 no such transition.
Permits
A transition succeeds when any entry inpermit matches the caller.
If no entry matches, the transition returns
403 transition permit denied.
An empty permit list fails validation with at least one permit clause is required. Use [system] for edges that only automation should take.
What happens on a transition
The platform checks a transition in this order and stops at the first failure. Everything runs in one transaction, so a failure leaves the ticket unchanged.
Because the form is applied in step 3, guards and tag checks see the metadata and tags the form just wrote.
Guards with CEL
Guards are CEL expressions that must evaluate tobool. Validation compiles every guard and rejects non-boolean expressions. At transition time each guard has a 50 ms evaluation budget.
event, queue and http are declared but empty when a guard runs. CEL string extensions are available (lowerAscii(), split(), replace() and so on).
Reading a key that does not exist is an evaluation error, which returns 400 guard expression could not be evaluated. Guard optional keys with has(). A guard that returns false returns 400 guard failed.
Examples:
Validation rules
validation adds requirements on top of permits and guards.
On create
require_form and optional_form cannot both be set.
Before a transition
Eachbefore_transition entry applies to one declared from → to edge:
Omit
version to use the latest version of a catalog form. Inline forms in forms.extra take precedence over catalog forms of the same name. See Forms.
Transition actions
on_exit and on_enter list actions to run when a transition happens. Two kinds are supported:
Actions are queued in the same transaction as the state change and run asynchronously after it commits — the transition response does not wait for them, and an action failure does not undo the transition. For each transition,
on_exit actions run before on_enter actions.
ticket.http URLs must use http or https and must resolve to a public address when you validate the workflow. Private and link-local addresses are rejected. See Automation for retries, bindings and RPC details.
Work-order workflows
Setkind: work_order to mark a workflow as a CMMS work-order workflow. CMMS features look for workflows of this kind — for example, an alarm that creates work orders without a named workflow uses the Project’s active work_order workflow. name is only a label. A workflow with no kind whose name is work_order is also treated as a work-order workflow.
The cmms.work_order workflow template installs a ready-made work-order workflow you can edit.
Sub-tickets and terminal states
A parent ticket links a child with a tag of kindsubticket whose value is the child ticket’s UUID. While any linked child has system_class open or in_progress, the parent cannot move to a terminal label (terminal: true, or class terminal_success, terminal_failed or cancelled). The transition returns:
The Console links sub-tickets by setting
metadata.parent_ticket_id on the child. This check does not read that field. Only subticket tags on the parent block terminal transitions.Graph validation
Validation runs these checks on every spec. Errors block activation; warnings do not.
A label counts as terminal when it has
terminal: true or class terminal_success, terminal_failed or cancelled.
Validate a spec
POST /projects/{project_id}/ticket-workflows/validate checks a spec without saving it. Send JSON with the spec under workflow, or a YAML document with a top-level workflow: key and Content-Type: application/yaml.
200 when the spec parses. Check valid:
A body that cannot be parsed, or a spec without
name or initial, returns 400.
Full example
Amaintenance-request workflow: tickets need a tagged device, someone must be assigned before work starts, resolving requires a resolution form, and resolving posts a message to a webhook.
Both files below are complete request bodies for validate, create and patch.
Versioning and activation
A workflow definition (workflow_def_id) holds a list of immutable versions. Routes that take {workflow_id} expect a version id.
1
Create a version
POST /projects/{project_id}/ticket-workflows with the spec. The response data.item includes id (version id), workflow_def_id, version, is_active, spec and validation_report. A new version is never active.2
Check the validation report
The version is saved even when it has errors.
validation_report.ok is false and validation_report.errors lists them as "<path>: <message>" strings.3
Activate the version
POST /projects/{project_id}/ticket-workflows/{workflow_id}/activate. This deactivates any other version of the same workflow. A version with validation errors returns 400 workflow validation failed.4
Create tickets
Pass
workflow_def_id to POST …/tickets. New tickets always use the definition’s active version.
Existing tickets stay on the version they were created with. Their transitions, permits, guards and actions keep following that version after you activate a newer one.
State labels endpoint
GET /projects/{project_id}/ticket-workflows/{workflow_id}/state-labels lists the labels of one workflow version, with the ids tickets reference in state_label_id.
An unknown version id returns
404 workflow not found.