Ticket-automation rules — author, dry-run, and roll out
Ticket automation turns the triage questions every agent used to answer by hand — is this billing, is it stale, who owns it — into a rule you author once and the queue applies forever. This guide walks one rule from the grammar catalog all the way through a dry-run to a rollout checklist, and along the way tells you how the builder stays in sync with the backend’s vocabulary instead of hard-coding it. Everything here is tenant-owned configuration. Authoring, dry-running, and managing rules requires an owner or admin role; reading the grammar catalog is open to any authenticated operator. Nothing a rule does ever reaches the customer’s phone — thenotify action is restricted to Slack, in-app, and webhook destinations by the schema itself, so automation can never become an outbound-messaging path.
1. Mental model — one trigger, some conditions, a few actions
A rule subscribes to exactly one trigger, filters with conditions, and runs a handful of actions when they hold:
Two knobs control how a rule composes with its neighbors.
match is all (AND) or any (OR) over the condition set. priority orders evaluation — lower runs first — and a matching rule with stop_processing: true ends that event’s pass, so a decisive first-match rule is final.
The product page explains the per-action behaviour (assign skips an agent already at their channel cap, tags land with canonical casing, priority changes re-scale SLA due windows, escalation combines priority + assignee in one action). The rest of this guide is the doing.
2. Where the builder lives
Inbox → Settings → Ticket automation, RoleGuard’d to owner/admin to match the backend gate on the test and rules endpoints. The page has two halves — the rule editor on top (trigger, conditions, actions) and a dry-run panel below (sample ticket + event) — and is deliberately catalog-driven: the trigger selector, the per-field operator options, and the value dropdowns all render from the backend’s catalog, not from strings bundled into the frontend.3. The API surface the builder consumes
Two endpoints power the builder, and a rules CRUD set persists what passes the test. The full request/response schemas live in the Inbox API reference; what a guide needs is the contract:
The catalog is the answer to the enum-drift problem, and it is the pattern this guide recommends you copy: the page requests the grammar once, renders form options from it, and therefore cannot hard-code a stale list the backend later changes.
3.1 Persisted-rule execution, in milestones
The persisted side is deliberately split into milestones, and the workspace page header names the split: the first milestone owns the builder + preview surface, the follow-up owns durable storage and the execution path. Respect that ordering when you roll rules out — the dry-run contract is stable, so author and validate first, and treat persistence as the thing you enable only once the rule’s preview consistently matches your intent. Also note which triggers the current synchronous path fires:ticket_created, ticket_status_changed, and ticket_priority_changed emit from the ticket mutation service; ticket_tagged and ticket_idle enter through the same rule grammar but are scheduled-event arms — the ticket_idle arm is the one a future sweep closes. The rule mechanics below work identically either way, because the engine is the same pure core the preview endpoint and the executor share.
4. Worked end-to-end — the billing-escalation rule
The target: when a ticket taggedbilling-issue goes idle for over two hours, retag it as escalated and notify the supervisor-code over Slack. We author it against the catalog, dry-run it against a sample ticket, then save it as a persisted rule.
4.1 Authoring against the catalog
The builder’s first move for every rule is the same five-step checklist:- Pick the trigger —
ticket_idle. The picker renders fromcatalog.triggersreturned byGET /ticket-automation/catalog, so it names exactly the five lifecycle events above. - Add the tag condition — field
tags, operatorcontains, value"billing-issue". - Add the time gate — field
idle_minutes, operatorgte, value120. The catalog reportsidle_minuteswith onlygte/ltein its operator list, which is how the builder narrows the picker for this field instead of offering every operator everywhere. - Pick
match: all— both conditions must hold. Distinct fromany, which needs only one to hold and would fire the escalation on any tag-less idle ticket. - Wire the actions —
add_tagwithtag: "escalated", thennotifywithchannel: "slack"andtarget: "#billing-supervisor". The dropdowns render fromcatalog.action_typesplus the closedpriorities/statuses/typeslists the catalog also returns.
4.2 Dry-run the rule
Before saving anything, dry-run the inline rule against a sample ticket whose last-touch timestamp we fake withidle_minutes: 150:
matched— did the rule’s conditions hold on that sample.actions— the fire list in evaluation order.effect— the resulting ticket patch plus the notifications the actions would emit. Thepatchis folded so only fields that actually change show up — a no-opadd_tagon an already-tagged ticket contributes nothing.
idle_minutes to 60 and the call returns matched: false with an empty actions and effect — the dry-run is symmetric, so use it to probe both sides of the gate.
4.3 Save the rule
Once the preview says yes, persist the rule withPOST /api/v1/inbox/ticket-automation/rules — the body is exactly the rule object above, unchanged:
stop_processing: false is the polite default — leave evaluation open so the generic triage rules after this one still run. Flip it to true only when a rule’s first-match should be authoritative (a VIP fast-track, for example).
5. Reading catalog enums — the no-hardcoding pattern
The catalog endpoint exists so the builder does not need its own copy of the backend’s vocabulary. The mental model:- Fields drive operator pickers. A field’s
operatorsarray is the closed set for that field —idle_minuteslists onlygte/lte, the enum fields listeqthroughis_not_emptywithoutcontains, and so on. Render the picker from the field record, not from the global operator list. - Closed value lists ride with the catalog.
priorities,statuses,typesship as arrays on the top level, and the per-field records repeat the same closed lists (so aprioritycondition can render its value dropdown straight from itsvaluesentry). - Triggers are the picker for the rule’s binding event. Rendered from
catalog.triggers. - Action chips render from
catalog.action_types. The notify action’s channel dropdown is the schema-narrowedslack | in_app | webhooktriple, and that narrowing is the compliance gate — automation never becomes an outbound-messaging path.
6. Rollout checklist — author to live
- Start from the catalog. Load
GET /ticket-automation/catalogonce per session; your pickers should match its grammar without any manual enum list. - Draft the rule. One trigger, conditions on the fields that actually distinguish the tickets you care about, and at most a few narrowly-scoped actions.
- Dry-run the positive case. Send a sample ticket that should match to
POST /ticket-automation/test; confirmmatched: trueand the actions and effect you intend. - Dry-run the negative case(s). Flip one input — drop the tag, lower the idle minutes — and confirm
matched: false. If the negative case matches, tighten conditions before persisting anything. - Save the rule.
POST /ticket-automation/rules. Give the rule a verb-first name (“Billing idle → escalate + notify”) so the timeline entry and the audit row read plainly. - Test the persisted path. Fire a real ticket-create / status-change / priority-change event carrying the trigger your rule subscribes to, then read the ticket timeline: the applied rule shows up as a system-authored note plus the audit row.
- Iterate conservatively.
PATCH /ticket-automation/rules/:idwith the same body shape; dry-run the revision before you replace the live one. When a rule is genuinely done,DELETEremoves it — there is no soft-disable path beyond theenabled: falseflag. - Treat execution as a milestone. The persist + execution half of the split is announced in the page header as the follow-up milestone; until it lands on your workspace, the builder + dry-run contract above is the shipped surface and the rules you save are validated but not yet firing.
ticket_idle schedule-driven runs — the synchronous triggers (ticket_created, ticket_status_changed, ticket_priority_changed) already fire; the idle and tag arms are grammar-complete where the executor is trigger-agnostic.
See also
- Ticket automation — event-driven ticket rules — the product page for the same engine: action behaviour, delivery guarantees, and how automation composes with SLA escalation policies and reply approvals.
- Inbox tickets workflow — tickets, the queue SLA clocks they pick up at open time, and the
ticket_createdtrigger they raise. - Inbox SLA timers — the conversation-level clocks whose due windows a priority-changing rule re-scales; a nice pairing for a
set_priorityautomation. - Webhook events — subscribe to the
ticket.automation_appliedevent the executor emits on every applied rule. - Inbox API reference — full request/response schemas for the endpoints above (catalog, test, rules CRUD).