Importing from an old help desk system: old reporters and agents

I'm looking at importing tens of thousands of support cases from an old help desk system.

I want to store them in a cloned copy of my production (live) help desk in YouTrack. The cloned copy should be visible only to current help desk agents.

If a user submitted a case in our old system years ago, then submits a new one using the same address in YouTrack now, they should not be aware of the existince of the earlier case. The reason is that I cannot guarantee proper ownership of the credential. That is, while user@ibm.com ten years ago may be the same as user@ibm.com today, quacker99@gmail.com could be a different person.

Will this be feasible?

Also, I want to set the Assignee field to reflect the old agent, even though they're no longer agents. Will this be a problem with the quantity of help desk agents allowed to be agents in my production, live help desk project?

0
7 comments

Hello! 

Reporters in YouTrack can only see their own tickets, so ordinarily there would be no visibility issue. The problem is that if someone submits a new ticket using the same email address as an old imported case, YouTrack can merge the old and the new user, and they'd see both the new and historical tickets together.

To prevent this, set the archive project's reporter access to Restricted and leave the authorized reporters list empty. Reporters who aren't explicitly authorized can't access the project at all, so old cases remain invisible to them even if their email matches. If you don't need any helpdesk-specific features in the archive (email channels, online forms), a standard project is an even simpler option. Reporters have no access to standard projects at all.

Setting someone as Assignee on a historical ticket doesn't make them a helpdesk agent. The agent count for your live helpdesk project is determined solely by users who have the Agent user type, which must be explicitly assigned by an admin. Users referenced only as Assignees in archived tickets won't have this type and won't affect your production project's agent count.

If those old agent accounts don't already exist in your YouTrack instance, they'll be created as Standard users during the import (or auto-banned if the standard user license limit is exceeded). Either way, they won't affect the agent count. 

For the import itself: YouTrack has built-in migration tools for some systems (Zendesk, Jira Service Management, GitHub, and others). For other systems, CSV/XLSX or a custom import script would be the way to go.

I hope this helps. Have a nice day! 

0

In fact, setting the old system archive as a regular project is probably the right way to go.

The old system can only be accessed by accessing its' SQL Server database directly. So far I'm using an ETL tool to map from that database to individual JSON files, one per issue. So far they basically look like this. I will need the import script to read each JSON file from a directory on the server and create the issue. Does this look okay so far?

```json
[
    {
        "$type": "issue",
        "id": "9-5968",
        "key": "FPX-5968",
        "fields": {
            "summary": {
                "type": "string",
                "value": "The summary of the issue"
            },
            "description": {
                "type": "text",
                "value": "Description:\n\n I'm having a problem with your product."
            },
            "author": "joe@customer.test",
            "created": "2006-06-20T21:27:47",
            "State": {
                "type": "string",
                "value": "Resolved"
            },
            "Assignee": {
                "value": "alice.smith@ourcompany.test"
            },
            "comments": [
                {
                    "author": "alice.smith@ourcompany.test",
                    "timestamp": "2006-06-22T16:15:32",
                    "text": "Dear Joe,\n\nThank you for letting us know."
                },
                {
                    "author": "joe@customer.test",
                    "timestamp": "2006-06-22T17:41:33",
                    "text": "Hey, Alice, that worked. Thanks!"
                },
                {
                    "author": "alice.smith@ourcompany.test",
                    "timestamp": "2006-06-26T17:01:49",
                    "text": "Dear Joe, You can fix it by turning it off and on again."
                }
            ],
            "Custom field 1": "foo bar",
            "Query Type": "Bug report",
            "Product": "Microsoft Word"
        }
    },
    {
        "$type$": "attachment",
        "name": "foo.xml",
        "mimeType": "text/xml",
        "content": "PGZvby8+"
    }
]
```

0

Hello! 

