Automation rules

Automation rules let a project react to ticket events — or run on a clock — without anyone watching the board. Each rule pairs one trigger or one schedule with an optional set of conditions and one or more actions: escalate bugs that are due soon, label everything a customer comments on, or post a canned reply when a ticket is created. Rules are configured per project, with a separate set of global rules that apply across every project.

Where to find it

  • Project rules — /projects/:key/automation in the app (breadcrumb: project → Settings → Automation rules). Managing them requires the project admin role on that project; org-wide administrators pass automatically.
  • Global rules — /admin/automation ("Global automation"). This page requires the org ADMIN role.
  • API — project rules live under /api/v1/projects/{projectKey}/automation-rules; global rules under /api/v1/admin/automation-rules.

Anatomy of a rule

The builder on the automation page has four parts: a name (required, at most 120 characters), a When selector, an If section of conditions, and a Then section of actions. The When selector offers the two schedules (*Every hour (scheduled)*, *Every day (scheduled)*) followed by the six event triggers.

  • Exactly one of an event trigger or a schedule — the server rejects a rule with both or neither: A rule needs exactly one of: an event trigger or a schedule.
  • Conditions are optional and are ANDed: the builder labels them "(optional — all must match)". There is no OR; create a second rule instead.
  • At least one action is required (A rule needs at least one action). Actions run in the order listed.

