Using SmartLists data elsewhere
SmartList rows are stored on the work item as a Jira issue property, which makes them readable - and writable - from the REST API, Jira Automation and ScriptRunner.
The property key is:
forge-smartlists-issue-data
If you have existing scripts or rules, read The legacy property key at the end of this page.
Reading the data over REST
GET /rest/api/2/issue/{issueKey}/properties/forge-smartlists-issue-data
If the work item has no SmartLists, Jira responds:
{
"errors": {},
"errorMessages": [
"The property forge-smartlists-issue-data does not exist."
],
"httpStatusCode": {
"empty": false,
"present": true
}
}
Otherwise, value is an array - one entry per SmartList on the work item. Each entry has two keys:
template- the panel definition the SmartList was created from (its name, project and columns)data- the rows the user filled in
Example payload
A work item with two SmartLists, Testing checklist and Cursed Menu:
{
"key": "forge-smartlists-issue-data",
"value": [
{
"template": {
"columns": [
{
"name": "Test Name",
"id": "f43298da-a6ff-45eb-9514-436ff1de94a5",
"type": "Text",
"required": false
},
{
"name": "Status",
"options": [
{ "label": "Fail", "value": "Fail" },
{ "label": "Some Issues", "value": "Some Issues" },
{ "label": "No Issues", "value": "No Issues" }
],
"id": "8764a455-f367-4b57-a089-b5bf9b7bd06e",
"type": "Checkbox",
"required": false
},
{
"name": "Date Performed",
"id": "7a6dda47-b278-42fd-ba4a-03982be84ae5",
"type": "Date",
"required": false
}
],
"name": "Testing checklist",
"project": "PROJ",
"id": "c4f12153-de1b-4c69-9425-607fc7c1bfc5"
},
"data": {
"templateId": "c4f12153-de1b-4c69-9425-607fc7c1bfc5",
"rows": [
{
"id": "c4f12153-de1b-4c69-9425-607fc7c1bfc5-0",
"data": {
"Test Name": "The component renders",
"Status": "Some Issues",
"Date Performed": "2025-04-01"
}
},
{
"id": "c4f12153-de1b-4c69-9425-607fc7c1bfc5-1",
"data": {
"Test Name": "Data is saved",
"Status": "No Issues",
"Date Performed": "2025-04-02"
}
}
]
}
}
]
}
Four things to know about the shape
Row data is keyed by column name, not column id. row.data["Test Name"]. Renaming a column on the panel does not rename the key in rows that already exist.
Multi-value cells are pipe-delimited strings. Checkbox, User and File columns store several values in one string joined with |, for example, "Vanilla Cake|Strawberry Cake". Split on | to get the list.
Select columns store the value, not the label. If you configured an option with label 25% and value 25-percent, the row holds 25-percent.
Calculation columns store the computed result, and store the error message text when the calculation failed. A cell reading "Division by zero" is a failed calculation, not data.
Jira Automation
SmartList data is reachable through smart values:
{{issue.properties.forge-smartlists-issue-data}}
Logging the raw data
Create a rule with a Manual trigger and a Log action:
{{issue.key}} SmartList data: {{issue.properties.forge-smartlists-issue-data}}
The result appears in the rule's audit log.
Checking whether a work item has any SmartLists
Add a smart value condition:
First value:
{{issue.properties.forge-smartlists-issue-data}}Condition:
does not equalSecond value:
Empty
The rule stops unless the work item has SmartList data.
Iterating over rows
Use the Advanced branching component over {{issue.properties.forge-smartlists-issue-data}} to loop through each SmartList, then over its rows. From there, you can comment on the work item, transition it, or set a field from the extracted values.
ScriptRunner for Jira Cloud
Reading and iterating
def issueKey = 'PROJ-10'
// Fetch the work item's properties.
def result = get("/rest/api/2/issue/${issueKey}/properties/forge-smartlists-issue-data")
.asObject(Map) // asObject converts JSON to a Groovy Map
.body['value'] // the list of SmartLists
if (!result) {
logger.info("Issue {} does not have any SmartLists data.", issueKey)
return
}
result.each { smartlist ->
def template = smartlist['template'] // the panel definition
def rows = smartlist['data']['rows'] // the user-entered rows
println "SmartList Name: ${template['name']} - Columns: ${template['columns'].collect { it['name'] }}"
rows.eachWithIndex { row, idx ->
println "Row Data ${idx}: ${row['data']}"
}
}
Output:
SmartList Name: Testing checklist - Columns: [Test Name, Status, Date Performed, Passed?] Row Data 0: [Test Name:The component renders, Status:Some Issues, Date Performed:2025-04-01, Passed?:Fail] Row Data 1: [Test Name:Data is saved, Status:No Issues, Date Performed:2025-04-02, Passed?:Pass]
Calculating across rows
Counting rows, summarising a Select column, and collecting the distinct values of a multi-value Checkbox column:
def issueKey = 'PROJ-10'
def result = get("/rest/api/2/issue/${issueKey}/properties/forge-smartlists-issue-data")
.asObject(Map)
.body['value']
if (!result) {
logger.info("Issue {} does not have any SmartList data.", issueKey)
return
}
def rowCounts = [:]
def passedSummary = [Pass: 0, Fail: 0]
def uniqueCakes = [] as Set
result.each { smartlist ->
def template = smartlist['template']
def rows = smartlist['data']['rows']
rowCounts[template['name']] = rows.size()
if (template['name'] == "Testing checklist") {
rows.each { row ->
def passedStatus = row['data']['Passed?']
if (passedStatus) {
passedSummary[passedStatus] = passedSummary.get(passedStatus, 0) + 1
}
}
} else if (template['name'] == "Cursed Menu") {
rows.each { row ->
def cakes = row['data']['Cakes']
if (cakes) {
uniqueCakes.addAll(cakes.split("\\|")) // multi-value cells are pipe-delimited
}
}
}
}
println "Row Counts Per SmartList: ${rowCounts}"
println "Passed Summary: ${passedSummary}"
println "Unique Cakes: ${uniqueCakes}"
Writing rows back
You can add rows programmatically by reading the property, modifying it and PUTting it back. This example appends three tasks to the first SmartList on a work item:
import groovy.json.JsonOutput
def issueKey = 'PROJ-8'
def smartlists = get("/rest/api/2/issue/${issueKey}/properties/forge-smartlists-issue-data")
.asObject(Map).body['value']
if (!smartlists) {
logger.info("Issue {} does not have any SmartLists data.", issueKey)
return
}
def smartlist = smartlists?.head()['data'] // the first SmartList
def templateId = smartlist['templateId']
def rows = smartlist['rows']
rows << [ id: "${templateId}-1", data: [ "Task": "Install Operating System", "Completed": "No" ] ]
rows << [ id: "${templateId}-2", data: [ "Task": "Install antivirus software", "Completed": "No" ] ]
rows << [ id: "${templateId}-3", data: [ "Task": "Install browser", "Completed": "No" ] ]
def payload = smartlists
payload[0]['data']['rows'] = rows
def result = put("/rest/api/2/issue/${issueKey}/properties/forge-smartlists-issue-data")
.header('Content-Type', 'application/json')
.body(payload)
.asString()
if (result.status == 200) {
logger.info("Successfully updated issue properties.")
} else {
logger.error("Failed to update issue properties: {} - {}", result.status, result.body)
}
Rules for writing safely
The app reads whatever is in the property and renders it. Nothing validates what you write, so:
PUT the whole array, not a single SmartList. The property value is the complete list.
Give every row a unique
id. The convention is{templateId}-{index}.Match the column names exactly, including case and punctuation. A key that does not match a column name is ignored by the UI but stays in the stored data.
Use the option
value, not the label, for Select columns.Join multi-value cells with
|.Footer totals are not recalculated by a write. They refresh the next time a user edits and saves the SmartList in the UI.
The legacy property key
The property was originally named forge-jira-issue-matrix-data. It was renamed to forge-smartlists-issue-data when the app was renamed to SmartLists for Jira.
Work items that still carry the old property are migrated automatically the first time someone opens them in Jira, so no data is lost. But:
Scripts and automation rules referencing the old key must be updated. They will not fail loudly - they will simply find nothing.
A work item that has not been opened since the rename may still hold data under the old key. If you are sweeping across many work items, read the new key first and fall back to the old one.