Automation
Last updated: 6 September 2026
Automation rules react to what happens to your issues: a card entering a column, a new issue appearing, a field changing, a card overstaying its column's time limit. Each rule is a trigger, a set of conditions, and a list of actions.
Rules belong to a board and are edited in Settings → Automation Rules by that board's administrators (or a Jira site admin).
Where rules run
This matters more than it sounds, so it's worth stating plainly. Two things run rules today: the board you have open, and the hourly SLA checker.
In the board, while it's open. When you drag a card between columns, or create an issue from a column's quick-create form, the board evaluates that board's enabled column_enter and issue_created rules straight away and reports the result in its banner. This is the engine that handles everyday work, and it only runs for actions taken inside Kanban+, in a browser tab somebody has open.
The hourly SLA checker. A background job wakes once an hour, finds every board with at least one enabled sla_breach rule, and runs those rules against issues that have overstayed a column's threshold. Nobody has to be watching. See SLA tracking.
The Jira event listener does not currently reach any board. The app subscribes to jira:issue_updated and jira:issue_created, and the server-side engine behind that subscription is written and tested. But the step that works out which boards an incoming issue belongs to compares the issue's project key against the numeric project ids a board stores, and those two never match — so the engine finds no boards and runs nothing. This is a bug, and it is ours to fix. Until it is fixed:
- Changes made outside Kanban+ trigger no rules. Editing an issue in native Jira, in the backlog, from a REST client or from another app does nothing to your rules.
field_changedrules never fire. The event listener is their only engine.column_enterandissue_createdrules fire once, in the board — not twice. If you have written rules defensively to survive a double run, that precaution costs you nothing today and will matter again once the listener works.- When it does work, it will run only verified rules. The listener has never executed a rule for any board, so — unlike the hourly checker — it does not carry older boards forward on trust. See Permissions below.
The status-entry timestamps that SLA tracking depends on are written by a different listener, which is unaffected and working. That is why the hourly checker still has data to run on.
On a schedule. Besides the hourly checker, three jobs run once a day: a snapshot job, a cross-board dependency scan and a retention prune.
Rules run by the hourly SLA checker act as the app, not as you. Rules that run in the board act as you. Either way the change appears in Jira's own history. Because the app acts on its own authority in the background, it first has to know that a person was entitled to ask for that — which is the next section.
Permissions: verified when you save, enforced when rules run
The background engines have no user session, so the app cannot ask Jira "may this person do this?" at the moment a rule fires. It asks earlier, when the board is saved, and it asks about the person saving.
Whenever a board administrator saves the board's projects, its filter JQL, its columns or its automation rules, Kanban+ works out which Jira permissions the enabled rules need — browse for every board; assign, edit (priority, labels, due date), comment, transition or create (sub-tasks) depending on the actions — and checks, through Jira's own permissions API and using the saving administrator's permissions, that they hold each of them in every project the board covers. It also test-runs the board's filter JQL as that administrator. If anything is missing, the save is refused with a message naming the project, the permission and the rule, and nothing is stored. If it all passes, the board is stamped: who verified, when, which projects, which permissions.
When a background engine is about to act, it reads that stamp and acts only if it still covers the board's current projects and the rule's current actions. Anything else is skipped and written to the audit log as such — the row is marked unverified and every action shows as skipped. So a rule can never make the app do, in a project, something the administrator who saved it could not have done themselves.
Three consequences worth knowing:
- Rules that change issues need a project scope. A board scoped only by a custom JQL filter, with no projects, cannot be verified — there is no project to check permissions against — so such a board refuses to save a rule that changes issues. Add a project first.
- The saving administrator's permissions are what count. If a colleague created a rule that assigns issues and you, lacking Assign issues in one of the board's projects, later re-save the columns, the save is refused until you gain the permission or the rule is removed. This is deliberate: every save re-verifies the whole board as the person saving it.
- Boards saved before this check existed show an "Unverified" notice above their rules, and each rule that changes issues carries an Unverified tag. The hourly SLA checker and the daily scans keep running those boards as before, with one exception: a board that has no project in its scope (a filter JQL only) is paused, and its audit log says so once a day per rule, until a project is added. The Jira event listener, when it runs, will only run verified rules. Click Verify now in the notice (or save projects, filter, columns or rules) to verify — it re-checks the stored configuration as you, without changing it.
Disabled rules are not counted when a board is verified; enabling one is itself a save of the rules, which re-verifies.
What a rule looks like
Rules are built in the UI, but the shape they're stored in explains the vocabulary:
json
{
"id": "…",
"name": "Auto-label urgent bugs",
"enabled": true,
"trigger": { "type": "column_enter", "columnId": "<board column id>" },
"conditions": [ { "field": "priority", "op": "is", "value": "Highest" } ],
"actions": [ { "type": "add_label", "label": "needs-review" } ]
}Read as: WHEN the trigger fires, IF every condition passes, DO each action in order.
Triggers
There are four.
| Trigger | You also pick | Fires when | Run by |
|---|---|---|---|
| Issue enters column | a board column | you move a card into that column inside Kanban+ | the board, while it's open |
| Issue is created | — | you create an issue from a column's quick-create form | the board, while it's open |
| Field changes | a Jira field name, e.g. assignee, priority, status | that field appears in the update's changelog | the Jira event listener — which does not currently run |
| SLA threshold breached | a column, and at limit or at warning | the hourly scan finds an issue past that column's threshold | the hourly SLA checker |
Notes on Field changes: this trigger has no working engine today — see Where rules run above. When it does run, the field name is matched as a plain, case-sensitive string against Jira's changelog: assignee works; Assignee does not.
Notes on SLA threshold breached: the thresholds themselves are set on the column, in the Board tab's column editor — not in Settings. The trigger's at limit / at warning choice selects which of the two to test.
Conditions
Every condition must pass. A rule with no conditions always fires — the editor says so above the list.
| Field | Operators | Matches against |
|---|---|---|
type | is, is not | Issue type name |
priority | is, is not | Priority name |
status | is, is not | Status name |
assignee | is, is not, is unassigned | Display name; an empty value means unassigned |
label | contains, does not contain, is empty | Each label, as a substring |
summary | contains, does not contain | Summary text, as a substring |
reporter | is, is not | Display name |
is_blocked | is blocked by (active), is not blocked | At least one open "is blocked by" link |
has_open_blockers | is, is not | Identical to is_blocked — it's an alias |
is_blocking | is blocking (active), is not blocking anything | At least one open outward "blocks" link |
Comparisons are case-insensitive.
Two behaviours worth knowing:
- An unrecognised field or operator passes. That's deliberate — a rule saved by a future version degrades toward firing rather than silently doing nothing. The side effect is that a typo in a condition makes a rule fire more broadly, not less. Use the preview.
- Server-side blocker conditions see direct blockers only. The board's own blocked badges follow chains transitively; the server-side engines — the hourly SLA checker, and the event listener when it works — look one hop out.
Actions
Eight, executed in the order you list them. One failing action is recorded and the rest still run.
| Action | You supply | What happens |
|---|---|---|
| Assign to | a user, or nobody | Sets or clears the assignee |
| Set priority | Highest, High, Medium, Low or Lowest | Sets the priority by name |
| Set due date | +N, -N, or YYYY-MM-DD | Sets the due date |
| Add label | a label | No-op if it's already there |
| Remove label | a label | No-op if it isn't there |
| Move to status | a status name | Looks up a matching transition available from the issue's current state and applies it |
| Create subtask | a summary | Creates a sub-task in the issue's project |
| Post comment | text | Posts the text as a comment |
The details that bite
Set due date has a strict grammar. Only two forms are accepted: a signed relative day count, or an ISO date.
+7
-3
2026-12-31Anything else — +7d, next week, 2026-1-5 — is silently ignored, and the due date is left alone. The field's helper text says the same thing, but it's a quiet failure — nothing tells you afterwards.
Move to status can skip. Jira transitions are workflow-specific. If the status you named isn't reachable from the issue's current state, the action logs a warning and moves on. Nothing is applied and nothing errors loudly.
Create subtask needs a sub-task issue type. If the project has none, the action is skipped.
Comment text is not templated. This is the one that catches people. Placeholders like {{issueKey}} or {{summary}} are not substituted — the comment posts exactly the characters you typed, braces included. Write comments that read correctly as fixed text.
Actions that no longer exist
Earlier builds could post to Slack, post to Microsoft Teams, and call an outgoing webhook. All three were removed so the app makes no network calls outside Atlassian. Blocker escalation was changed for the same reason and now posts Jira comments instead of notifying an external service.
If a rule was saved with one of the removed action types, it is skipped without an error. If you're carrying rules forward from an old build, open them and check for actions that appear to do nothing.
Preview before you enable
Every rule has a 🔍 Preview button. It fetches up to 200 issues from the board's scope and shows which ones the rule's conditions match, using the same evaluator the server uses — the client and server condition logic are deliberately kept identical, so what you see is what will fire.
Preview evaluates conditions. It does not execute the actions.
Audit log
Only the server-side engines write to the log. A rule run by the hourly SLA checker, or by the event listener once it works, is recorded on the board: timestamp, rule id and name, trigger type, issue key, each action and whether it succeeded, and how long the run took. Open it in Settings → Automation Audit Log.
Runs the app declined are logged too. When an engine skips a rule because the board's permissions were not verified (see Permissions above), the entry is flagged skipped · unverified, every action shows as skipped, and hovering the row tells you why. The hourly checker writes one such entry per rule per day rather than one per hour. The daily snapshot and the daily cross-board scan write an entry of their own kind (daily snapshot, dependency scan) when they skip a board. Entries for rules that ran on a board saved before verification existed are marked legacy.
Rules that run in the board write nothing here. Their results appear in the board's banner at the moment they run and are not kept anywhere. Since the event listener currently matches no boards, in practice the log receives sla_breach entries and nothing else.
Four more things to know:
- It keeps the last 100 entries per board. Older entries fall off.
- Concurrent runs can lose an entry. The log is stored as a single record that's read, appended to, and written back — two triggers landing at the same instant can produce one entry instead of two. Actions still run; only the record is lossy.
- Anyone who can open the Kanban+ page can read it, board administrator or not. It contains rule names — which admins write freely, and which often carry a person's name — and issue keys.
- Board and site admins can clear it.
Limits
- There is no loop protection and no rate limit. Nothing in the app notices a rule whose actions re-trigger another rule, or itself. Today a chain can't start on its own, because the only engines are your own click and an hourly job — but the guard doesn't exist, so sketch the chain before you rely on rules that write fields other rules watch.
- No per-issue cooldown, except for
sla_breachtriggers, which hold a 24-hour cooldown per rule per issue. - Rules are per board, and boards are visible site-wide, so a rule you add affects every issue in that board's project scope — not only issues someone is looking at.
- Automation writes from the hourly checker are made by the app, so Jira history attributes them to the app rather than a person — but only within the permissions the saving administrator was verified to hold (see Permissions).
Related
- SLA tracking — the
sla_breachtrigger, and getting the clock right - Board & timeline — columns, WIP limits, the transition guard
- Data & privacy — what rules and audit entries store, and for how long