Moving tickets from Zendesk to JSM Cloud

Hi
Exalate support give idea that community can help :slight_smile:

I have created connection between Zendesk and JSM with plan move all tickets from Zendesk to JSM. Now i have allready set up some connection and i have some fields what i need to get into JSM also. Yes i know that i cant have assignee and reporter because lot of people have left and Atlassian Cloud does not support accountID if there is no license. But i created allready customfields into JSM

Zendesk_Assignee

{ @key : 16609674611740}

Zendesk_Reporter

{ @key : 16609674611740}

Right now there is key but how i could get into there e-mail address from orginal user?

Also in zendesk is

  • cc_emails
  • email_cc_ids
  • followers_ids

How i should write mapping for this e-mail values to get into customfield JSM?

with best
Urmo

Great to see you’re making progress with your Zendesk to Jira Service Management (JSM) Cloud migration! You’re right about the assignee/reporter limitations in Atlassian Cloud—if the user doesn’t exist or lacks a license, you can’t set them as the actual assignee/reporter, but you can store their info in custom fields.

To map Zendesk user fields (like assignee, reporter, cc_emails, etc.) to custom fields in JSM and store their email addresses, you’ll want to extract the email from the Zendesk ticket and set it into your JSM custom field during the sync.

Here’s how you can approach this in your Exalate sync script (on the JSM incoming side):

  • For the Zendesk assignee’s email:
    issue.customFields."Zendesk_Assignee" = replica.assignee?.email
    
  • For the Zendesk reporter’s email:
    issue.customFields."Zendesk_Reporter" = replica.reporter?.email
    
  • For cc_emails (which is usually a list), you can join them into a string or handle as needed:
    issue.customFields."Zendesk_CC_Emails" = replica.cc_emails?.join(", ")
    
  • For email_cc_ids or followers_ids, you’ll need to fetch the corresponding user objects and extract their emails, if available in the replica.

If you want to see more advanced user field mapping examples (including handling cases where emails aren’t available), check out the official documentation:

And for more Zendesk-Jira integration use cases and script ideas:

This approach will let you preserve the original user information in your custom fields, even if you can’t set them as the actual assignee/reporter in JSM Cloud.

How to get zendesk ticket created time into jira customfield Zendesk_createddate?

Hi @urmo ,

Could you please help me capture the Zendesk ticket created timestamp and send it through the replica?

Preferred (simple) approach

On the Zendesk outgoing sync, can you add:

replica.zendeskCreated = issue.created

Once we see zendeskCreated in the payload, mapping it on the Jira/JSM incoming side becomes straightforward:

issue.customFields."Zendesk_createddate".value = replica.zendeskCreated

Fallback (only if the above doesn’t show in payload)

If issue.created is not available / not coming through correctly, we can fetch it directly from the Zendesk API, parse it, and then pass it via the replica.

Example:

def response = httpClient.get("/api/v2/tickets/${ticket.id}.json")
def createdAt = response?.ticket?.created_at  // e.g. "2026-02-02T10:15:30Z"
replica.zendeskCreated = createdAt

Then we can map the same way on Jira:

issue.customFields."Zendesk_createddate".value = replica.zendeskCreated

If you can add the simple line first and confirm whether zendeskCreated appears in the outgoing payload, that would be ideal — we may not need the API route at all.

Thanks, Dhiren!

Hi

I updated Outgoing sync:
// Set Zendesk_follow to a comma-separated list of follower emails, only if the field exists

if (issue.customFields.containsKey(‘Zendesk_follow’)) {

*if (replica.followers instanceof List) {*

    *issue.customFields.'Zendesk_follow'.value = replica.followers.collect { it?.email ?: '' }.findAll { it }.join(', ')*

*} else {*

    *issue.customFields.'Zendesk_follow'.value = ''*

*}*

}

// Set Zendesk_cc to a comma-separated list of CC emails, only if the field exists

if (issue.customFields.containsKey(‘Zendesk_cc’)) {

*if (replica.ccs instanceof List) {*

    *issue.customFields.'Zendesk_cc'.value = replica.ccs.collect { it?.email ?: '' }.findAll { it }.join(', ')*

*} else {*

    *issue.customFields.'Zendesk_cc'.value = ''*

*}*

}

// Map other fields as per current configuration

replica.summary = issue.summary

replica.comments = issue.comments

replica.attachments = issue.attachments

replica.type = issue.type

replica.status = issue.status

