Intermediate~20 min setupDeveloper Tools & Project ManagementVerified April 2026
GitHub logo
Jira logo

How to Sync GitHub Milestones to Jira Sprints with N8n

Automatically create and link Jira tickets when issues are added to GitHub milestones.

Steps and UI details are based on platform versions at time of writing β€” check each platform for the latest interface.

Best for

Development teams who need custom sprint creation logic and already use N8n for other automations.

Not ideal for

Teams wanting plug-and-play setup without code nodes or webhook troubleshooting.

Sync type

real-time

Use case type

sync

Real-World Example

πŸ’‘

A 25-person SaaS development team uses this to automatically create Jira sprints when their PM assigns GitHub issues to milestones during weekly planning. Before automation, developers manually created sprints and copied issue details, taking 15-20 minutes per sprint. Now sprint setup happens instantly when milestones are assigned.

What Will This Cost?

Drag the slider to your expected monthly volume.

/mo
505005K50K

Each platform counts differently β€” Zapier: 1 task per trigger. Make: 1 operation per module per record. n8n: 1 execution per run.

Prices shown for annual billing. Based on published pricing as of April 2026.

Estimated ROI

1000

min saved/mo

$583

labor value/mo

Free

no platform cost

Based on ~2 min manual effort per operation at $35/hr fully loaded labor cost.

Implementation

Skip the setup

Import this workflow directly into n8n

Copy the pre-built n8n blueprint and paste it straight into n8n. All modules, filters, and field mappings are already configured β€” you just need to connect your accounts.

Before You Start

Make sure you have everything ready.

GitHub repository with admin access to create personal access tokens
Jira Software instance (cloud or server) with project admin permissions
N8n instance running (cloud or self-hosted)
At least one Jira board configured for sprint management
GitHub milestones already created for your sprints

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Milestone Titlemilestone.title
Issue Titleissue.title
Issue Numberissue.number
5 optional fieldsβ–Έ show
Milestone Due Datemilestone.due_on
Issue Bodyissue.body
Repository Namerepository.name
Issue Labelsissue.labels
Assigneeissue.assignee.login

Step-by-Step Setup

1

Workflows > New > Triggers > GitHub

Create New Workflow

Start a new N8n workflow and add the GitHub trigger. This will monitor for changes to your repositories and fire when issues are added to milestones.

  1. 1Click 'New Workflow' from the N8n dashboard
  2. 2Drag the GitHub node from the trigger panel onto the canvas
  3. 3Select 'Repository' from the Events dropdown
  4. 4Choose 'Issues' as the specific event to monitor
βœ“ What you should see: You should see a purple GitHub node on your canvas with 'Repository Issues' configured as the trigger.
2

GitHub Node > Credentials > New

Connect GitHub Account

Authenticate with GitHub using a personal access token. The token needs repo and issues permissions to read milestone data and issue assignments.

  1. 1Click 'Create New Credential' in the GitHub node
  2. 2Select 'GitHub OAuth2 API' from the credential type dropdown
  3. 3Generate a token at github.com/settings/tokens with 'repo' scope checked
  4. 4Paste the token into the 'Access Token' field and click 'Connect'
βœ“ What you should see: Green 'Connected' status appears next to your GitHub credential with your username displayed.
⚠
Common mistake β€” Don't use a classic token - use the new fine-grained tokens for better security and granular repository access.
n8n settings
Connection
Choose a connection…Add
click Add
GitHub
Log in to authorize
Authorize n8n
popup window
βœ“
Connected
green checkmark
3

Nodes > Logic > IF

Filter for Milestone Events

Add an IF node to filter only events where issues are assigned to milestones. This prevents the workflow from firing on every issue change.

  1. 1Add an IF node after the GitHub trigger
  2. 2Set the condition to 'action' equals 'assigned'
  3. 3Add a second condition with 'milestone' is not 'null'
  4. 4Connect both conditions with AND logic
βœ“ What you should see: The IF node shows two conditions connected with AND, filtering for milestone assignments only.
⚠
Common mistake β€” GitHub sends both 'assigned' and 'milestoned' events - using just 'assigned' catches issues added to existing milestones and newly milestoned issues.
GitHub
GI
trigger
filter
Condition
matches criteria?
yes β€” passes through
no β€” skipped
Jira
JI
notified
4

Nodes > Data > Set

Extract Milestone Data