New rules are created enabled. Each rule row has *Disable*/*Enable* and *Delete* controls; a disabled rule is skipped entirely (event rules do not fire, scheduled rules are not claimed).

Triggers

An event-triggered rule runs after the triggering change commits, in its own transaction — a failing rule can never roll back or block the user's edit. The six triggers are the values of the TicketChangeKind enum:

TriggerBuilder labelFires when
CREATEDTicket createdA ticket is created — by a user, the customer portal, email intake or a recurring template.
UPDATEDTicket updatedA ticket's fields are edited.
STATUS_CHANGEDStatus changedA ticket is transitioned to another workflow status.
ASSIGNEDAssignee changedThe assignee is changed.
COMMENTEDComment addedA comment is added — including customer portal replies and inbound email replies.
LABEL_ADDEDLabel addedA label is attached by a user (set-labels or CSV attach).

Loop-proofing. Rule actions mutate tickets directly and never re-publish a ticket-changed event, so automation cannot trigger itself: an ADD_COMMENT action does not fire COMMENTED rules, an automation ADD_LABEL does not fire LABEL_ADDED, and so on. Chained or cyclic rules are impossible by construction.

Scheduled rules

Pick *Every hour (scheduled)* or *Every day (scheduled)* instead of a trigger to make a clock-driven rule (schedule = HOURLY or DAILY). The backend checks for due rules every 15 seconds; each run is claimed with a compare-and-swap on the rule's next-run timestamp, so in a multi-node cluster exactly one backend executes it, and the next slot is set one hour or one day from the moment the run is claimed — a freshly created hourly rule first fires roughly an hour after creation. A scheduled run sweeps the project's tickets: it takes the first 200 tickets (in board order — rank, then ticket number), applies the conditions to each, and runs the actions on every match, then records one summary row in the run history, e.g. scheduled sweep: changed 3 of 7 matching tickets.

The 200-ticket sweep budget is a hard cap. In a project with more than 200 tickets, tickets beyond the first 200 in board order are not examined on that run. A global scheduled rule shares a single 200-ticket budget across *all* projects, consumed project by project — with many projects, later projects may not be swept at all. Prefer event triggers where possible, and keep scheduled rules for time-based conditions such as approaching due dates.

Conditions

Each condition names a ticket field and an expected value. All five fields are available to project rules; global rules may use only TYPE, PRIORITY and DUE_WITHIN (see below).

FieldValueMatches when
TYPEType name or code (case-insensitive), e.g. BUGThe ticket's type matches — custom, SAFe and sub-task types included; the builder lists the project's configured types.
PRIORITYOne of HIGHEST, HIGH, MEDIUM, LOW, LOWESTThe ticket has that priority.
STATUSWorkflow status id (the builder shows status names)The ticket is currently in that status.
LABELLabel id (the builder shows label names)The ticket carries that label.
DUE_WITHINWhole number of days N (builder input: 0–365)The ticket has a due date on or before today + N days.

DUE_WITHIN deserves care: overdue tickets match too (the test is "due date not after today + N"), 0 means "due today or already overdue", and a ticket without a due date never matches. Combined with a daily schedule it makes a reliable escalation rule.

Actions

Actions are deliberately conservative: an action that would change nothing, or whose target no longer exists, is a silent no-op rather than an error, and if one action fails the remaining actions of the rule still run. Applied actions are written to the ticket's change history, attributed to the user who created the rule — except ADD_LABEL, which attaches the label without a history entry.

ActionBuilder labelValueEffect / no-op cases
ASSIGNAssign toUser id (builder lists project members)Sets the assignee. No-op if already assigned to that user or the user no longer exists.
SET_PRIORITYSet priorityHIGHEST … LOWESTSets the priority. No-op if unchanged.
TRANSITIONTransition toWorkflow status idMoves the ticket — only if the workflow allows that transition from the current status for the ticket's type; otherwise no-op.
ADD_LABELAdd labelLabel idAttaches the label. No-op if already present or the label belongs to another project.
ADD_COMMENTAdd commentFree text (smart values supported)Posts a comment — internal by default, optionally customer-visible.
ADD_CANNED_COMMENTAdd canned commentCanned response idPosts the body of a canned-response template as a comment; same visibility option. No-op if the template was deleted or belongs to another project.

Automated comments and customer visibility

Comments posted by ADD_COMMENT and ADD_CANNED_COMMENT default to internal notes — customers never see them. Ticking the visible to customer checkbox on the action makes the comment a public reply instead; for tickets raised through the customer portal or email intake, a public automated reply is also emailed to the requester. The comment is authored by the rule's creator, so it reads like any other reply. Canned-comment templates come from the project's canned responses (managed in project settings) — pointing a rule at a template keeps the wording in one place: edit the response and every rule that posts it picks up the new text.

Smart values

Comment bodies may embed {{token}} placeholders resolved from the triggering ticket at post time. Token names are case-insensitive and tolerate surrounding spaces ({{ issue.key }}). A known token with no value renders empty; an unknown token is left as literal text so typos stay visible instead of silently vanishing. A comment that renders to nothing but whitespace is not posted at all.

TokenRenders as
{{issue.key}}Ticket key, e.g. PROJ-42
{{issue.title}} / {{issue.summary}}Ticket title
{{issue.priority}}Priority name, e.g. HIGH
{{issue.status}}Current workflow status name
{{issue.assignee}}Assignee display name, or Unassigned
{{issue.type}}Ticket type name
{{issue.dueDate}}Due date (YYYY-MM-DD), empty if none
{{now}}Today's date
{{now.plusDays(N)}} / {{now.minusDays(N)}}Today shifted by N days (N up to 4 digits)
An ADD_COMMENT body using smart values
{{issue.key}} "{{issue.title}}" is due {{issue.dueDate}}.
Escalated to {{issue.priority}} on {{now}}; follow up by {{now.plusDays(3)}}.

Run history

The Recent runs section at the bottom of the project automation page (with a *Refresh* control) shows the last 50 executions, newest first; the platform retains the newest 200 runs per project, pruning older rows automatically as new runs are logged. Each row records the rule name, the ticket, the result and a timestamp; ticket keys link straight to the ticket. Global-rule runs are logged under each affected project, so they appear here too.

ResultShown asMeaning
APPLIEDappliedConditions matched and at least one action changed the ticket.
SKIPPEDmatched, no changeConditions matched but every action was a no-op (e.g. priority already set).
FAILEDfailedThe rule threw; the error message is shown next to the rule name (truncated to 500 characters).

Event-triggered runs are logged per matched ticket. A scheduled sweep logs one row with * in the ticket column and a summary message such as scheduled sweep: changed 2 of 5 matching tickets (global rules: global sweep: …). An event rule whose conditions do not match logs nothing; a project scheduled sweep records its summary row even when no ticket matched, while a global sweep logs only in projects with at least one match.

Project rules vs global rules

Global rules run in every project but are restricted to project-agnostic vocabulary — nothing that names a per-project object. The server enforces this on save and rejects, for example, a STATUS condition with Global rules cannot use the STATUS condition (project-scoped).

Project rulesGlobal rules
Managed at/projects/:key/automation/admin/automation
Required roleProject admin (or org ADMIN)Org ADMIN
ConditionsAll fiveTYPE, PRIORITY, DUE_WITHIN only
ActionsAll sixASSIGN, SET_PRIORITY, ADD_COMMENT only
Scheduled sweep budget200 tickets in that project200 tickets shared across all projects
Runs appearOn the project's runs listOn each affected project's runs list

API

Method & pathPurpose
GET /api/v1/projects/{projectKey}/automation-rulesList the project's rules
POST /api/v1/projects/{projectKey}/automation-rulesCreate a rule (returns 201)
PUT /api/v1/projects/{projectKey}/automation-rules/{ruleId}Update a rule (also used to enable/disable)
DELETE /api/v1/projects/{projectKey}/automation-rules/{ruleId}Delete a rule (returns 204)
GET /api/v1/projects/{projectKey}/automation-rules/runsLast 50 runs for the project
GET|POST|PUT|DELETE /api/v1/admin/automation-rules[/{ruleId}]Same operations for global rules
POST body: a daily scheduled escalation rule
{
  "name": "Escalate bugs due this week",
  "enabled": true,
  "trigger": null,
  "schedule": "DAILY",
  "conditions": [
    { "field": "TYPE", "value": "BUG" },
    { "field": "DUE_WITHIN", "value": "7" }
  ],
  "actions": [
    { "type": "SET_PRIORITY", "value": "HIGHEST" },
    { "type": "ADD_COMMENT",
      "value": "{{issue.key}} is due {{issue.dueDate}} — escalated on {{now}}.",
      "customerVisible": false }
  ]
}

For an event-triggered rule, set trigger to one of CREATED, UPDATED, STATUS_CHANGED, ASSIGNED, COMMENTED or LABEL_ADDED and omit schedule (or send null). customerVisible applies to ADD_COMMENT and ADD_CANNED_COMMENT and defaults to false; rules saved before the flag existed are treated as internal.

Auditing rule changes

Every create, update and delete of an automation rule is written to the configuration audit log under the AUTOMATION_RULE category, recording who made the change, when, the action (CREATE, UPDATE or DELETE) and the rule's id and name — enabling or disabling a rule counts as an UPDATE. Org administrators can review these entries at /admin/config-audit (API: GET /api/v1/admin/config-audit). The audit records the fact of the change, not a before/after diff.