replica.priority = issue.priority

replica.assignee = issue.assignee

replica.reporter = issue.reporter

replica.key = issue.key

replica.labels = issue.labels

replica.description = issue.description

replica.zendeskCreated = issue.created

And incoming sync:

// Helper function to safely set a custom field value if the field exists

void setCustomFieldValue(issue, fieldName, value) {

*if (issue.customFields\[fieldName\] != null) {*

    *issue.customFields\[fieldName\].value = value*

*}*

}

if (firstSync) {

*issue.projectKey = "ZEN"*

}

def defaultUser = nodeHelper.getUserByEmail(“jira-placeholder@lhv.ee”)

issue.reporter = nodeHelper.getUserByEmail(replica.requester?.email) ?: defaultUser

issue.assignee = nodeHelper.getUserByEmail(replica.assignee?.email) ?: defaultUser

// Set custom field Zendesk_follow to comma-separated emails from followers, or empty string

// if (replica.followers instanceof List) {

// setCustomFieldValue(issue, “Zendesk_follow”, replica.followers.collect { it?.email }.findAll { it }.join(“,”))

// } else {

// setCustomFieldValue(issue, “Zendesk_follow”, “”)

// }

// Set summary: use replica.summary if not empty/null, otherwise use replica.key if not empty/null, otherwise default

if (replica.summary?.trim()) {

*// Replace all newline characters in the summary with a space*

*issue.summary = replica.summary.replaceAll(/\[\\r\\n\]+/, ' ')*

} else if (replica.key?.trim()) {

*issue.summary = replica.key*

} else {

*issue.summary = 'No summary provided'*

}

issue.description = replica.description

issue.comments = commentHelper.mergeComments(issue, replica)

issue.attachments = attachmentHelper.mergeAttachments(issue, replica)

issue.labels = replica.labels

issue.type = nodeHelper.getIssueType(replica.type?.name, issue.projectKey) ?: nodeHelper.getIssueType(“Zendesk”, issue.projectKey)

def priorityMap = [

*"urgent": "Highest",*

*"high"  : "High",*

*"normal": "Medium",*

*"low"   : "Low"*

]

def priorityName = priorityMap[replica.priority?.name?.toLowerCase()] ?: “Medium”

issue.priority = nodeHelper.getPriority(priorityName)

// FIX: Improve status mapping to be case-insensitive and include missing statuses

def statusMap = [

*"new"      : "Open",*

*"open"     : "Open", // Added mapping for "open"*

*"pending"  : "In Progress",*

*"waiting"  : "In Progress",*

*"solved"   : "Closed",*

*"rejected" : "Closed",*

*"done"     : "Closed"*

]

def remoteStatusName = replica.status?.name?.toLowerCase()

// Only update status if a valid mapping exists to avoid accidental changes

if (statusMap.containsKey(remoteStatusName)) {

*issue.setStatus(statusMap\[remoteStatusName\])*

}

// Set custom fields for created date and key

issue.customFields.“Zendesk_createddate”.value = replica.zendeskCreated

setCustomFieldValue(issue, “Zendesk_key”, replica.key)

setCustomFieldValue(issue, “Zendesk_Assignee”, replica.assignee?.email)

setCustomFieldValue(issue, “Zendesk_Reporter”, replica.reporter?.email)

setCustomFieldValue(issue, “Zendesk_CC_Emails”, replica.cc_emails?.join(", "))

setCustomFieldValue(issue, “Zendesk_follow”, replica.followers?.join(", "))

Seems to be that right now we don’t get over Zendesk_createddate, Zendesk_CC_Emails and Zendesk_follow.

Maybe there is still some issue what i did wrong in configuration?

with best
Urmo

Hi Urmo,

The issue is most likely on the Zendesk outgoing side.

Right now you’re trying to set JSM custom fields (issue.customFields) in the Zendesk outgoing script — that won’t work. The outgoing script should only populate the replica, not custom fields.

Please make sure you’re setting things like followers, CCs and created date on the replica (e.g. replica.zendeskFollowers, replica.zendeskCCs, replica.zendeskCreated) and then map those exact fields in the JSM incoming script.

Also double-check that the field names match exactly between outgoing and incoming — at the moment you’re referencing fields in incoming that were never added to the replica.

First step: check the Replica tab and confirm those fields are visible there. If they’re not in the replica, they can’t be mapped in JSM.

Let me know what you see there :+1:

Thanks,
Dhiren