

How to Sync Asana Sprints to Slack with n8n
Automatically post sprint start announcements, completion summaries, and blocker alerts to Slack channels when Asana project milestones change.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Engineering or product teams running Asana-based sprints who want Slack announcements without manually posting updates
Not ideal for
Teams using Asana's native Slack integration already — the built-in app handles basic task notifications without custom logic
Sync type
real-timeUse case type
notificationReal-World Example
A 12-person product team at a B2B SaaS company runs two-week sprints in Asana. Before this workflow, the PM posted sprint kickoff and wrap-up messages to #product-sprint manually — often hours late or skipped entirely. After setup, Slack gets an automatic announcement the moment the sprint milestone is marked complete or a task is flagged as blocked, including a live capacity summary pulled from Asana custom fields.
What Will This Cost?
Drag the slider to your expected monthly volume.
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
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.
Field Mapping
Map these fields between your apps.
| Field | API Name | |
|---|---|---|
| Required | ||
| Task GID | events[0].resource.gid | |
| Event Action | events[0].action | |
| Task Name | data.name | |
| Assignee Name | data.assignee.name | |
| Completed Flag | data.completed | |
| Changed Field | events[0].change.field | |
| Slack Channel ID | ||
3 optional fields▸ show
| Due Date | data.due_on |
| Story Points (Custom Field) | data.custom_fields[].display_value |
| Team Capacity % (Custom Field) | data.custom_fields[].display_value |
Step-by-Step Setup
n8n Canvas > + Node > Trigger > Webhook
Create a new n8n workflow and add the Webhook node
Open n8n and click the orange '+ New Workflow' button in the top right. On the blank canvas, click the '+' node icon and search for 'Webhook'. Select the Webhook node — this will be your trigger. n8n generates a unique webhook URL immediately. Copy that URL; you'll paste it into Asana in the next step.
- 1Click '+ New Workflow' in the top right of the n8n dashboard
- 2Click the '+' node placeholder on the canvas
- 3Search for 'Webhook' in the node search bar
- 4Select 'Webhook' under Trigger nodes
- 5Copy the generated webhook URL from the node panel
Asana Developer Console > Personal Access Tokens > Copy Token
Register the webhook in Asana
Asana doesn't have a UI-based webhook setup — you have to call their API directly. Use Asana's Webhooks API (POST to https://app.asana.com/api/1.0/webhooks) with your personal access token in the Authorization header. Set the 'resource' to your Asana project GID and the 'target' to the n8n webhook URL you copied. Asana will send a handshake request immediately to verify the endpoint.
- 1Go to https://app.asana.com/0/developer-console and create a Personal Access Token
- 2Open a terminal or Postman and POST to https://app.asana.com/api/1.0/webhooks
- 3Set Authorization header to: Bearer YOUR_ASANA_TOKEN
- 4Set body: { "data": { "resource": "YOUR_PROJECT_GID", "target": "YOUR_N8N_WEBHOOK_URL" } }
- 5Check the response for a 201 status and note the webhook GID
n8n Canvas > + Node > Flow > Switch
Add a Switch node to route by event type
Asana webhooks fire for many event types — task creation, section changes, due date updates, and more. You only want three: sprint start (a section named 'In Progress' gets a task), sprint completion (milestone task marked complete), and blockers (a tag named 'Blocker' is added). Add an n8n Switch node after the Webhook node to filter these three paths. Each route will lead to a different Slack message format.
- 1Click '+' after the Webhook node and search for 'Switch'
- 2Set the Switch mode to 'Rules'
- 3Add Rule 1: field = {{ $json.events[0].action }}, equals 'changed', AND {{ $json.events[0].change.field }} equals 'completed'
- 4Add Rule 2: field = {{ $json.events[0].resource.resource_type }} equals 'tag' AND action equals 'added'
- 5Add Rule 3: {{ $json.events[0].change.new_value.name }} contains 'In Progress'
n8n Canvas > + Node > Core > HTTP Request
Fetch full task details from Asana API
The Asana webhook payload gives you minimal data — just the resource GID and event type. To build a meaningful Slack message, you need task name, assignee, due date, and custom fields like story points. Add an HTTP Request node on each branch to call GET https://app.asana.com/api/1.0/tasks/{task_gid}?opt_fields=name,assignee.name,due_on,custom_fields,completed. Connect one HTTP Request node per Switch branch.
- 1Click '+' on Switch output 0 and add an HTTP Request node
- 2Set Method to GET
- 3Set URL to: https://app.asana.com/api/1.0/tasks/{{ $json.events[0].resource.gid }}?opt_fields=name,assignee.name,due_on,custom_fields,completed
- 4Under Authentication, choose 'Header Auth', set Name to 'Authorization', Value to 'Bearer YOUR_ASANA_TOKEN'
- 5Duplicate this node and connect copies to Switch outputs 1 and 2
n8n Canvas > + Node > Core > Code
Add a Code node to parse sprint data and build message payloads
Asana custom fields come back as an array of objects, each with a gid and display_value. You need to find the right custom field by name (e.g. 'Story Points', 'Capacity %') rather than by index. Add a Code node after each HTTP Request node to extract these values and construct a clean object for the Slack message. This is where n8n's code node earns its place — no other node handles Asana's custom_fields array cleanly.
- 1Click '+' after the HTTP Request node on the sprint completion branch
- 2Select the 'Code' node
- 3Set language to JavaScript
- 4Paste the transformation code from the Pro Tip section below
- 5Run the node and verify the output shows storyPoints, capacity, taskName, and assignee fields
This Code node runs after the Asana HTTP Request node on any branch. It extracts story points and capacity from Asana's custom_fields array (which comes back as unordered objects), computes sprint summary counts, and returns a clean payload the Slack node can reference directly. Paste it into the Code node on each branch, adjusting the custom field names to match what your Asana project uses.
JavaScript — Code Node// n8n Code Node — Asana Sprint Data Parser▸ Show code
// n8n Code Node — Asana Sprint Data Parser // Runs after HTTP Request node fetching full task details // $input.all() returns array of items from upstream node
... expand to see full code
// n8n Code Node — Asana Sprint Data Parser
// Runs after HTTP Request node fetching full task details
// $input.all() returns array of items from upstream node
const items = $input.all();
const results = [];
for (const item of items) {
const task = item.json.data;
// Asana custom_fields is an unordered array — find by name, not index
const customFields = task.custom_fields || [];
const storyPointsField = customFields.find(
f => f.name === 'Story Points'
);
const capacityField = customFields.find(
f => f.name === 'Capacity %'
);
const storyPoints = storyPointsField
? parseInt(storyPointsField.display_value, 10) || 0
: 0;
const capacity = capacityField
? capacityField.display_value || 'N/A'
: 'N/A';
// Format due date from YYYY-MM-DD to human-readable
const rawDue = task.due_on || null;
const formattedDue = rawDue
? new Date(rawDue + 'T00:00:00').toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric'
})
: 'No due date';
// Pre-build Slack message text so the Slack node stays simple
const isCompleted = task.completed === true;
const emoji = isCompleted ? '✅' : '🚀';
const status = isCompleted ? 'Sprint complete' : 'Sprint started';
const slackText =
`${emoji} ${status}: ${task.name}\n` +
`Owner: ${task.assignee ? task.assignee.name : 'Unassigned'}\n` +
`Due: ${formattedDue}\n` +
`Story Points: ${storyPoints}\n` +
`Team Capacity: ${capacity}`;
results.push({
json: {
taskName: task.name,
assigneeName: task.assignee ? task.assignee.name : 'Unassigned',
dueOn: formattedDue,
storyPoints,
capacity,
isCompleted,
slackText
}
});
}
return results;channel: {{channel}}
ts: {{ts}}
n8n Canvas > + Node > Core > HTTP Request
Fetch sprint-level summary from the Asana project
For sprint completion summaries, you need project-level stats — total tasks, completed count, incomplete count. Add another HTTP Request node calling GET https://app.asana.com/api/1.0/projects/{project_gid}/tasks?opt_fields=completed,name&completed_since=now. This returns only incomplete tasks. Run a second call without the filter to get total count. Wire both calls after the Code node on the completion branch.
- 1Add HTTP Request node after Code node on the 'completed' branch
- 2GET https://app.asana.com/api/1.0/projects/YOUR_PROJECT_GID/tasks?opt_fields=completed,name
- 3Add a second HTTP Request node with the same URL plus &completed_since=now to fetch only incomplete tasks
- 4Connect both nodes into a Merge node set to 'Combine > Merge By Position'
- 5Use an expression in the next Code node to calculate: total - incomplete = completed count
n8n Canvas > + Node > Apps > Slack > Post Message
Configure the Slack node for sprint start announcements
Connect a Slack node to Switch branch 2 (the 'In Progress' section trigger). In n8n, search for the Slack node and select 'Post Message' as the operation. Choose the OAuth2 credential you'll set up in the credentials panel. Set the channel to your sprint channel ID (not the name — use the ID from Slack's channel settings). Build the message text using n8n expressions referencing your Code node output.
- 1Click '+' on the sprint start branch and search for 'Slack'
- 2Select operation 'Post a Message'
- 3Click 'Create New Credential' and authenticate with Slack OAuth2
- 4Set Channel to the Slack channel ID (e.g. C04XXXXXXX)
- 5Set Text to: 🚀 Sprint started: {{ $node['Code'].json.taskName }} | Assignee: {{ $node['Code'].json.assigneeName }} | Due: {{ $node['Code'].json.dueOn }}
n8n Canvas > + Node > Apps > Slack > Post Message
Configure Slack nodes for sprint completion and blocker alerts
Add two more Slack 'Post Message' nodes — one for each remaining Switch branch. For sprint completion, use Slack's Block Kit format to post a structured summary card with task counts, story points completed, and team capacity. For blocker alerts, post to a separate #blockers channel (or the same sprint channel with a 🚧 emoji prefix). Each node references its own upstream Code node output.
- 1Add Slack node to the 'completed' branch, operation 'Post a Message'
- 2Set message body using JSON Blocks field with a Block Kit section block
- 3Add Slack node to the 'blocker' branch, set Channel to #blockers channel ID
- 4Set Text to: 🚧 Blocker flagged: {{ $node['Code'].json.taskName }} | Owner: {{ $node['Code'].json.assigneeName }}
- 5Test each branch independently by manually sending a matching Asana event
n8n Settings > Error Workflow > + New Error Workflow
Add error handling with an Error Trigger node
Sprint notifications that silently fail are worse than no automation. Add an Error Trigger workflow in n8n: go to Settings > Error Workflow and create a separate workflow that catches failures from this one. The Error Trigger node captures the error message, the node name that failed, and the execution ID. Wire it to a Slack node that posts to a #dev-alerts channel so someone knows immediately when the Asana webhook or API call breaks.
- 1Click the workflow name at the top of your canvas to open workflow settings
- 2Scroll to 'Error Workflow' and click 'Create Error Workflow'
- 3On the new canvas, add an Error Trigger node
- 4Add a Slack node after it: Text = '❌ Sprint sync failed at node: {{ $json.failedNode }} | Error: {{ $json.errorMessage }}'
- 5Save and link this workflow back to the main workflow's Error Workflow setting
n8n Canvas > Workflow Active Toggle (top right)
Activate the workflow and switch to the Production webhook URL
During testing you used the Test webhook URL. Now click the toggle in the top right of n8n to activate the workflow. Once activated, n8n switches to the Production webhook URL automatically — but Asana is still pointed at the Test URL. Go back to the Asana Webhooks API, delete the old webhook using DELETE https://app.asana.com/api/1.0/webhooks/{webhook_gid}, and re-register using the Production URL from n8n's Webhook node settings.
- 1Click the 'Inactive' toggle in the top right to set workflow to 'Active'
- 2Open the Webhook node and copy the Production URL (not the Test URL)
- 3DELETE the old Asana webhook via API: DELETE https://app.asana.com/api/1.0/webhooks/OLD_GID
- 4Re-register a new Asana webhook with the Production URL
- 5Trigger a real Asana event (mark a task complete) and confirm the Slack message arrives
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
Use n8n for this if your team is already self-hosting infrastructure and wants full control over the webhook logic without paying per-task fees. n8n's Code node is the real reason to pick it here — Asana's custom_fields array is genuinely messy to parse without code, and doing it in a no-code tool means building fragile workarounds. If your team has no one comfortable running JavaScript and no interest in maintaining a self-hosted instance, Make handles this workflow adequately with its HTTP module and array iterator, and costs $9/month for the Teams plan.
On n8n Cloud, this workflow costs roughly $0 in execution fees for typical sprint volumes — n8n charges by workflow executions, and a 2-week sprint with daily Asana updates might trigger 200-400 executions per month. The Starter plan at $20/month covers 2,500 executions. If you're self-hosting, your only cost is server compute, which on a $5/month VPS is effectively nothing. Compare that to Zapier, where each Zap step counts as a task: a 3-step workflow at 400 runs/month burns 1,200 tasks, hitting the $19.99/month Professional plan minimum almost immediately.
Make beats n8n for visual conditional branching — its scenario builder shows all three sprint event paths on one canvas with labeled routers, which is easier to hand off to a non-developer. Zapier has a native Asana 'Task Completed' trigger that requires zero API setup, cutting 30 minutes off initial configuration. Power Automate has a pre-built Asana connector with a 'When a task is completed' trigger, which is useful if your org is already on Microsoft 365 — no webhook registration needed. Pipedream's Asana source handles the webhook handshake automatically and gives you a typed event schema, which removes one common failure point. None of that changes the recommendation: n8n is right here because the custom_fields parsing and multi-branch routing genuinely benefit from a real code node, and the others make you fight their abstractions to get there.
Three things you'll hit after setup. First, Asana's webhook handshake is fragile — any deployment restart that takes your n8n URL offline for more than a few seconds won't break the webhook, but three consecutive failed deliveries will cause Asana to silently deactivate it. Build a weekly cron job that calls the Asana webhooks API to verify active status. Second, Slack's Block Kit layout breaks on mobile if your sprint summary text is too long — keep task name fields under 80 characters or Slack truncates them with an ellipsis that cuts off the important numbers. Third, Asana's API rate limit is 150 requests per minute per user token — if multiple sprints close simultaneously (end of quarter), parallel HTTP Request calls from multiple executions will hit 429s. Add a 500ms Wait node before each API call during the first week to see if this is a problem before it becomes one.
Ideas for what to build next
- →Add a weekly sprint digest — Set up a second n8n workflow on a scheduled trigger (every Friday at 4pm) that calls the Asana projects API, counts completed vs open tasks, and posts a formatted weekly summary to the sprint channel.
- →Create a Slack slash command to query sprint status on demand — Add a second n8n webhook that receives a Slack slash command like /sprint-status, calls the Asana API for current sprint data, and responds with live capacity and task counts directly in Slack.
- →Route blockers to a Jira ticket automatically — Extend the blocker branch to also create a Jira issue via the Jira node in n8n, linking it back to the Asana task GID so blockers are tracked in both systems without double entry.
Related guides
How to Share Notion Meeting Notes to Slack with Pipedream
~15 min setup
How to Share Notion Meeting Notes to Slack with Power Automate
~15 min setup
How to Share Notion Meeting Notes to Slack with n8n
~20 min setup
How to Send Notion Meeting Notes to Slack with Zapier
~8 min setup
How to Share Notion Meeting Notes to Slack with Make
~12 min setup
How to Create Notion Tasks from Slack with Pipedream
~15 min setup