Products
Services
Blog

Display Assignee History of a Jira Issue

Roma Bubyakin

Roma Bubyakin

Oct 21, 2025

Read 9 min

Article

Topics covered: Jira Integration, Confluence Storage Format, User Parameters, Sorting

Request

Sam Tang posted a question on 👉 community.atlassian.com

Hello Atlassian Community,

I want to list out a table to display some tickets' assignee history, I know I can get the information via 'activity tab' for a single ticket, but I would like a list of tickets in a table like this:

Ticket ID, ticket summary, assignee history

ID-1          ticket 1           Tom > Jerry > Shannon

ID-2          ticket 2           Jerry > Tom> Shannon

ID-3          ticket 3           A> B> C

 

Can you please help? 
Thank you very much.

The Real Problem!

This macro solves a frequent visibility issue:
How do you track and display who was assigned to a Jira issue over time, directly within Confluence?

Perfect for:

  • Sprint retrospectives

  • Audits

  • Issue lifecycle analysis

Can User Macro deal with it? Let’s see…


Assignee History Macro

Step 1: Basic macro

This will be a static, block macro, without a body.

image-20250716-182155.png

For customization, the JQL User Parameter is needed:

image-20250622-154146.png

Use a String parameter (not required), with this default value: reporter = currentUser() AND updatedDate >= "-30d"

image-20250622-154012.png

The following template will do

  • Make a Jira request

  • Print a list of issues with the assignee’s change history

## Search for issue with key, summary, and changelog included
#set ( $issues = $JiraManager.get("/rest/api/3/search/jql?fields=key,summary,assignee&expand=changelog&jql=${parameters.jql}").issues )

#foreach ( $i in $issues)
<p>$i.key:<br>
#foreach ( $h in $i.changelog.histories  )
  #foreach ($item in $h.items)
    #if ($item.fieldId == "assignee")
$!item.fromString → $!item.toString<br>
    #end
  #end
#end
</p>
#end

! symbol is used to handle empty strings in values like $!item.fromString and $!item.toString

Without it, you might see errors like this on Unassigned issues:

Rendering error in User Macro: Assignee History
Reference $item.fromString evaluated to null when attempting to render at template[line 43, column 1]

To test the result, add the macro to the page using Macro Browser or by typing /Issue Assignee History.

Update the cached macro list
Newly created macros might not appear due to client-side caching. Use Ctrl/Cmd + R to fully reload the page and see the new macro
image-20250622-154924.png

JiraManager makes requests to Jira using OAuth authentication.
This requires a one-time authorization step to allow access.

image-20250717-071827.png
$JiraManager
All requests are done on behalf of the current user — so Jira security settings are respected by design

Once access is granted, the template renders like this:

image-20250716-173901.png
User Parameter
Your results may differ from the images, depending on the JQL you use

Step 2: Full Assignee History

Previously, only changes were shown. Now, we’ve added support for empty states and aligned changes into a single line.

If the JQL returns nothing, the macro includes a Check for an empty result to handle that and notify the user.

To make the assignee history a one-liner, the current Assignee is shown, followed by a list of $item.fromString values from the change history.

## Search for issue with key, summary, and changelog included
#set ( $issues = $JiraManager.get("/rest/api/3/search/jql?fields=key,summary,assignee&expand=changelog&jql=${parameters.jql}").issues )

## Check for an empty result
#if(!$issues)
  No issues for JQL <code>${parameters.jql}</code></a>
  #stop ## End of execution, there is nothing to render
#end

#foreach ( $i in $issues)
<b>$i.key:</b>
  ## Current Assignee
  #if ($i.fields.assignee)
$i.fields.assignee.displayName
  #else
<i>(Unassigned)</i>
  #end
  #foreach ( $h in $i.changelog.histories  )
    #foreach ($item in $h.items)
      #if ($item.fieldId == "assignee")
        #if ($item.fromString)
 ← $item.fromString
        #else
 ← <i>(Unassigned)</i>
        #end
      #end
    #end
  #end
<br> ## new line for new issue
#end

Ctrl+S or Cmd+S to save the template

Ctrl+R or Cmd+R to reload the page and see the result

image-20250716-174137.png

Step 3: Nice-looking Assignee History with clickable usernames

Changes in this step:

  • The current assignee moved to the end of the history

  • Arrows reversed from ← to →

  • History items sorted chronologically using SortTool:

$SortTool.sort($i.changelog.histories, ["created"])

To create Resource Identifiers links, usernames were replaced with AccountIDs using Confluence Storage Format:

