# Automation and JQL

A selection is not only something a person picks in the picker. It is written by
Automation, queried in JQL, read as a smart value, and kept in a relationship index that
stays correct on its own. This page covers all four.

The property, the JQL alias and the JQL function share one vocabulary, **selects** and
**selectedBy**, so the same idea reads the same way wherever you meet it.

## Setting a field from a rule

There are two ways, and they differ in what the rule author has to type and in whose name
the change is recorded.

| | This application's action | Jira's own Edit work item |
|---|---|---|
| Configuration | A form: pick the field, pick the operation, list the keys | Advanced JSON, typed by hand |
| Operations | Replace, add to, remove from, clear | Replace only |
| Work item history shows | The application | Automation for Jira |
| Respects the field's JQL scope | Yes, out-of-scope keys are skipped and named | No, it writes through REST |

### The application's action

Forge custom fields do not appear in Automation's own **Edit work item** field list
([FRGE-1108](https://ecosystem.atlassian.net/browse/FRGE-1108)), which is why this exists.

> **Set work item selection.** Set, add to, remove from or clear a Work Item Selector field.

| Input | What it takes |
|---|---|
| **Work Item Selector field** | Picked from a list of this application's fields |
| **Operation** | Replace, Add to, Remove from, or Clear |
| **Work items** | Keys separated by commas, spaces or new lines |

**It takes no work item input.** The action always writes the work item the rule is
currently running on, so a branch changes what it acts on: put it inside a branch over
linked work items and it writes each of them in turn.

<Figure
  src="/images/docs/work-item-selector/automation-set-work-item-selection.webp"
  alt="The Set work item selection action open in Jira's rule editor. Its panel holds a Work Item Selector field dropdown set to WIS Single, four Operation radio buttons reading Replace the selection, Add to the selection, Remove from the selection and Clear the selection, and a Work items box containing TK-1. Below them are two notices: Replace overwrites what is there, and The app makes this change, not a person."
  caption="The action's whole configuration. There is no work item to choose, because it writes the one the rule is on."
  width={1752}
  height={709}
/>

It writes through Jira's ordinary issue edit, **as the application**, so a change no person
made is not recorded against a person: the work item's history shows the app. Everything
else behaves normally, because it is still an ordinary edit. Field validation applies,
workflow validators run, and the relationship index and native work item links update
through the same path a human edit takes.

Acting as the application also means the action is **not limited to what the rule's user
can see**. Automation rules are configured by administrators, and the field's
[JQL scope](../jql-scope/) is the control over what a rule can select.

#### Three behaviours to know before you build a rule

<Aside type="caution" title="Replace with nothing fails, and does not clear the field">
If the work items resolve to an empty string, a smart value pointing at an empty field for
instance, the action stops with an error rather than wiping the selection on every run.
Use **Clear** to empty a field deliberately.
</Aside>

**Work items outside the field's JQL scope are skipped, not written.** The rest are
written, and the skipped keys are named in the action's `message` and `skippedKeys`
outputs. If *every* key is out of scope the action fails, so "all your keys were rejected"
can never look like "field cleared".

**To copy from another Work Item Selector field, type the smart value by hand**, as
`{{issue.customfield_12345}}`. Automation's suggestion list offers a broken form for Forge
fields ([AUTO-2131](https://jira.atlassian.com/browse/AUTO-2131)).

To select by query rather than by key, use Automation's own **Lookup work items** action
and feed `{{lookupIssues.key}}` into the work items input.

#### Outputs

Available to later rule steps: `message`, `changed`, `workItemKey`, `fieldId`, `fieldName`,
`selectedKeys`, `selectedCount`, `addedKeys`, `removedKeys`, `skippedKeys`.

Automation does not write successful action outputs to the audit log, so `message` is
written to carry the whole story on its own.

<Aside type="note" title="Two operational notes">
Changes to the action's definition can take up to **20 minutes** to appear in the rule
builder, because Automation caches app manifests.

The action is available in **every rule on the site**. Atlassian provides no way to scope
an Automation action to particular projects.
</Aside>

### Jira's own Edit work item action

The field cannot be picked from the field list, so the **Advanced** section is the only
route, and the value is JSON:

```json title="Edit work item, Advanced"
{
  "fields": {
    "customfield_10192": ["IT-1", "IT-2"]
  }
}
```

Use your own field's id rather than the one above. The field configuration screen's
**Knowledge base** section prints this snippet with the id already filled in.

This path writes through the REST API, so the field's JQL scope does not filter what it
writes, and the work item's history attributes the change to Automation for Jira rather
than to the application.

## Reading a selection in a rule

There are two ways to read one, and the ordinary one is enough for most rules.

### Reading the field itself

The field answers to a plain smart value like any other Jira field, by display name or by
id. Both forms are equivalent, and the id is the sturdier of the two because renaming the
field does not break a rule that uses it.

Below, "Blocked by" is a single select field and "Depends on" is a multi select one.

| Smart value | Gives you |
|---|---|
| `{{issue.Blocked by}}` | The one key a single select field holds |
| `{{issue.customfield_10192}}` | The same field, by id |
| `{{issue.Depends on}}` | Every key a multi select field holds |
| `{{issue.customfield_10193}}` | The same field, by id |
| `{{issue.Depends on.first}}` | Just the first of them |
| `{{issue.customfield_10193.first}}` | The same, by id |

**A multi select field returns a list**, so every one of Jira's
[list smart values](https://support.atlassian.com/cloud-automation/docs/jira-smart-values-lists/)
works on it: `.first`, `.last`, `.size`, `.get(0)`, filtering and the iteration syntax.

<Aside type="note" title="These give you keys, not work items">
The value is work item keys and nothing else, so `{{issue.Depends on.summary}}` gives
nothing. To act on the work items themselves rather than on their keys, feed the keys into
a **branch rule on related work items** with a JQL branch. The rule then runs once per
work item, with each of them as its own `{{issue}}`.
</Aside>

### Reading the relationship index

The index answers the question the field cannot: **what points at this work item**. A
field only knows what it selects, and the index keeps the reverse direction as well, which
is why a rule asking "is anything still blocked by this" reads the index rather than a
field. It works in conditions as well as actions.

| Smart value | Gives you |
|---|---|
| `{{issue.properties.workItemSelection.selects}}` | Every work item this one selects |
| `{{issue.properties.workItemSelection.selectedBy}}` | Every work item that selects this one |
| `{{issue.properties.workItemSelection.selectsCount}}` | How many it selects |
| `{{issue.properties.workItemSelection.selectedByCount}}` | How many select it |
| `{{issue.properties.workItemSelection.byField.12345.selects}}` | Just one field's selections |
| `{{issue.properties.workItemSelection.byField.12345.name}}` | That field's display name |

It also reads across every Work Item Selector field at once, where a field smart value
reads one field. Iterate a list the usual way:

```text title="Smart value"
{{#issue.properties.workItemSelection.selectedBy}}{{.}} {{/}}
```

## Custom JQL functions

Four functions, one job each, with the direction stated in the name.

| Function | Returns |
|---|---|
| `workItemsSelectedBy("IT-1")` | The work items IT-1 points at |
| `workItemsSelecting("IT-1")` | The work items that point at IT-1 |
| `workItemsWithSelection()` | Anything with at least one selection |
| `workItemsSelectedByAny()` | Anything referenced at least once |

### Narrowing a function to one field

Every function takes an optional trailing **field** argument, and it is the difference
between "related somehow" and "related in this particular way".

Leave it out and the function searches **every Work Item Selector field on the site**, so
a work item selected in "Blocked by" and one selected in "Duplicates" both come back.
Name a field and only that field's selections count.

It accepts two forms, and they mean the same thing:

| Form | Example | When to use it |
|---|---|---|
| Display name | `"Blocked by"` | Readable, and what you will write by hand |
| Field id | `"cf[12345]"` | Survives a rename, and unambiguous when two fields share a name across contexts |

The name is the field's, not the context's, so one argument covers every context that
field has. Quote it either way, and match the display name exactly, spaces included.

```jql title="JQL"
issue in workItemsSelectedBy("IT-1")
issue in workItemsSelectedBy("IT-1", "Blocked by")
issue in workItemsSelecting("IT-1", "cf[12345]")
issue in workItemsWithSelection("Blocked by")
issue not in workItemsSelectedByAny()
```

The field configuration screen's **Knowledge base** section prints these with the field's
own name and id already filled in, so the argument does not have to be typed from memory.

### Querying without a function

Every indexed path also has a JQL search alias, which is the path with a `wis` prefix, so
plain JQL works too.

| Alias | Meaning |
|---|---|
| `wisSelects` | Keys this work item selects |
| `wisSelectedBy` | Keys that select this work item |
| `wisSelectsCount`, `wisSelectedByCount` | How many, in each direction |
| `wisSelectsField`, `wisSelectedByField` | Field ids involved |
| `wisSelectsEdge`, `wisSelectedByEdge` | `"<fieldId>:<key>"` pairs |

```jql title="JQL"
wisSelects = "IT-1"
wisSelectedByCount > 0
```

## How the index stays correct

Everything above reads a relationship index, so the index being right is the thing that
makes the rest trustworthy. It is maintained asynchronously through Forge async events,
with a per-work-item concurrency key so all writes to one work item's property are
serialised rather than racing.

Four events maintain it, and installing or upgrading the application backfills work items
that already hold values:

- **Work item created** and **updated**, but only when the changelog names one of this
  application's fields
- **Work item deleted**, which clears the dangling references on everything it pointed at
  and everything that pointed at it
- **Work item link deleted**, so a link removed by hand clears the selection it came from.
  Link *creation* is deliberately not subscribed to, because a link someone adds themselves
  must not appear in the field.

<Aside type="caution" title="The property is app-owned and read-only">
The field value is the only source of truth. Writing `workItemSelection` by hand, over the
REST API or from an Automation **Send web request**, does **not** change the field and does
**not** create the reverse index on the other work item, because Forge has no event for an
issue-property change ([FRGE-55](https://ecosystem.atlassian.net/browse/FRGE-55), which
Atlassian deprioritised in April 2026).

It *is* still indexed by Jira, so a hand-written document pollutes the `wis*` aliases, the
smart values above and the validator's circular-dependency check until the application next
rewrites it. To change a selection, write the **field**: use the Automation action above, or
`PUT /rest/api/3/issue/{key}`.
</Aside>

## Related

- [Workflow validation](../workflow-validation/), which enforces rules on a selection at a transition
- [JQL scope](../jql-scope/), which bounds what a rule or a person can select
- [Permission mode](../permission-mode/)
- [Privacy and data handling](../privacy/)

---

A problem or a question? Write to [support@oktul.com](mailto:support@oktul.com) or open a request in the [Help Center](https://oktul.atlassian.net/servicedesk/customer/portals). Both reach the same service desk, so either way the request gets a reference and an SLA measuring the response.