Use a Set node to pull milestone title, due date, and description from the GitHub payload. These fields will map to Jira sprint properties.

  1. 1Add a Set node after the IF node's 'true' branch
  2. 2Add field 'milestone_title' with value '{{$node["GitHub Trigger"].json["issue"]["milestone"]["title"]}}'
  3. 3Add field 'milestone_due' with value '{{$node["GitHub Trigger"].json["issue"]["milestone"]["due_on"]}}'
  4. 4Add field 'issue_title' and 'issue_body' from the GitHub issue data
βœ“ What you should see: Set node shows 4 mapped fields with green checkmarks indicating valid JSON paths.
5

Nodes > Apps > Jira

Search Existing Jira Sprints

Add a Jira node to check if a sprint already exists for this milestone. This prevents creating duplicate sprints when multiple issues are added.

  1. 1Drag a Jira node onto the canvas after the Set node
  2. 2Choose 'Search' as the operation
  3. 3Set Resource to 'Sprint'
  4. 4Use JQL query: 'sprint.name ~ "{{$node["Set"].json["milestone_title"]}}'
βœ“ What you should see: Jira node configured to search sprints by milestone title with JQL syntax validated.
⚠
Common mistake β€” Jira's sprint search is case-sensitive - consider adding a lowercase transformation if your milestone titles vary in capitalization.
6

Jira Node > Credentials > New

Connect Jira Credentials

Set up Jira authentication using your instance URL and API token. N8n supports both cloud and server instances with different auth methods.

  1. 1Click 'Create New Credential' in the Jira node
  2. 2Select 'Jira Software Cloud API' for cloud instances
  3. 3Enter your Jira domain (yourcompany.atlassian.net)
  4. 4Generate an API token from id.atlassian.com and paste it with your email
βœ“ What you should see: Jira credential shows 'Connected' with your instance URL and user email displayed.
⚠
Common mistake β€” Server instances use username/password auth while cloud requires API tokens - don't mix the credential types.
7

Nodes > Logic > IF

Add Sprint Creation Logic

Use another IF node to create a new Jira sprint only if the search returned no results. This handles the create-or-link logic cleanly.

  1. 1Add an IF node after the Jira search
  2. 2Set condition to check if search results array length equals 0
  3. 3Expression: '{{$node["Jira"].json.length === 0}}'
  4. 4Label the true branch 'Create Sprint' and false branch 'Use Existing'
βœ“ What you should see: IF node with length check configured, showing two output branches for create vs existing logic.
8

Create Sprint Branch > Jira > Create

Create New Jira Sprint

On the 'true' branch, add a Jira node to create a sprint using the milestone data. Map the milestone due date to the sprint end date.

  1. 1Add a Jira node to the 'Create Sprint' branch
  2. 2Set Operation to 'Create' and Resource to 'Sprint'
  3. 3Map Sprint Name to '{{$node["Set"].json["milestone_title"]}}'
  4. 4Set End Date to '{{$node["Set"].json["milestone_due"]}}' if not null
βœ“ What you should see: Jira create node configured with milestone title as sprint name and due date mapped.
⚠
Common mistake β€” Jira requires an existing board ID for sprint creation - make sure your API credentials have access to the target board.
9

Both Branches > Set > Merge

Get Sprint ID

Add Set nodes on both branches to normalize the sprint ID value. The create branch gets it from the new sprint, the existing branch from search results.

  1. 1Add Set node after 'Create Sprint' with field 'sprint_id' = '{{$node["Jira"].json["id"]}}'
  2. 2Add Set node after 'Use Existing' with field 'sprint_id' = '{{$node["Jira"].json[0]["id"]}}'
  3. 3Add a Merge node to combine both branches
  4. 4Connect both Set nodes to the Merge node
βœ“ What you should see: Two Set nodes feeding into a Merge node, both outputting a normalized sprint_id field.
⚠
Common mistake β€” The existing sprint uses array index [0] since search returns an array - if multiple sprints match, this takes the first one.
10

Merge > Jira > Create Issue

Create Jira Issue

Add a final Jira node to create the issue in the identified sprint. Map GitHub issue data to Jira fields and assign it to the sprint.

  1. 1Add a Jira node after the Merge
  2. 2Set Operation to 'Create' and Resource to 'Issue'
  3. 3Map Summary to GitHub issue title
  4. 4Set Sprint field to '{{$node["Merge"].json["sprint_id"]}}'
  5. 5Map Description to GitHub issue body
