How to Sync Task Lists and Assignee between Jira and GitHub

Published: May 07, 2025 | Last updated: Feb 25, 2026

Table of Contents

For every Epic you create in Jira, you can add child work items to indicate the list of tasks and subtasks to be performed. However, GitHub issues don’t support this type of parent-child hierarchy natively.

To keep track of information coming in from Jira, your connection should be able to replicate this hierarchy on the GitHub repo as task lists. You also need to sync the assignee between both systems.

Why Sync Task Lists and Assignees Between Jira and GitHub?

Syncing task lists and assignees between Jira and GitHub brings several benefits:

  • It fetches information about the user or work item for both Jira and GitHub users.
  • It keeps both teams on their respective systems without putting sensitive data at risk.
  • It helps internal teams and admins collaborate without context-switching.
  • It helps organizations keep track of users making changes on both sides.
  • Developers can keep track of open-source contributions from outside collaborators.

Use Case Requirements

Now that you understand why syncing task lists and assignees between Jira and GitHub matters, here are the technical requirements:

  • Getting authorization with the right access tokens
  • Fetching the right API name from the fields on both sides
  • Arranging the tasks on the GitHub side according to the established hierarchy
  • Mapping the correct Epics and tasks, as well as the assignee
  • Establishing sync rules for the incoming and outgoing data
  • Setting triggers to update the fields automatically
  • Closing the GitHub issue marks the Jira work item as “Done.”

How to Sync Task Lists and Assignees Using Exalate

You need a reliable solution that supports connections between both platforms.

Exalate is a bidirectional integration solution that works with Jira, GitHub, Zendesk, Azure DevOps, Azure DevOps Server, ServiceNow, Salesforce, Freshservice, Freshdesk, Asana, and more.

Why Exalate?

  • It supports trigger-based integration and Bulk Sync operations.
  • It provides an integration as a service (IaaS) option for MSPs.
  • You can use its Groovy scripting engine for complex use cases.
  • It simplifies scripting with Aida, the AI-assisted configuration tool.
  • Test Run functionality lets you test sync scripts before production deployment.
  • Script versioning provides full audit trails with rollback capability.

How to Configure Exalate for Jira GitHub Sync

Configure Sync Rules

After creating your Jira GitHub connection, you have two configuration options: “Quick Sync” and “Edit & Test”.

For advanced Jira GitHub integration with task lists and assignee mapping, choose “Edit & Test” to access Script Mode.

Once done, create an Epic in Jira. Then add a few children (work items) before designating an assignee.

To configure the sync, go to the connection you want to edit and click on the “Edit connection” icon.

You have two options:

  • Outgoing sync (on the Jira side) refers to the data to be sent over to the GitHub side.
  • Incoming sync (on the GitHub side) refers to the data to be received from the work item on Jira.

Jira Outgoing Sync Rules

Under the “Rules” tab on the Jira side, enter the following code snippet:

replica.key            = issue.key
replica.type           = issue.type
replica.assignee       = issue.assignee
replica.reporter       = issue.reporter
replica.summary        = issue.summary
replica.description    = issue.description
replica.labels         = issue.labels
replica.comments       = issue.comments
replica.resolution     = issue.resolution
replica.status         = issue.status
replica.parentId       = issue.parentId
replica.priority       = issue.priority
replica.attachments    = issue.attachments
replica.project        = issue.project
replica.project.versions = []
replica.project.components = []
replica."child" = []
if (issue.issueType.name == "Epic"){
    def js = new groovy.json.JsonSlurper()
    def jql = "cf[10014]=${issue.key}".toString()
    def localIssue = new JiraClient(httpClient).http("GET", "/rest/api/latest/search", ["jql":[jql]], null, [:])  {
        response ->
        if (response.code >= 300 && response.code != 404)
            throw new com.exalate.api.exception.IssueTrackerException("Failed to perform the request GET /rest/servicedeskapi/request/${issue.id}/feedback (status ${response.code}), and body was: \n\"${response.body}\"\nPlease contact Exalate Support: ".toString() + response.body)
        if (response.code == 404)
            return null
        def txt = response.body as String
        txt = js.parseText(txt)
        txt.issues.each {
            it ->
            replica."child" += it.id
        }
    }
}

replica.customFields."GitHub Repository" = issue.customFields."GitHub Repository"Code language: JavaScript (javascript)

The rules in this Jira Outgoing sync fetch the entities (key, id, string, txt, etc.) and objects in the Epic and send them over to GitHub.

GitHub Incoming Sync Rules

if(firstSync){
    if (replica.customFields."GitHub Repository"?.value?.value == "Project Mars")
        issue.repository   = "majid-org/project-mars"
    else
        issue.repository   = "majid-org/demo-repo"
    issue.summary      = replica.summary
    issue.description  = replica.description
    store(issue)
    syncHelper.syncBackAfterProcessing()
}
issue.summary      = replica.summary
issue.description  = replica.description
if (replica.issueType.name != "Task"){
    if (replica?."child".size != 0){
        issue.description += "\n\n______\nRelated Issues\n______"
        replica."child".each{
            it ->
            def localParent = syncHelper.getLocalKeyFromRemoteId(it, "issue")?.key
            issue.description += "\n- [ ] #${localParent}"
        }
    }
}
if (replica.issueType.name == "Task"){
    if (replica?.parentId){
        issue.description += "\n\n______\nParent Issue\n______"
            def localParent = syncHelper.getLocalKeyFromRemoteId(replica.parentId, "issue")?.key
            issue.description += "\n- [ ] #${localParent}"
        }
}
def userMap = [
  "syed.majid.hassan@exalate.com" : "syed-majidhassan",
  "dhiren.notani@exalate.com" :"dhirennotani19"
]
if (replica?.assignee)
  issue.assignee  = nodeHelper.getUserByUsername(userMap[replica.assignee?.email])
