# Migrate Jira to Notion

> Notion has no issue type, so a Jira project becomes a database and every issue becomes a page inside it.

Source: https://migratly.dev/migrate/jira-to-notion/

Migratly migrates Jira to Notion by reading both APIs live, deriving the mapping between them, and
presenting it for approval before anything is written. This pair covers **8 entity types**
and **58 field mappings**: 27 carry across intact, 30 are flagged as lossy,
partial, dropped or needing another route, and 4 need a decision from you. The Jira side is
read-only for the whole run.

## 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.

## Entity map

| Jira | Notion | Wave | Fields |
|---|---|---|---|
| Project | Database | 0 | 4 |
| User | User | 0 | 4 |
| Sprint | Page | 1 | 5 |
| Issue | Page | 2 | 30 |
| Sub-task | Page | 3 | 6 |
| Attachment | File | 4 | 3 |
| Comment | Block | 4 | 3 |
| IssueLink | Relation | 5 | 3 |

## Field coverage

### 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. |
| `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. |
| `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. |
| `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). |

### 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. |

## FAQ

**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.

---

Start a migration: https://app.migratly.dev/
