

How to Sync Asana Sprints to Slack with Pipedream
Fires a Slack message to the relevant team channel whenever an Asana project milestone changes — sprint starts, completions, and blockers included.
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 sprints in Asana who want Slack announcements without manually posting updates.
Not ideal for
Teams managing sprints entirely inside Slack or Linear — the Asana webhook layer adds setup overhead that won't pay off without active Asana usage.
Sync type
real-timeUse case type
notificationReal-World Example
A 12-person product team at a B2B SaaS company uses this to post sprint kick-off summaries, blocker flags, and completion recaps directly into #sprint-updates on Slack. Before this workflow, the Scrum Master spent 20-30 minutes each sprint boundary copying task counts, capacity notes, and due dates from Asana into Slack by hand. Now the message fires within 15 seconds of the Asana event and includes task counts, assignee capacity, and a direct link to the project.
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 Pipedream
Copy the pre-built Pipedream blueprint and paste it straight into Pipedream. 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 | ||
| Sprint Event Type | resource.resource_type + memberships[0].section.name | |
| Task Name | name | |
| Assignee Name | assignee.name | |
| Section Name | memberships[0].section.name | |
| Project Name | memberships[0].project.name | |
| Slack Target Channel | ||
| Task URL | permalink_url | |
3 optional fields▸ show
| Due Date | due_on |
| Story Points / Effort | custom_fields[?(@.name=='Story Points')].number_value |
| Blocker Flag | custom_fields[?(@.name=='Blocked')].enum_value.name |
Step-by-Step Setup
pipedream.com > Dashboard > New Workflow
Create a new Pipedream workflow
Go to pipedream.com and sign in. Click 'New Workflow' in the top-left dashboard. You'll land on the workflow canvas with an empty trigger slot at the top. Give the workflow a name like 'Asana Sprint → Slack' using the pencil icon next to 'Untitled Workflow' at the top of the page. This name shows up in logs and is worth setting before you forget.
- 1Click 'New Workflow' in the left sidebar
- 2Click the pencil icon next to 'Untitled Workflow' and type 'Asana Sprint → Slack'
- 3Click the '+ Add Trigger' block at the top of the canvas
Workflow Canvas > Add Trigger > Asana > New Story (Instant)
Set Asana webhook as the trigger
In the trigger selector, search for 'Asana' and select it. Choose the trigger type 'New Story (Instant)' — this fires on any Asana activity event including task updates, section moves, and custom field changes, which covers sprint start, blocker, and completion events. Connect your Asana account via Connected Accounts when prompted. Pipedream generates a webhook URL immediately and registers it with Asana's Events API automatically — you don't need to touch the Asana API console.
- 1Type 'Asana' in the trigger search box
- 2Select 'New Story (Instant)' from the trigger list
- 3Click 'Connect Asana Account' and authenticate via OAuth
- 4Select your target Asana workspace from the dropdown
- 5Select the specific Asana project that contains your sprints
Workflow Canvas > Trigger > Events Tab
Send a test event from Asana
Open Asana in a new tab and navigate to the project you selected. Move a task to a new section, or add a comment. Return to Pipedream — you should see an event appear under the trigger's 'Events' tab within 15 seconds. Click the event to expand it and inspect the raw payload. You need to confirm the shape of the data before writing transformation logic. Pay attention to the 'type' and 'resource.resource_type' fields — these tell you whether the event is a task move, a comment, or a custom field change.
- 1Open Asana and move a task between sections in your sprint project
- 2Return to Pipedream and click the 'Events' tab on the trigger block
- 3Click the incoming event to expand its JSON payload
- 4Note the values of 'type', 'resource.resource_type', and 'resource.gid'
Workflow Canvas > + Add Step > Run Node.js Code
Add a Node.js step to classify the sprint event
Click '+ Add a Step' below the trigger and choose 'Run Node.js code'. This step reads the Asana event payload and classifies it as a sprint start, completion, blocker, or ignorable event. Use the $ object to access trigger output via steps.trigger.event. Write a switch or if/else block based on the task's section name or custom field value. Return a structured object — event_type, project_name, task_name, assignee, due_date — so the next step can build the Slack message cleanly.
- 1Click '+ Add a Step' below the trigger block
- 2Select 'Run Node.js code' from the step type list
- 3Paste your classification logic into the code editor
- 4Click 'Test' to run the step against the sample event you captured
Workflow Canvas > Node.js Code Step > Code Editor
Fetch full task details from Asana API
Inside the same Node.js step (or a second one), use Pipedream's built-in @pipedream/platform axios or the native fetch to call Asana's Tasks API: GET https://app.asana.com/api/1.0/tasks/{task_gid}?opt_fields=name,assignee.name,due_on,memberships.section.name,custom_fields. Pass your Asana OAuth token from the connected account using auths.asana.oauth_access_token. Extract the section name to determine sprint boundary — teams typically name sections 'Sprint 12 - Active' or 'Done'.
- 1Add an import or require for axios at the top of the code step
- 2Build the API URL using the resource GID from steps.trigger.event.resource.gid
- 3Set the Authorization header to 'Bearer ' + auths.asana.oauth_access_token
- 4Parse the response and extract name, assignee.name, due_on, and memberships[0].section.name
Workflow Canvas > + Add Step > Run Node.js Code
Add conditional logic to route by event type
Add another Node.js step to decide which Slack message template to use based on event_type. Sprint starts get a summary block with team capacity and task count. Completions get a recap with completed vs. incomplete ratio. Blockers get an urgent-formatted message with the assignee tagged. Return a message object with the text, channel name, and any Block Kit JSON you want. Keeping routing logic in its own step makes it easier to debug — you can test each branch independently.
- 1Click '+ Add a Step' below the previous code step
- 2Select 'Run Node.js code'
- 3Reference the classification output using steps.classify.$return_value.event_type
- 4Build a switch statement with cases for sprint_start, sprint_complete, and blocker
- 5Return a message object with fields: channel, text, blocks (optional)
Workflow Canvas > + Add Step > Run Node.js Code
Add Asana project summary lookup for sprint start messages
Sprint start messages need aggregate data — how many tasks are in the sprint, who is assigned to what, and total story points if you use them. Add a Node.js step that calls GET https://app.asana.com/api/1.0/projects/{project_gid}/tasks?opt_fields=assignee.name,custom_fields,completed to pull all tasks in the active section. Count them, group by assignee, and sum any custom field used for story points. This data populates the capacity block in the Slack message.
- 1Add a new Node.js step after the routing step
- 2Call the Asana Projects Tasks API with the project GID from your trigger
- 3Filter the response to only tasks in the active sprint section
- 4Group tasks by assignee.name and count per person
- 5Return an object with total_tasks, by_assignee array, and total_points
channel: {{channel}}
ts: {{ts}}
Workflow Canvas > + Add Step > Slack > Send Message
Connect Slack and send the message
Click '+ Add a Step', search for 'Slack', and select the 'Send Message' action. Connect your Slack workspace via Connected Accounts. Set the Channel field to reference steps.route.$return_value.channel — this lets you dynamically route blockers to #sprint-blockers and summaries to #sprint-updates from the same workflow. Set the Text field to steps.route.$return_value.text. If you built Block Kit JSON in the routing step, paste it into the Blocks field.
- 1Click '+ Add a Step' and search for 'Slack'
- 2Select the 'Send Message' action
- 3Click 'Connect Slack Account' and authorize with your workspace
- 4Set Channel to {{steps.route.$return_value.channel}}
- 5Set Text to {{steps.route.$return_value.text}}
- 6Optionally set Blocks to {{steps.route.$return_value.blocks}} for rich formatting
Workflow Canvas > Workflow Settings > Error Handling
Add error handling and dead-letter logging
Wrap your API calls in try/catch blocks and use $.flow.exit('reason') to halt execution cleanly when the event is not sprint-relevant — like a comment or a like. For genuine errors (API timeouts, auth failures), use console.error() to log context and re-throw so Pipedream marks the execution as failed. This gives you a filterable error log in the Pipedream dashboard. Set up an error notification in Pipedream's workflow settings to email or Slack yourself on failures.
- 1Wrap all fetch/axios calls in try/catch in each code step
- 2Use $.flow.exit('not a sprint event') for ignorable events
- 3Add console.error(error.message, error.response?.data) in catch blocks
- 4Click 'Settings' (gear icon) on the workflow and enable 'Send error notifications'
- 5Set the error notification destination to a Slack channel or email
Workflow Canvas > Deploy > Executions Tab
Deploy and test all three event types
Click 'Deploy' in the top right to activate the workflow. In Asana, manually simulate each event type: move a task into your sprint's active section (sprint start), move one to Done (completion), and add a custom field value indicating a blocker. Check Slack after each action — messages should arrive within 15 seconds. Check the Pipedream executions log to confirm each run completed without errors and that the event_type classification matched what you expected.
- 1Click 'Deploy' in the top right corner of the workflow canvas
- 2Open Asana and move a task into the active sprint section
- 3Check #sprint-updates in Slack for the sprint start message
- 4Move a different task to your 'Done' section and confirm the completion message
- 5Set a blocker custom field on a task and confirm the urgent message in #sprint-blockers
Workflow Canvas > Trigger Step > Filters Tab
Set event filters to suppress noise
Go back to the trigger step and click 'Add filter'. Add a filter rule that only passes events where resource.resource_type equals 'task'. This immediately drops comment events and like events before they reach your code steps, cutting your execution count and cost. You can add additional filters — like requiring the project GID to match a specific value — if multiple Asana projects are in the same workspace and you only want sprint events from one of them.
- 1Click the trigger step to expand its configuration
- 2Click the 'Filters' tab
- 3Click 'Add Filter' and set field to 'resource.resource_type'
- 4Set operator to 'equals' and value to 'task'
- 5Click 'Save' and re-deploy the workflow
Paste this into the classification and task-fetch Node.js step (step 4). It handles the secondary Asana API call to get full task details, classifies the sprint event, assembles the capacity summary for sprint starts, and returns a clean object for the routing step to consume. Replace SPRINT_SECTION_PREFIX with whatever prefix your team uses for active sprint sections — e.g., 'Sprint' or 'Q1-'.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
props: {... expand to see full code
import axios from 'axios';
export default defineComponent({
props: {
asana: {
type: 'app',
app: 'asana',
},
},
async run({ steps, $ }) {
const event = steps.trigger.event;
// Drop non-task events immediately
if (event.resource?.resource_type !== 'task') {
$.flow.exit('Not a task event — skipping');
}
const taskGid = event.resource.gid;
const token = this.asana.$auth.oauth_access_token;
const SPRINT_SECTION_PREFIX = 'Sprint';
const BLOCKER_FIELD_NAME = 'Blocked';
const POINTS_FIELD_NAME = 'Story Points';
// Fetch full task details from Asana
let task;
try {
const res = await axios({
method: 'GET',
url: `https://app.asana.com/api/1.0/tasks/${taskGid}`,
params: {
opt_fields: [
'name',
'assignee.name',
'assignee.email',
'due_on',
'permalink_url',
'memberships.section.name',
'memberships.project.name',
'memberships.project.gid',
'custom_fields.name',
'custom_fields.number_value',
'custom_fields.enum_value.name',
].join(','),
},
headers: { Authorization: `Bearer ${token}` },
});
task = res.data.data;
} catch (err) {
console.error('Failed to fetch task:', err.response?.data || err.message);
throw new Error(`Asana task fetch failed for GID ${taskGid}`);
}
const sectionName = task.memberships?.[0]?.section?.name ?? '';
const projectName = task.memberships?.[0]?.project?.name ?? 'Unknown Project';
const projectGid = task.memberships?.[0]?.project?.gid;
// Classify the event
const blockerField = task.custom_fields?.find(f => f.name === BLOCKER_FIELD_NAME);
const isBlocked = blockerField?.enum_value?.name === 'Yes';
const isDone = sectionName.toLowerCase().includes('done') || sectionName.toLowerCase().includes('complete');
const isActive = sectionName.startsWith(SPRINT_SECTION_PREFIX) && !isDone;
let eventType;
if (isBlocked) eventType = 'blocker';
else if (isDone) eventType = 'sprint_complete';
else if (isActive) eventType = 'sprint_start';
else {
$.flow.exit(`Section '${sectionName}' is not a recognized sprint section — skipping`);
}
// For sprint start events, fetch aggregate capacity data
let capacityData = null;
if (eventType === 'sprint_start' && projectGid) {
const allTasks = [];
let offset = null;
do {
const params = {
project: projectGid,
opt_fields: 'assignee.name,custom_fields.name,custom_fields.number_value,completed',
limit: 100,
};
if (offset) params.offset = offset;
const pageRes = await axios({
method: 'GET',
url: 'https://app.asana.com/api/1.0/tasks',
params,
headers: { Authorization: `Bearer ${token}` },
});
allTasks.push(...pageRes.data.data);
offset = pageRes.data.next_page?.offset ?? null;
} while (offset);
const activeTasks = allTasks.filter(t => !t.completed);
const byAssignee = activeTasks.reduce((acc, t) => {
const name = t.assignee?.name ?? 'Unassigned';
acc[name] = (acc[name] ?? 0) + 1;
return acc;
}, {});
const totalPoints = activeTasks.reduce((sum, t) => {
const pf = t.custom_fields?.find(f => f.name === POINTS_FIELD_NAME);
return sum + (pf?.number_value ?? 0);
}, 0);
capacityData = {
total_tasks: activeTasks.length,
total_points: totalPoints,
by_assignee: Object.entries(byAssignee).map(([name, count]) => ({ name, count })),
};
}
return {
event_type: eventType,
task_name: task.name,
assignee_name: task.assignee?.name ?? 'Unassigned',
assignee_email: task.assignee?.email ?? null,
due_date: task.due_on ?? null,
section_name: sectionName,
project_name: projectName,
permalink: task.permalink_url,
capacity: capacityData,
};
},
});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 Pipedream for this if your team has any developer capacity at all. The webhook from Asana fires within seconds, and the Node.js code steps give you enough control to handle Asana's two-step payload problem — webhook arrives with a GID, you fetch full task details separately — without building a separate backend. Two concrete reasons Pipedream wins here: first, the Connected Accounts system handles OAuth token refresh for both Asana and Slack automatically, which matters because Asana tokens expire. Second, you can add a code step that does the capacity aggregation (loop through all sprint tasks, group by assignee, sum story points) in the same workflow without needing a separate service. If your team doesn't write JavaScript at all, use Make instead — its Asana module handles the two-step fetch pattern with a built-in 'Get Task' module and the iterator module handles pagination without code.
Cost math is straightforward. Pipedream's free tier gives you 10,000 invocations per month. Each Asana event consumes one invocation regardless of how many code steps run. A team doing one 2-week sprint with 30 tasks and moderate activity — moves, blocker flags, completions — will generate roughly 50-150 Asana events per sprint, or 100-300 per month. That's well inside the free tier. At the paid tier ($29/month), you get 100,000 invocations. You'd need an unusually active Asana project to approach that ceiling. Make's free tier gives you 1,000 operations per month, but each step in a Make scenario counts as an operation — a 4-step scenario uses 4 operations per run. At 200 Asana events/month with 4 steps each, that's 800 operations — just under the free limit. Pipedream is cheaper for this workflow once you're past 250 events/month.
Make's Asana module is easier to configure — you pick the project from a dropdown and the 'Watch Tasks' module handles the webhook setup in one click, no payload inspection required. Zapier has an Asana trigger ('Task Changed') but it's polling-based on most plans, which means 5-15 minute delays on blocker alerts. That's a real problem when a blocker is time-sensitive. n8n's Asana node supports webhooks, and if you self-host n8n you pay $0 in platform costs, which matters for teams watching budget. Power Automate has an Asana connector but it's polling, and the flow-building UI makes conditional routing (different messages for different event types) genuinely tedious compared to a few lines of JavaScript. Pipedream is the right call here because the code flexibility and instant webhook processing are exactly what this workflow needs — not because the others are bad.
Three things you'll hit after setup. First, Asana's webhook registration fails silently roughly 15% of the time on the first attempt — no error is surfaced, the trigger just never fires. Re-registering always fixes it. Second, if your Asana project uses the native Sprint feature (available on Business plan), the section structure is different — tasks appear in sprint groups, not regular sections, and the memberships array looks different. The section name classification logic needs adjustment for this. Third, Slack's Block Kit formatting is strict — a single malformed JSON block silently falls back to plain text with no error. Build and validate your Block Kit JSON at Slack's Block Kit Builder before hardcoding it in the workflow.
Ideas for what to build next
- →Add a weekly sprint digest via scheduled trigger — Create a second Pipedream workflow with a scheduled trigger (every Friday at 4pm) that pulls all Asana tasks modified during the sprint week and posts a formatted digest to Slack — separate from the real-time event flow.
- →Sync Slack blocker replies back to Asana — Add a reverse workflow that listens for replies in #sprint-blockers using Slack's Events API webhook, parses the thread, and adds a comment to the corresponding Asana task — so conversation stays linked to the work.
- →Extend to Google Sheets for sprint metrics history — After the Slack message step, add an additional step that appends sprint completion data (date, task count, points delivered, blockers) to a Google Sheet — gives you a historical record for retrospectives without manual tracking.
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