<ri:user ri:account-id="${i.fields.assignee.accountId}"/></ac:link>

And finally, the Atlassian UI lozenge was used for the Unassigned state. The following class aui-lozenge-subtle makes it lighter:

<span class="aui-lozenge aui-lozenge-subtle">unassigned</span>

The whole new template:

## Search for issue with key, summary, and changelog included
#set ( $issues = $JiraManager.get("/rest/api/3/search/jql?fields=key,summary,assignee&expand=changelog&jql=${parameters.jql}").issues )

## Check for empty result
#if(!$issues)
  No issues for JQL <code>${parameters.jql}</code></a>
  #stop ## End of execution, there is nothing to render
#end

#foreach ( $i in $issues)
<b>$i.key:</b>

  ## Sort history items by creation date ascending 
  #set ($sortedHistory = $SortTool.sort($i.changelog.histories, ["created"]))
  #foreach ( $h in $sortedHistory )
    #foreach ($item in $h.items)
      #if ($item.fieldId == "assignee")
        #if ($item.from)
<ac:link><ri:user ri:account-id="${item.from}"/></ac:link> → 
        #else
<span class="aui-lozenge aui-lozenge-subtle">unassigned</span> → 
        #end
      #end
    #end
  #end
  #if ($i.fields.assignee)
<ac:link><ri:user ri:account-id="${i.fields.assignee.accountId}"/></ac:link>
  #else
<span class="aui-lozenge aui-lozenge-subtle">unassigned</span>
  #end
<br> ## new line for new issue
#end

And here’s how it looks on the page now:

image-20250716-175509.png

Quite better. But something is missing…

Step 4: Table view with issue details

We need a $jiraURL variable to make the Issue key clickable. Jira URL is the same as Confluence’s $baseUrl, but without /wiki. Here’s how to construct the issue URL:

$baseUrl                                     ## 1
$StringUtils.replace($baseUrl, "/wiki", "")  ## 2
#set ( $key = "UM-555" )
${jiraURL}/browse/${key}                     ## 3

## Output
1> https://subdomain.atlassian.net/wiki
2> https://subdomain.atlassian.net
3> https://subdomain.atlassian.net/browse/UM-555

To prevent Issue Key from wrapping onto two lines, an inline style is needed:

<td style="white-space: nowrap;">
image-20250717-064752.png

Aside from that, the table is pretty standard, now with an extra Summary column:

## Search for issue with key, summary, and changelog included
#set ( $issues = $JiraManager.get("/rest/api/3/search/jql?fields=key,summary,assignee&expand=changelog&jql=${parameters.jql}").issues )

## Jira URL is a Confluence baseURL, but w/o "wiki/"
#set ( $jiraURL = $StringUtils.replace($baseUrl, "/wiki", "") )

## Check for empty result
#if(!$issues)
  No issues for JQL <code>${parameters.jql}</code></a>
  #stop ## End of execution, there is nothing to render
#end

<table>
      <thead>
        <tr>
          <th>Issue</th>
          <th>Summary</th>
          <th>Assignee history</th>
        </tr>
      </thead>
      <tbody>
  
  ## Loop through issues
  #foreach ( $i in $issues)
  
        <tr>
          <td style="white-space: nowrap;"> ## "nowrap" for issue key to be in one line 
            <a href="${jiraURL}/browse/${i.key}">$i.key</a>
          </td>
          <td>$i.fields.summary</td>
          <td>
          #if ( $i.changelog.histories )
        
            ## Sort history items by creation date ascending 
            #set ($sortedHistory = $SortTool.sort($i.changelog.histories, ["created"]))
            #foreach ( $h in $sortedHistory )
              #foreach ($item in $h.items)
                #if ($item.fieldId == "assignee")
                  #if ($item.from)
<ac:link><ri:user ri:account-id="${item.from}"/></ac:link> → 
                  #else
<span class="aui-lozenge aui-lozenge-subtle">unassigned</span> → 
                  #end
                #end
              #end
            #end
          #end
          ## Current Assignee
          #if ($i.fields.assignee)
<ac:link><ri:user ri:account-id="${i.fields.assignee.accountId}"/></ac:link>
          #else
<span class="aui-lozenge aui-lozenge-subtle">unassigned</span>
          #end
          </td>
        </tr>
        
  #end

      </tbody>
    </table>
image-20250716-181612.png

That’s it! The macro is now complete and has been uploaded to the Library: Issues Assignee History


The REAL problem has been solved — with User Macro for Confluence Cloud (wink)

Got Questions?

Fill out the form, and we'll guide you through every step of the way