βœ“ What you should see: Jira issue creation node with GitHub fields mapped and sprint assignment configured.
⚠
Common mistake β€” Sprint assignment happens during issue creation - you can't easily move issues between sprints via API later.
11

Workflow > Activate > Executions

Test the Workflow

Activate the workflow and test with a real GitHub issue assignment. Check both the create-new-sprint and use-existing-sprint paths.

  1. 1Click 'Activate' toggle in the top right
  2. 2Go to GitHub and assign an issue to a milestone
  3. 3Return to N8n and check the execution log
  4. 4Verify the Jira issue was created in the correct sprint
βœ“ What you should see: Execution log shows successful run with green checkmarks on all nodes and new Jira issue visible.
⚠
Common mistake β€” Test with a non-production milestone first - the workflow will create real sprints and issues in Jira.

Drop this into an n8n Code node.

JavaScript β€” Code Node// Clean milestone title for Jira sprint naming
β–Έ Show code
// Clean milestone title for Jira sprint naming
const milestoneTitle = items[0].json.milestone_title;
const cleanTitle = milestoneTitle.replace(/[^a-zA-Z0-9\s-]/g, '').trim();

... expand to see full code

// Clean milestone title for Jira sprint naming
const milestoneTitle = items[0].json.milestone_title;
const cleanTitle = milestoneTitle.replace(/[^a-zA-Z0-9\s-]/g, '').trim();
const sprintName = `Sprint: ${cleanTitle}`;
return [{json: {sprint_name: sprintName}}];
n8n
β–Ά Run once
executed
βœ“
GitHub
βœ“
Jira
Jira
πŸ”” notification
received

Scaling Beyond 50+ milestone assignments per day+ Records

If your volume exceeds 50+ milestone assignments per day records, apply these adjustments.

1

Batch Issue Processing

Group multiple issues assigned to the same milestone within a 5-minute window using N8n's wait node. This reduces duplicate sprint searches and API calls to Jira.

2

Cache Sprint Lookups

Store created sprint IDs in N8n's database using the sticky notes feature or external cache. This eliminates repeated Jira searches for popular milestones.

3

Rate Limit Handling

Add exponential backoff retry logic using code nodes when hitting Jira's 300 requests per minute limit. GitHub webhooks will retry failed deliveries automatically.

Going live

Production Checklist

Before you turn this on for real, confirm each item.

Troubleshooting

Common errors and how to fix them.

Frequently Asked Questions

Common questions about this workflow.

Analysis

VerdictWhy n8n for this workflow

Use N8n for this if you need custom data transformation between GitHub milestones and Jira sprints, or if you're already running N8n self-hosted. The code nodes let you handle complex milestone-to-sprint mapping logic that Zapier can't touch. Skip N8n if you want zero maintenance - Make's visual debugging is better for non-technical team members who need to troubleshoot.

Cost

This workflow uses 4-6 executions per GitHub issue assignment. At 100 milestone assignments monthly, that's 500 executions total. N8n's starter plan at $20/month covers 2,500 executions easily. Make would cost $9/month for the same volume, and Zapier starts at $20 but limits you to 750 tasks. Make wins on price, but barely.

Tradeoffs

Make's GitHub integration includes a specific 'Issue added to milestone' trigger that's cleaner than N8n's generic webhook filtering. Zapier's Jira connector handles sprint creation more elegantly with built-in duplicate detection. But N8n's code nodes let you implement custom sprint naming conventions and complex milestone-to-sprint mappings that the other platforms can't handle without multiple scenarios.

GitHub's webhook payload structure changes between issue events - milestone assignments include different fields than regular issue updates. Jira's sprint API requires a board ID for creation, but GitHub milestones don't map to boards automatically. You'll need to hardcode the board ID or build lookup logic. The sprint search JQL is picky about exact name matches, so milestone titles with special characters break the duplicate detection.

Ideas for what to build next

  • β†’
    Add Slack Sprint Notifications β€” Send a message to your dev team channel when new sprints are created with issue counts and due dates.
  • β†’
    Sync Issue Status Changes β€” Build a reverse workflow that updates GitHub issue labels when Jira tickets move through workflow states.
  • β†’
    Auto-assign Issues to Developers β€” Match GitHub issue assignees to Jira users and automatically assign tickets based on team capacity or expertise areas.

Related guides

Was this guide helpful?
← GitHub + Jira overviewn8n profile β†’