Migratly migrates Jira to Notion by reading both APIs, deriving the mapping between them, and showing you what it found before anything is written. For this pair that is 8 entity types and 58 field mappings, of which 27 carry across intact and 30 are flagged as lossy, partial, dropped, or needing a route other than the obvious one. 4 of them need a decision from you. The Jira side is read-only throughout — Migratly never writes to or deletes from your source.
How the structure changes
- Each Jira project becomes a Notion database, with the project's fields as database properties.
- Every issue becomes a page in that database. Sub-tasks are pages too, written a wave later.
- Sprints become their own pages, created before the issues that reference them.
- Comments become blocks on the issue's page; attachments become Notion files.
- Issue links become a Notion relation property, resolved in a final pass once every page exists.
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.
| Jira | Notion | Wave | How it is handled | Fields |
|---|---|---|---|---|
| Project | Database | 0 | the collection that everything else lands in | 4 |
| User | User | 0 | accounts matched before anything is written | 4 |
| Sprint | Page | 1 | the records themselves | 5 |
| Issue | Page | 2 | the records themselves | 30 |
| Sub-task | Page | 3 | the records themselves | 6 |
| Attachment | File | 4 | comments, attachments and body content | 3 |
| Comment | Block | 4 | comments, attachments and body content | 3 |
| IssueLink | Relation | 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.
Project → Database
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
name |
title |
Full | |
key |
properties.Project Key |
Full | |
description |
description |
Partial | ADF rich text converts to plain rich_text on the database; some block-level formatting is dropped. |
lead.accountId |
no equivalent | Dropped | Notion databases have no native owner field — project lead is preserved on each migrated page instead. |
User → User
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
accountId |
Source Account ID |
Lossy | Notion users are identified by workspace email — Jira accountId stored only for audit. |
displayName |
Display Name |
Full | |
emailAddress |
Email |
Full | Email is the join key between Jira users and Notion workspace members. |
active |
no equivalent | Dropped | Notion does not expose user active status via API. |
Sprint → Page
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
name |
properties.title |
Full | |
state |
properties.Status |
Full | |
startDate |
properties.Date Range |
Full | |
endDate |
properties.Date Range |
Full | |
goal |
no equivalent | Workaround | Notion has no native goal field — appended as a callout block on the sprint page. |
Issue → Page
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
key |
properties.Issue Key |
Full | |
summary |
properties.title |
Full | |
description |
no equivalent | Partial | ADF rich text converts to Notion blocks — most formatting preserved but some ADF-specific nodes have no Notion equivalent. |
assignee.accountId |
properties.Assignee |
Full | |
reporter.accountId |
no equivalent | Workaround | Notion Pages API cannot set page creator — reporter is prepended as a callout block. |
status.name |
properties.Status |
Partial | Jira workflow statuses may not match Notion select options — enum remapping required. Asks you first |
priority.name |
properties.Priority |
Full | |
labels |
properties.Labels |
Full | |
duedate |
properties.Due |
Full | |
created |
properties.Original Created |
Workaround | Notion created_time is read-only — original date stored as a separate property. |
updated |
no equivalent | Dropped | Notion last_edited_time is read-only — cannot be set or stored meaningfully via API. Asks you first |
issuetype.name |
properties.Type |
Full | |
parent.key |
properties.Parent |
Partial | Notion self-referential relations need the default data_source id of the just-created database (not the database id itself). Two-step PATCH is implemented in the executor but Notion's API requires the data_sources[0].id which we don't yet extract from the create response. Stored as rich_text for now; flip back to relation_remap once that resolution lands. |
customFields |
no equivalent | Pending | Custom fields vary per Jira instance — discovered at migration start. Asks you first |
project.key |
properties.Project Key |
Full | |
creator.accountId |
properties.Original Creator |
Workaround | Notion API cannot set page creator — original creator stored as plain text for audit. |
fixVersions |
properties.Fix Versions |
Full | |
versions |
properties.Affects Versions |
Full | |
components |
properties.Components |
Full | |
resolution.name |
properties.Resolution |
Full | |
resolutiondate |
properties.Resolved |
Full | |
votes.votes |
no equivalent | Dropped | Jira vote counts have no Notion equivalent and rarely carry decision-grade information; deliberately dropped. |
watches.watchCount |
no equivalent | Dropped | Watch counts are not meaningful post-migration; deliberately dropped. |
workratio |
no equivalent | Dropped | Derived field; not meaningful in a migrated system. |
progress |
no equivalent | Dropped | Time-tracking aggregate; covered separately by timeoriginalestimate / timespent if explicitly mapped. |
aggregateprogress |
no equivalent | Dropped | Roll-up of sub-issue progress; reconstructed in target if needed. |
lastViewed |
no equivalent | Dropped | Per-user metadata; not migrated. |
statuscategorychangedate |
no equivalent | Dropped | Derived field updated by Jira on status changes; not meaningful post-migration. |
statusCategory |
no equivalent | Dropped | Derived from status.name — already covered by the status mapping. |
id |
no equivalent | Dropped | Internal Jira numeric ID; key is preserved instead. |
Sub-task → Page
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
key |
properties.Issue Key |
Full | |
summary |
properties.title |
Full | |
description |
no equivalent | Partial | ADF rich text → Notion blocks (same caveats as parent Issue). |
parent.key |
properties.Parent |
Partial | Stored as plain text — same Notion self-relation constraint as Issue.parent.key. See note there. |
assignee.accountId |
properties.Assignee |
Full | |
status.name |
properties.Status |
Partial | Status enum remap required (shared with Issue). Asks you first |
Attachment → File
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
content |
no equivalent | Partial | File content fully migrated but requires two API calls: download from source, attach via external URL block. |
filename |
no equivalent | Full | |
size |
no equivalent | Internal | Used to skip files that exceed Notion's 5MB external file limit. |
Comment → Block
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
body |
no equivalent | Partial | ADF → Notion blocks loses some formatting; comments appear as page blocks, not in a native comment thread. |
author.accountId |
no equivalent | Workaround | Cannot author blocks as another user — author name prepended as text. |
created |
no equivalent | Workaround | Comment timestamp inlined in the header text. |
IssueLink → Relation
| Jira field | Notion field | Coverage | Notes |
|---|---|---|---|
inwardIssue.key |
properties.Related |
Full | |
outwardIssue.key |
properties.Related |
Full | |
type.name |
no equivalent | Dropped | Notion Relations don't support typed links (blocks/clones/duplicates) — all links become generic Relations. |
What does not carry across cleanly
These are the 29 mappings where Notion cannot hold what Jira holds. Each one appears in your coverage report with the same reason, and with the alternative routes available.
accountIdLossy
Notion users are identified by workspace email — Jira accountId stored only for audit.activeDropped
Notion does not expose user active status via API.descriptionPartial
ADF rich text converts to plain rich_text on the database; some block-level formatting is dropped.lead.accountIdDropped
Notion databases have no native owner field — project lead is preserved on each migrated page instead.goalWorkaround
Notion has no native goal field — appended as a callout block on the sprint page.descriptionPartial
ADF rich text converts to Notion blocks — most formatting preserved but some ADF-specific nodes have no Notion equivalent.reporter.accountIdWorkaround
Notion Pages API cannot set page creator — reporter is prepended as a callout block.status.namePartial
Jira workflow statuses may not match Notion select options — enum remapping required.createdWorkaround
Notion created_time is read-only — original date stored as a separate property.updatedDropped
Notion last_edited_time is read-only — cannot be set or stored meaningfully via API.parent.keyPartial
Notion self-referential relations need the default data_source id of the just-created database (not the database id itself). Two-step PATCH is implemented in the executor but Notion's API requires the data_sources[0].id which we don't yet extract from the create response. Stored as rich_text for now; flip back to relation_remap once that resolution lands.creator.accountIdWorkaround
Notion API cannot set page creator — original creator stored as plain text for audit.votes.votesDropped
Jira vote counts have no Notion equivalent and rarely carry decision-grade information; deliberately dropped.watches.watchCountDropped
Watch counts are not meaningful post-migration; deliberately dropped.workratioDropped
Derived field; not meaningful in a migrated system.progressDropped
Time-tracking aggregate; covered separately by timeoriginalestimate / timespent if explicitly mapped.aggregateprogressDropped
Roll-up of sub-issue progress; reconstructed in target if needed.lastViewedDropped
Per-user metadata; not migrated.statuscategorychangedateDropped
Derived field updated by Jira on status changes; not meaningful post-migration.statusCategoryDropped
Derived from status.name — already covered by the status mapping.idDropped
Internal Jira numeric ID; key is preserved instead.descriptionPartial
ADF rich text → Notion blocks (same caveats as parent Issue).parent.keyPartial
Stored as plain text — same Notion self-relation constraint as Issue.parent.key. See note there.status.namePartial
Status enum remap required (shared with Issue).bodyPartial
ADF → Notion blocks loses some formatting; comments appear as page blocks, not in a native comment thread.author.accountIdWorkaround
Cannot author blocks as another user — author name prepended as text.createdWorkaround
Comment timestamp inlined in the header text.contentPartial
File content fully migrated but requires two API calls: download from source, attach via external URL block.type.nameDropped
Notion Relations don't support typed links (blocks/clones/duplicates) — all links become generic Relations.
Decisions you will be asked to confirm
Migratly stops and asks rather than guessing when the right answer depends on your instance.
status.name→properties.Status
Map Jira status values to your Notion Status select options.updated→ unmapped
Decide: drop, or store as a text property 'Original Updated'.customFields→ unmapped
For each custom field: drop, map to an existing Notion property, or create a new property.status.name→properties.Status
Status enum remap required (shared with Issue).
How a Jira to Notion migration runs
- Connect both systems. Jira is connected read-only. Migratly never writes to, archives or deletes anything on the source side.
- Schema discovery. Both APIs are read live, including your custom fields, so the mapping reflects your instance rather than a generic Jira instance.
- Coverage report. All 58 mappings are presented with their coverage. You confirm, reroute or exclude before anything is written.
- Execution in 6 waves. Writes are idempotent and cursor-tracked, so an interrupted run resumes instead of restarting or duplicating.
- Verification. The result is checked back against the source, and every action is in an append-only audit log.
Questions
Can Migratly migrate Jira to Notion?
Yes. Jira to Notion is a supported pair with a mapping covering 8 entity types and 58 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 Jira to Notion?
30 of the 58 field mappings for this pair are flagged rather than clean. For example, `accountId` is marked lossy: Notion users are identified by workspace email — Jira accountId stored only for audit. 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 Jira to Notion 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 Jira data modified during the migration?
No. The source is read-only for the entire run. Migratly never writes to, archives, or deletes anything in Jira, and every action taken against Notion is written to an append-only audit log.
Can Jira issues be migrated into a Notion database?
Yes. The Jira project becomes the database and each issue becomes a row in it, with Jira fields mapped onto Notion properties of a matching type — select for status, multi-select for labels, date for due dates, people for assignees.
Do Jira issue links survive in Notion?
They become a Notion relation property. Because a relation can only point at a page that already exists, links are resolved in a final wave after every issue page has been written, which is also what lets circular links work.
What about Jira attachments in Notion?
Attachments are carried as Notion file properties. Files behind an authenticated Jira URL are fetched during the run rather than linked, so they keep working after the Jira instance is switched off.