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. Eachtrigger_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
initiallabel, on the active workflow version. - Have no creator (
created_byis 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.
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 intrigger_intake:
Calendar events behave differently from the others:
calendar.occurrence.started.v1dedupes per occurrence by default. If you leavededupe_keyempty, 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.v1does 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 includesystemin itspermitlist.
Deduplication
Setdedupe_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 emptydedupe_keyputs 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_keywithcooldown: 24h. - Off. Empty or
0scooldown means every event that passesfilteropens a ticket. - New version, new buckets. Activating a new workflow version starts with empty buckets.
group_by and dedupe_key, validation returns a warning and grouping wins. dedupe_key and cooldown are ignored for that entry.
Grouping
Setgroup_by to collapse many related events into one ticket, for example one ticket per OTA deployment instead of one per failed device.
- When an event passes
filter, the platform evaluatesgroup_byand joins the results into a group key. - Events with the same key (for the same workflow version and event) collect in a buffer.
- When the
group_waitwindow closes, the platform opens one ticket for the group. title,tagsandmetadataare evaluated once, against the latest event in the buffer, withgroup.event_count,group.first_fired_atandgroup.keyfilled in.filteris 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 themaintenance-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.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 withtrigger_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:
- Resolves the active version of the chosen workflow, or the project’s
work_orderworkflow. If there is none, no ticket opens. - Checks the dedup bucket, using the same rules as Deduplication.
- Opens a ticket titled
Alarm <alarm definition ID>in the initial label, taggedasset(the alarm’s asset) andalarm_instance(the instance ID).
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:
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-leveldue_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.
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_transitionrule. 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.
- Each event is published once per deadline, even if delivery is retried.
- Patching
due_atmoves both timers. A timer for a deadline that has since moved later is ignored. - If
due_atis 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_failedorcancelledare ignored. - Timers follow the
due_policyof 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
Transition actions
Attach actions to a transition to call other systems when a ticket changes state:on_exitactions run when a ticket leaves thefromlabel through this transition.on_enteractions run when it arrives in thetolabel through this transition.
from → to move.
How actions run
- 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.
- The platform picks up queued actions within a few seconds and runs them in the background. The transition request does not wait for them.
- For one transition,
on_exitactions start beforeon_enteractions. - 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.
- Each action is retried on its own. One failing action does not stop the others.
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 toin_progress, look up the device in an asset system and post to a chat webhook. When it is resolved, tell the device.
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.
Workflow templates
Workflow templates are platform-provided workflows that you install into a project as your own editable copy. The catalog currently includescmms.work_order, a work-order workflow with intake and completion forms.
List templates
data.items[] fields:
The catalog is the same for every project.
Install a template
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
- Workflows — author, validate and activate specs
- Forms — collect structured data on create and transition
- Assignment and notifications — route due and lifecycle events to people
- API reference — ticket, workflow and template routes
- Troubleshooting — when automation does not fire