else {
  issue.assignee = null
  issue.assignees = null
}Code language: JavaScript (javascript)

Sync issue status and assignees with GitHub

This code snippet maps the fields and entities coming in from the Jira side to specific repositories while maintaining the hierarchy and parent-child relationship. You can also map specific assignees to different email addresses.

GitHub Outgoing Sync Rules

Under the “Rules” tab on the GitHub side, enter the following code snippet:

replica.key            = issue.key
replica.assignee       = issue.assignee 

// Support for multiple assignees to a single issue 

replica.assignees      = issue.assignees
replica.reporter       = issue.reporter
replica.summary        = issue.summary
replica.description    = issue.description
replica.labels         = issue.labels
replica.comments       = issue.comments
replica.status         = issue.status
replica.project        = issue.project
replica.id = issueKey.idStrCode language: JavaScript (javascript)

This code snippet allows you to send different entities to Jira, including all the assignees for an issue. It also fetches the issue ID and replicates it on the other side.

Jira Incoming Sync Rules

if(firstSync){
  issue.projectKey   = "CM"
  issue.typeName     = nodeHelper.getIssueType(replica.type?.name, issue.projectKey)?.name ?: "Task"
}
issue.customFields."SNOW Company".value = replica.id
issue.summary      = replica.summary
issue.comments     = commentHelper.mergeComments(issue, replica)
issue.attachments  = attachmentHelper.mergeAttachments(issue, replica)
issue.labels       = replica.labels
def statusMapping = ["open":"To Do", "closed":"Done"]
def remoteStatusName = replica.status.name
issue.setStatus(statusMapping[remoteStatusName])
def userMap = [
  "syed-majidhassan" : "syed.majid.hassan@exalate.com", 
  "dhirennotani19" :"dhiren.notani@exalate.com"
]

def defaultUser = nodeHelper.getUserByEmail("defaultuseremail")
issue.assignee  = nodeHelper.getUserByEmail(userMap[replica.assignee?.username]) ?: defaultUser
if(issue.typeName == "Task"){
    def a = ""
    def matcher  = replica.description =~ /#(\d{1,3})$/
    if (matcher)
        a = replica.description.split("#")[1]
    def localIssue = syncHelper.getLocalIssueKeyFromRemoteId(issue.'SNOW Company'.toLong())
    def res = httpClient.get("/rest/api/3/issue/${localIssue.urn}")
    res = httpClient.get("/rest/api/3/issue/${res.fields.parent.key}")
    def localParent = syncHelper.getLocalIssueKeyFromRemoteId(res.fields.customfield_10094.toLong())
  syncHelper.eventSchedulerNotificationService.scheduleSyncEventNoChangeCheck(connection, localParent)
}Code language: JavaScript (javascript)
GitHub issue showing active link to Epic in task list

With this snippet, you can fetch the status of any work item (task) and have it reflect on the GitHub issue. You will also be able to indicate the assignees and users by email and username.

On the GitHub issue, you can find a link to the Epic added as a task list. This will appear as an active link. When you click on the Epic link, you can see the tasks and the ID reflected as task lists. The status of each task will also appear on the issue.

Note: You can also use Aida to generate code snippets for this use case. Just make sure to review the code first before publishing changes.

Step 5: Set Up Triggers

Use the following trigger to establish the condition that if the Epic is from the project named “CM” and the GitHub Repository is active:

project=CM and "GitHub Repository" != null

Start monitoring your synchronization to adjust the rules according to the demands of specific projects and work items.

What Fields Can You Sync Between Jira and GitHub?

Any field accessible via REST API can be synced, including:

Standard Fields: Summaries, descriptions, comments, attachments, status, priority, assignee, labels, milestones

Advanced Fields: Custom fields, Elements Connect fields, due dates, work logs, reporters, work item types

Jira-Specific: Epic-child relationships, sprints, components, versions

GitHub-Specific: Multiple assignees, repository assignment, pull request references

Why Use Exalate for Jira GitHub Integration?

Exalate offers several advantages for this integration:

  • Unified Console: Manage all your integrations and connections from a single interface
  • AI-Assisted Configuration: Aida helps generate and troubleshoot sync scripts
  • Test Run Functionality: Test your sync scripts before production deployment
  • Script Versioning: Full audit trail of changes with rollback capability
  • Real-Time Sync: Complete queue visibility and work item history
  • Security-First: ISO 27001 certified, RBAC, encryption at rest and in transit. Visit Exalate Trust Center
  • Outcome-Based Pricing: Pay for active sync pairs, not user seats

You can also watch the video to see a comprehensive implementation of the use case.

If you still have questions or want to see how Exalate is tailored to your specific use case, book a demo with one of our experts.

Recommended Reads:

Subscribe to the Newsletter

Join +5.000 companies and get monthly integration content straight into your inbox

Shopping Basket