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 equal

  • Second 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.