Skip to main content
Workflow automation lets the platform act on tickets without anyone clicking through the Console. You declare all of it in the workflow spec, alongside labels and transitions: Automation only runs for the active workflow version. After you create or edit a workflow, activate the new version. See Workflows for the authoring loop.

Workflows

Labels, transitions, permits, guards, validation and activation.

Assignment and notifications

Subscribe to Ticket Due Soon, Ticket Due and other ticket events.

API reference

Every ticketing route, request body and response field.

Troubleshooting

Intake that never fires, timers that do not move, failed actions.
Examples on this page are YAML for readability. The create and patch workflow endpoints take the same spec as JSON under a workflow key: {"workflow": {...}}. POST …/ticket-workflows/validate accepts either that JSON body or the raw YAML.

Trigger intake

Trigger intake opens a ticket automatically when a matching platform event arrives in the project. Each trigger_intake entry names one event and uses CEL expressions to decide whether to open a ticket and what to put in it. Tickets opened this way:
  • Start in the workflow’s initial label, on the active workflow version.
  • Have no creator (created_by is empty) and no assignee. Assign them afterwards, or let a person pick them up.
  • Have no due_at. Patch one in if the ticket needs a deadline (see Due dates and due policy).

Spec fields

Durations use the units ms, s, m and h. There is no day unit, so write 24h or 720h.
A workflow with any trigger_intake entry must declare its initial label under labels.extra with class: open. Validation rejects the workflow otherwise.

CEL variables

The standard CEL string extensions are available, for example lowerAscii() and split(). Some payload fields, such as fleet_id and device_name, are left out when empty. Reading a missing field is an error, and an error means no ticket. Guard optional fields with has(), for example has(event.payload.fleet_id) ? event.payload.fleet_id : "none".

Supported events

Validation accepts only these events in trigger_intake:
The two telemetry.* events pass validation, but the platform does not currently publish them. An intake bound to them never opens a ticket. For value thresholds on device data, use an alarm with Work order on activation instead.
Calendar events behave differently from the others:
  • calendar.occurrence.started.v1 dedupes per occurrence by default. If you leave dedupe_key empty, the platform uses the schedule and occurrence time as the key, so a redelivered event cannot open a second ticket for the same occurrence. The new ticket is linked back to the calendar event.
  • calendar.occurrence.ended.v1 does not open a ticket. It finds the ticket opened for that occurrence and moves it along the first transition declared from its current label. The move runs as the system actor, so that transition must include system in its permit list.

Deduplication

Set dedupe_key and a positive cooldown to stop bursts of events from opening a ticket each.
  • Bucket. A bucket is the workflow version, plus the event, plus the evaluated dedupe_key. Two workflows listening to the same event dedupe independently. An empty dedupe_key puts every event for that intake in one shared bucket.
  • Window. The first event in a bucket opens a ticket and starts the cooldown. Later events in the same bucket are dropped until the cooldown ends.
  • Time-based only. Closing the ticket does not reset the window, and an open ticket does not extend it. There is no “one open ticket per device” mode. For “at most one ticket per device per day”, use a per-device dedupe_key with cooldown: 24h.
  • Off. Empty or 0s cooldown means every event that passes filter opens a ticket.
  • New version, new buckets. Activating a new workflow version starts with empty buckets.
If an entry sets both group_by and dedupe_key, validation returns a warning and grouping wins. dedupe_key and cooldown are ignored for that entry.

Grouping

Set group_by to collapse many related events into one ticket, for example one ticket per OTA deployment instead of one per failed device.
  1. When an event passes filter, the platform evaluates group_by and joins the results into a group key.
  2. Events with the same key (for the same workflow version and event) collect in a buffer.
  3. When the group_wait window closes, the platform opens one ticket for the group.
  4. title, tags and metadata are evaluated once, against the latest event in the buffer, with group.event_count, group.first_fired_at and group.key filled in. filter is checked again against that latest event.
If opening the group ticket fails, that batch of events is discarded. The next matching event starts a new group.

Example: device updates with dedup

One ticket per device per 30 minutes, however often the device changes. This is the maintenance-request workflow used across these pages.

Example: grouped OTA failures

One ticket per failing deployment. The ticket opens after five quiet minutes, and the same deployment cannot open another ticket for an hour.
The device tag and last_failure_reason come from the latest failure in the group. Add this block to a workflow like maintenance-request above, then validate and activate it.

Alarm-triggered tickets

An alarm can open a work-order ticket each time one of its instances activates. You configure this on the alarm definition, not with trigger_intake. In the Console, open the alarm definition and turn on Work order on activation. Then choose the Workflow and, optionally, a Dedup key. Over the API, set these fields on the alarm definition: When an instance activates, the platform:
  1. Resolves the active version of the chosen workflow, or the project’s work_order workflow. If there is none, no ticket opens.
  2. Checks the dedup bucket, using the same rules as Deduplication.
  3. Opens a ticket titled Alarm <alarm definition ID> in the initial label, tagged asset (the alarm’s asset) and alarm_instance (the instance ID).
