← All migration pairs
Migration guide Generated from the live connector spec

Migrate Linear to Jira

Jira is project-centric and unusually restrictive over REST, so several natural mappings run as deliberate workarounds.

11entity types
45field mappings
28carry across intact
16flagged before the run

Migratly migrates Linear to Jira by reading both APIs, deriving the mapping between them, and showing you what it found before anything is written. For this pair that is 11 entity types and 45 field mappings, of which 28 carry across intact and 16 are flagged as lossy, partial, dropped, or needing a route other than the obvious one. 10 of them need a decision from you. The Linear side is read-only throughout — Migratly never writes to or deletes from your source.

How the structure changes

What maps to what

Entities are written in dependency waves. Everything at wave 0 exists before anything at wave 1 is created, which is what makes cross-references resolvable and the whole run resumable from a cursor.

LinearJiraWaveHow it is handledFields
Team Project 0 the collection that everything else lands in 3
User User 0 accounts matched before anything is written 4
Cycle Sprint 1 created up front, before the records that reference it 5
Label Label 1 created up front, before the records that reference it 1
LinearProject Epic 1 created up front, before the records that reference it 3
WorkflowState Status 1 created up front, before the records that reference it 2
Issue Issue 2 the records themselves 12
Sub-task Issue 3 the records themselves 7
Attachment Attachment 4 comments, attachments and body content 1
Comment Comment 4 comments, attachments and body content 4
IssueRelation IssueLink 5 links between records, resolved last 3

Field-by-field coverage

Every mapping carries one of five labels. Full means it carries across intact. Partial means the value survives with a narrowed range. Lossy means something is lost. Workaround means there is no native slot and it is carried another way. Dropped means the target will not accept it. Anything short of full is shown to you before the run, not discovered afterwards.

Team → Project

Linear fieldJira fieldCoverageNotes
name name Full Reuse/pin an existing Jira project is the supported path. Fresh creation via REST needs admin + projectTypeKey + leadAccountId.
key key Partial Linear team keys may exceed Jira's 10-char limit and must be uppercase A-Z/0-9. alnum + upper + hard-clip to 10 produces a valid candidate; uniqueness is a human decision. Asks you first
description description Full

User → User

Linear fieldJira fieldCoverageNotes
id Source Linear ID Lossy Jira users are keyed by accountId; the Linear UUID is kept only for audit. user_map is built source_id -> target accountId via email join.
email emailAddress Full Email is the join key. The Linear member must already exist in the target Jira site with the same email.
name displayName Full
active active Lossy Jira user active status is read-only via REST; a Jira admin must provision/deactivate accounts.

Cycle → Sprint

Linear fieldJira fieldCoverageNotes
number name Full Linear cycle name -> Jira sprint name (required by Jira). source_field is 'number' (always present) so the transform always runs; it returns the cycle name, or falls back to 'Cycle <number>' for Linear's auto-cycles that have no name.
startsAt startDate Full
endsAt endDate Full
team.key originBoardId Full Sprint must be created on the project's Scrum board. The board id was resolved into id_map (Board:{collection_key}) by the sprint pre_populate; this looks it up. No value transform needed — collection_key drives the key.
state Partial Jira creates sprints in 'future' state. A generic post-create follow-up (declared in the connector spec) activates the sprint whose cycle is currently in progress: now within startsAt..endsAt and not completed. Jira allows one active sprint per board, so this cond yields 'active' for at most one; everything else stays 'future'. The follow-up request itself is spec-driven; only the value is computed here.

Label → Label

Linear fieldJira fieldCoverageNotes
name name Full Jira labels are free-form strings set directly on issues; no creation API. Informational only. Labels are applied at issue level from the issue's embedded labels.

LinearProject → Epic

Linear fieldJira fieldCoverageNotes
name fields.summary Full Linear Project -> Jira Epic (an issue with issuetype Epic). Created at level 1 so issues can link to it via fields.parent. Requires the target project's scheme to include the 'Epic' type; otherwise creation is skipped (non-fatal).
description fields.description Partial Linear project description (Markdown) -> Jira ADF.
state transition Workaround Linear project state -> Jira epic status via a follow-up transition (best-effort; only applies if a same-named status exists in the epic workflow).

WorkflowState → Status

Linear fieldJira fieldCoverageNotes
name name Workaround Jira statuses are read-only via REST. This entity has no create route; it pre-populates the name->status id map (SchemaByName:status) from the target project so the Issue status transition can resolve. Unmatched states keep the issue in its default status. Asks you first
type statusCategory Full Linear state type suggests the Jira statusCategory for the human-decision UI.

Issue → Issue