A few things to fix:

  • "$type"/"$type$" wrappers: please remove them. The Issue type only has id, key, fields, and history, no type envelope.
  • Comment "timestamp" should be “created”: timestamp is a property of HistoryItem. Comments extend SubItem, which defines the timestamp field as created.
  • State field type: should be {"type": "state", "value": "Resolved"}, not "string"
  • Assignee is missing type: should be {"type": "user", "value": "alice.smith@ourcompany.test"}
  • Attachments — they belong in fields.attachments[] on the issue, not as sibling objects in the array. The filename key is fileName, not name (see Attachment). Binary content doesn't go in the JSON; return it from getAttachmentContent() instead (see AttachmentContentWithMetadata).

I recommend checking the Demo Import Script, as it shows the correct format for different imported items.

I hope this helps. Please feel free to reach out anytime if any questions arise. 

0

I don't see that the Demo Import Script shows attachments. Does it?

What's the intention, that each issue have an array of attachments, then the import script fetches each one from the old system?

0

Hi!

I don't see that the Demo Import Script shows attachments. Does it?

You're right, the demo client doesn't actually exercise attachments. It defines a stub getAttachmentContent() method, but the bundled demo-issue.js data file has no attachments field, so the runtime never calls it.

What's the intention, that each issue have an array of attachments, then the import script fetches each one from the old system?

Yes, exactly. Each issue declares its attachments as metadata under fields.attachments[] (id, filename, author, created, mimeType, optional contentUrl), and the runtime calls getAttachmentContent(project, document, attachment) once per attachment to fetch the bytes. The return shape is { data, metadata }, where data is the binary content (base64-encoded string or a response stream) and metadata carries at least the mimeType.

You can find the full field reference here:

I hope this helps. Have a nice day!

0

There seems to be some overlap between Import-Attachment and Import-AttachmentContentWithMetadata. For example, I see mimeType both on Attachment itself as well as within the metadata object of the AttachmentContentWithMetadata.

If one were to add an attachment array to demo-issue or (demo-helpdesk-issue) in your youtrack-demo-client, which of these two forms should the objects take?

{
	"fields": {
		// Attachment
		"attachments": [
			{
				// Is id required?
				"id": "263-Untitled1.zip",
				"fileName": "Untitled1.zip",
				"author": "attachment_author@example.test",
				"created": "2005-08-29T15:25:59Z",
				"mimeType": "application/x-zip-compressed"
			}
		],
		// AttachmentContentWithMetadata
		"attachments": [
			{
				// Is id required?
				"id": "263-Untitled1.zip",
				"fileName": "Untitled1.zip",
				"author": "attachment_author@example.test",
				"created": "2005-08-29T15:25:59Z",
				"metadata": {
					"mimeType": "application/x-zip-compressed"
				}
			}
		]
	}
}
0

Hi!

The two types you're looking at cover different parts of the import flow, so the mimeType overlap is not redundancy. They're separate contract points.

Import-Attachment is what goes in fields.attachments[] on each issue in your data. Here mimeType is a top-level field on the attachment object. This is your Format A:

   attachments: [
     {
       id: "263-Untitled1.zip",
       filename: "Untitled1.zip",
       author: "attachment_author@example.test",
       created: "2005-08-29T15:25:59Z",
       mimeType: "application/x-zip-compressed"
     }
   ]

Import-AttachmentContentWithMetadata is what your getAttachmentContent() method returns, the actual file bytes. Here `mimeType` goes inside a nested `metadata` sub-object. This is your Format B:

   getAttachmentContent(project, document, attachment) {
     return {
       data: "base64-encoded-bytes",
       metadata: {
         mimeType: "application/x-zip-compressed"
       }
     };
   }

On your id question: it is optional. If you omit it, the runtime uses an internal placeholder for that attachment. That's fine for a one-shot historical migration. If you plan to run continuous imports (re-importing updated issues from the source system as they change), providing a stable unique ID per attachment matters, as the runtime uses it to tell which attachments have already been imported. The source system's own attachment ID is the natural choice.

For the full field reference for both types, see the Import API Reference. The Demo Import Script also shows the getAttachmentContent() return structure if that helps as a reference point.

Let me know if you have any more questions!

0

Please sign in to leave a comment.