The alarm instance must have an asset. Activations without one do not open a ticket. wo_dedup_key_cel is evaluated against event.payload (the activation) and project.id. Useful payload fields are asset_id, alarm_def_id, alarm_instance_id, priority and trigger_value. For example, one work order per asset and alarm definition per window:
Only event, project and group exist in this expression. Expressions that reference other variables, such as labels or alarm, fail and no ticket opens.
Dedup suppresses repeat tickets within the window. It does not attach the new activation to the existing ticket.

Due dates and due policy

A ticket’s deadline is its top-level due_at field (RFC3339). Set it in the create request or with PATCH …/tickets/{ticket_id}. A workflow’s due_policy then decides what happens as the deadline approaches and passes.
The Console’s Due date field writes metadata.due_at, not the top-level due_at. It does not start due-policy timers. To get timers, set due_at through the API.

due_policy fields

Validation rejects on_breach.to unless every declared transition into that label includes system in its permit list, and at least one such transition exists.

What happens at each time

The breach transition runs only if the workflow declares a transition from the ticket’s current label to on_breach.to with system in permit. It runs as the system actor with reason due policy breach, and it still has to pass:
  • The transition’s guards.
  • Any before_transition rule. A transition that requires a form is skipped, because a timer cannot fill one in.
  • The sub-ticket check. A ticket with open sub-tickets cannot move to a terminal label.
If any check fails, the ticket stays where it is. Ticket Due is still published. Timer rules:
  • Each event is published once per deadline, even if delivery is retried.
  • Patching due_at moves both timers. A timer for a deadline that has since moved later is ignored.
  • If due_at is in the past or less than 10 seconds away when you set it, Ticket Due fires about 10 seconds later.
  • Moving a ticket to a terminal label cancels its timers. Timers that still fire for a ticket in terminal_success, terminal_failed or cancelled are ignored.
  • Timers follow the due_policy of the workflow version the ticket is on, not the newest version.

Notifications

Ticket Due Soon and Ticket Due are notification event types with the recipient roles Assignee and Creator. Subscribe to them like any other ticket event. See Assignment and notifications.

Example: maintenance SLA with escalation

Create a ticket with a deadline:

Transition actions

Attach actions to a transition to call other systems when a ticket changes state:
  • on_exit actions run when a ticket leaves the from label through this transition.
  • on_enter actions run when it arrives in the to label through this transition.
Both lists live on the transition, so they run only for that specific from → to move.

How actions run

  1. The transition commits. The actions are queued as part of the same change, so a transition never commits without its actions, or the other way round.
  2. The platform picks up queued actions within a few seconds and runs them in the background. The transition request does not wait for them.
  3. For one transition, on_exit actions start before on_enter actions.
  4. A failed action is retried after 15 seconds, up to 3 attempts in total. After the third failure it is abandoned. The ticket stays in its new label either way.
  5. Each action is retried on its own. One failing action does not stop the others.
Version pinning. Actions come from the workflow version the ticket is on, and are captured when the transition commits. Activating a new version does not change actions for tickets already on the old version. Only tickets created after activation use the new version.

ticket.http

Calls an HTTP endpoint. A response with status 200–299 is a success; anything else counts as a failure and is retried. url, headers and body are sent exactly as written. They are not templates and are not evaluated per ticket, so you cannot insert the ticket ID or metadata into them. When you need per-ticket values, use ticket.rpc, whose fields are CEL. Bindings copy parts of a successful response into the ticket’s metadata: If a from path is missing from the response, the action fails and is retried.

ticket.rpc

Queues a call to a typed method on an edge device. It uses Device RPC queued invoke, so the call waits for the device if it is offline. See Queued invoke. CEL expressions are evaluated when the action runs, against the ticket as it is at that moment: The action succeeds once the call is queued. Delivery to the device and the method’s result are tracked by Device RPC, not by the ticket. Make the device method safe to run twice, because a retried action can queue the call again.

Example: notify and enrich

When a request moves to in_progress, look up the device in an asset system and post to a chat webhook. When it is resolved, tell the device.
Replace the example.com hosts with your own. Validation resolves each host, so placeholder hosts fail. ticket.tags.device[0] reads the first device tag, which the dedup example above adds at intake. If a ticket has no device tag, the expression fails and the action is abandoned after three attempts.
Header values are stored in the workflow spec as plain text and are visible to anyone who can read the workflow. Prefer endpoints that authenticate with a scoped, revocable key.

Workflow templates

Workflow templates are platform-provided workflows that you install into a project as your own editable copy. The catalog currently includes cmms.work_order, a work-order workflow with intake and completion forms.

List templates

Permission: project read access
Response data.items[] fields: The catalog is the same for every project.

Install a template

Permission: project manage assets
The cmms.work_order workflow is named work_order_v1 with kind: work_order, so alarms with Work order on activation and no explicit workflow use it. It includes due_policy.lead_time: 24h and no on_breach. To add triggers, escalation or actions, edit your copy like any workflow, then validate and activate the new version.

Next steps