Linear fieldJira fieldCoverageNotes
title fields.summary Full
description fields.description Partial Linear Markdown -> Jira ADF via the markdown_to_adf formatter. Headings/lists/links survive; Linear inline embeds and reactions are lost.
fields.issuetype.name fields.issuetype.name Full Jira requires an issuetype on create. Default Linear issues to 'Task'; the human-decision UI can override. Asks you first
priority fields.priority.name Full Linear priority int (0=None 1=Urgent 2=High 3=Medium 4=Low) -> Jira priority name. None -> omitted.
assignee.id fields.assignee.accountId Full Asks you first
labels.nodes fields.labels Full Linear labels embedded on the issue -> Jira free-form label strings. Spaces are collapsed to hyphens (Jira rejects spaces in labels).
state.name transition Full Jira does not accept status on create. property_type 'transition' keeps this OUT of the create body; after creation the executor runs a follow-up POST /transitions that matches the Linear state name to the target Jira status. Supply a value_map on the route if the target workflow uses different status names. Asks you first
dueDate fields.duedate Full
estimate fields.customfield_10016 Partial Jira story points use a per-instance custom field (customfield_10016 by convention). Asks you first
cycle.name fields.customfield_10020 Partial Linear cycle -> Jira sprint membership via the Sprint custom field (customfield_10020 by convention). Resolves the sprint id created from the cycle. Asks you first
project.id fields.parent.key Full Linear Project -> Jira Epic link. Modern Jira links a standard issue to its epic via fields.parent (the epic created from the Linear Project at level 1). Resolves the epic key from Schema:epic; omitted when the issue has no project or the epic wasn't created (e.g. target lacks the Epic type).
createdAt fields.created Dropped Jira's created timestamp is read-only and set to now on creation.

Sub-task → Issue

Linear fieldJira fieldCoverageNotes
title fields.summary Full
description fields.description Partial Linear Markdown -> Jira ADF.
parent.id fields.parent.key Full Linear sub-issue parent -> Jira parent key. The parent issue is created first (dependency ordering).
fields.issuetype.name fields.issuetype.name Full Sub-tasks require a sub-task-type issue type in Jira. Asks you first
assignee.id fields.assignee.accountId Full Asks you first
priority fields.priority.name Full
state.name transition Full Status applied via follow-up POST /transitions after create (see Issue); kept out of the create body. Asks you first

Attachment → Attachment

Linear fieldJira fieldCoverageNotes
url file Full Linear file attachment -> Jira attachment. The executor downloads the asset from Linear (with auth) and multipart-uploads it to the issue (content.attachment.create.multipart). url/filename/content-type fields are spec-declared.

Comment → Comment

Linear fieldJira fieldCoverageNotes
issueId issueId Internal The executor resolves the parent issue's Jira key from id_map and puts it in the URL path; no body field needed.
body body Partial Linear Markdown -> Jira ADF. Jira sets comment author=API user and created=now; original author/timestamp are not preserved in Phase 2 (Phase 3 will prepend them).
user Original Author Dropped Jira sets comment author = the API user; preserving the original author requires prepending to the ADF body (Phase 3).
createdAt Original Timestamp Dropped Jira sets comment created = now; original timestamp preservation requires prepending to the body (Phase 3).

IssueRelation → IssueLink

Linear fieldJira fieldCoverageNotes
issueId inwardIssue.key Full
relatedIssueId outwardIssue.key Full
type type.name Partial Linear relation types (blocks/duplicate/related) -> Jira link types. Custom Jira link types may need pre-creation.

What does not carry across cleanly

These are the 16 mappings where Jira cannot hold what Linear holds. Each one appears in your coverage report with the same reason, and with the alternative routes available.

Decisions you will be asked to confirm

Migratly stops and asks rather than guessing when the right answer depends on your instance.

How a Linear to Jira migration runs

  1. Connect both systems. Linear is connected read-only. Migratly never writes to, archives or deletes anything on the source side.
  2. Schema discovery. Both APIs are read live, including your custom fields, so the mapping reflects your instance rather than a generic Linear instance.
  3. Coverage report. All 45 mappings are presented with their coverage. You confirm, reroute or exclude before anything is written.
  4. Execution in 6 waves. Writes are idempotent and cursor-tracked, so an interrupted run resumes instead of restarting or duplicating.
  5. Verification. The result is checked back against the source, and every action is in an append-only audit log.

Questions

Can Migratly migrate Linear to Jira?

Yes. Linear to Jira is a supported pair with a mapping covering 11 entity types and 45 fields. You connect both systems, Migratly reads the live schemas on each side, and it produces a coverage report you approve before any data moves.

What is lost when migrating from Linear to Jira?

16 of the 45 field mappings for this pair are flagged rather than clean. For example, `id` is marked lossy: Jira users are keyed by accountId; the Linear UUID is kept only for audit. user_map is built source_id -> target accountId via email join. Every flagged mapping appears in the coverage report before the run, with its reason and the routes still open to it. Nothing is dropped silently.

How long does a Linear to Jira migration take?

It is bounded by the two APIs' rate limits rather than by Migratly. This pair runs in 6 dependency waves, starting with account matching and ending with cross-references, and every wave is resumable — a run interrupted halfway continues from its cursor rather than starting over or duplicating what it already wrote.

Is my Linear data modified during the migration?

No. The source is read-only for the entire run. Migratly never writes to, archives, or deletes anything in Linear, and every action taken against Jira is written to an append-only audit log.

Can you migrate Linear issues into an existing Jira project?

Yes. Existing statuses, labels and sprints are matched by name before anything is created, so an import into a live project reuses what is already there instead of duplicating it.

How are Linear cycles represented in Jira?

As sprints on the target board. Jira does not expose sprint creation as a plain REST create, so this mapping runs through the Agile API as a declared workaround rather than a direct field write.

Does Linear to Jira preserve comment authors and timestamps?

Comment bodies and original timestamps carry across. Authorship depends on whether the Linear user has a matching Jira account — matched by email during the user wave. Unmatched authors are named in the comment body instead of being dropped.

Other migration pairs