

How to Convert Slack Messages to Asana Tasks with Pipedream
When a Slack message gets a specific emoji reaction or contains a keyword, Pipedream instantly creates an Asana task with the message text, sender, and channel as context.
Steps and UI details are based on platform versions at time of writing — check each platform for the latest interface.
Best for
Engineering and product teams that triage work inside Slack and want tasks created without leaving the conversation.
Not ideal for
Teams that need bidirectional sync — if you want Asana task updates to post back to Slack, this one-way setup won't cover that.
Sync type
real-timeUse case type
routingReal-World Example
A 20-person product team uses this so that any message reacted with 📌 in #product-feedback instantly becomes an Asana task in their 'Feedback Backlog' project. Before this, a PM manually copied Slack messages into Asana at the end of each day — roughly 15-20 messages — and context like who flagged it and when was always lost.
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 | ||
| Task Name | name | |
| Task Notes / Description | notes | |
| Project GID | projects | |
6 optional fields▸ show
| Slack Channel Name | |
| Flagged By (User Display Name) | |
| Slack Message Permalink | |
| Reaction Emoji | |
| Due Date | due_on |
| Assignee GID | assignee |
Step-by-Step Setup
pipedream.com > Workflows > New Workflow
Create a new Pipedream workflow
Go to pipedream.com and sign in. Click 'New Workflow' in the top right of the Workflows dashboard. You'll land on a blank canvas with a trigger placeholder at the top. This is where you'll connect Slack as the event source. Give the workflow a name like 'Slack Reaction → Asana Task' so it's easy to find later.
- 1Sign in at pipedream.com
- 2Click 'New Workflow' in the top-right corner
- 3Click the trigger placeholder labeled 'Add a trigger'
- 4Type 'Slack' in the search box and select the Slack app
Trigger panel > Select Event > New Reaction Added
Configure the Slack trigger for emoji reactions
In the Slack trigger panel, select 'New Reaction Added' as the trigger event. This fires instantly every time any user adds an emoji reaction to any message in your workspace. Connect your Slack account via 'Connect Account' — Pipedream will open an OAuth popup to authorize access. After connecting, set the 'Reaction' filter field to the specific emoji you want to use, such as 'white_check_mark' or 'pushpin' (enter the emoji name without colons).
- 1Select 'New Reaction Added' from the trigger event dropdown
- 2Click 'Connect Account' and authorize Slack via OAuth popup
- 3In the 'Reaction' field, type the emoji name (e.g., pushpin) without colons
- 4Click 'Save and continue' to lock in the trigger
Trigger panel > Generate Test Event
Test the trigger to capture a real Slack event
Click 'Generate Test Event' in the trigger panel. Pipedream will listen for an incoming reaction event from your Slack workspace. Go to any Slack channel, find a message, and add the emoji reaction you configured. Within 2-3 seconds, Pipedream will capture the event and display the raw JSON payload in the trigger output panel. Confirm that the payload includes 'item.channel', 'item.ts', 'reaction', and 'user' fields.
- 1Click 'Generate Test Event' in the Slack trigger panel
- 2Switch to Slack and add your configured emoji to any message
- 3Return to Pipedream and wait 2-3 seconds for the event to appear
- 4Click the event in the panel to expand and review the JSON payload
Workflow canvas > + Add Step > Run Node.js code
Add a Node.js step to fetch the Slack message text
Click the '+' button below the trigger to add a new step. Select 'Run Node.js code' from the step options. This step will call the Slack API's 'conversations.history' method using the channel ID and message timestamp from the trigger event to retrieve the full message text. You'll use the Slack OAuth token from your connected account, which Pipedream exposes as an environment variable you can reference in code.
- 1Click the '+' button below the Slack trigger
- 2Select 'Run Node.js code' from the step type list
- 3Paste the fetch code into the code editor (see Pro Tip below)
- 4Click 'Test' to run the step against your test event
channel: {{channel}}
ts: {{ts}}
Workflow canvas > + Add Step > Run Node.js code
Add a step to resolve the Slack user ID to a display name
The reaction event gives you a user ID like 'U04ABCD12', not a readable name. Add another Node.js step that calls 'users.info' on the Slack API with that user ID. This returns the user's real name and display name so you can include a human-readable 'Flagged by' field in the Asana task. Without this step, your task descriptions will contain raw IDs that teammates won't recognize.
- 1Click '+' below the previous step to add another code step
- 2Write a fetch call to 'https://slack.com/api/users.info' with the user ID from the trigger
- 3Extract 'user.real_name' or 'user.profile.display_name' from the response
- 4Click 'Test' to verify the name resolves correctly
This single Node.js step replaces three separate API steps by fetching the message text, user display name, and channel name in one sequential async block, then assembling the full Asana task payload. Paste this as a single 'Run Node.js code' step immediately after the Slack trigger, replacing steps 4, 5, 6, and 7 in the guide above.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {
const token = process.env.SLACK_BOT_TOKEN;
const channelId = steps.trigger.event.item.channel;
const messageTs = steps.trigger.event.item.ts;
const reactorUserId = steps.trigger.event.user;
// Fetch message text
const historyRes = await axios.get('https://slack.com/api/conversations.history', {
headers: { Authorization: `Bearer ${token}` },
params: { channel: channelId, latest: messageTs, limit: 1, inclusive: true },
});
if (!historyRes.data.ok) {
throw new Error(`conversations.history failed: ${historyRes.data.error}`);
}
const messageText = historyRes.data.messages?.[0]?.text ?? '(no message text)';
// Fetch reactor display name
const userRes = await axios.get('https://slack.com/api/users.info', {
headers: { Authorization: `Bearer ${token}` },
params: { user: reactorUserId },
});
if (!userRes.data.ok) {
throw new Error(`users.info failed: ${userRes.data.error}`);
}
const displayName =
userRes.data.user?.profile?.display_name ||
userRes.data.user?.real_name ||
'Unknown User';
// Fetch channel name
const chanRes = await axios.get('https://slack.com/api/conversations.info', {
headers: { Authorization: `Bearer ${token}` },
params: { channel: channelId },
});
if (!chanRes.data.ok) {
throw new Error(`conversations.info failed: ${chanRes.data.error}`);
}
const channelName = chanRes.data.channel?.name ?? channelId;
// Build Slack permalink (remove period from timestamp)
const tsForUrl = messageTs.replace('.', '');
const permalink = `https://slack.com/archives/${channelId}/p${tsForUrl}`;
// Truncate task name to 100 chars
const taskName =
messageText.length > 100 ? messageText.slice(0, 97) + '...' : messageText;
// Assemble Asana task notes
const notes = [
`📌 Flagged by ${displayName} in #${channelName}`,
'',
'Full message:',
messageText,
'',
`View in Slack: ${permalink}`,
].join('\n');
return {
taskName,
notes,
permalink,
channelName,
displayName,
};
},
});Workflow canvas > + Add Step > Run Node.js code
Add a step to get the Slack channel name
The trigger provides a channel ID like 'C05XYZABC', not '#product-feedback'. Add a Node.js step that calls 'conversations.info' with the channel ID to retrieve the channel name. This makes the Asana task description readable — 'Flagged in #product-feedback' is far more useful than a raw channel ID for anyone triaging the task later.
- 1Click '+' to add another Node.js code step
- 2Fetch 'https://slack.com/api/conversations.info' with the channel ID from the trigger
- 3Extract 'channel.name' from the response
- 4Click 'Test' and confirm the channel name appears in the output
Workflow canvas > + Add Step > Run Node.js code
Build the Asana task payload in a code step
Add a Node.js step to assemble the Asana task object from the data collected in the previous steps. Set the task name to the first 100 characters of the Slack message. Build a description that includes the full message text, the channel name prefixed with '#', the display name of the user who reacted, and a permalink to the original Slack message. Construct the Slack message permalink using the format: 'https://[workspace].slack.com/archives/[channel_id]/p[timestamp_without_period]'.
- 1Click '+' to add a Node.js code step
- 2Assemble the task name by truncating message text to 100 characters
- 3Build the description string with channel name, user name, and Slack permalink
- 4Export the assembled payload as the step's return value using 'return { name, notes, projects }'
This single Node.js step replaces three separate API steps by fetching the message text, user display name, and channel name in one sequential async block, then assembling the full Asana task payload. Paste this as a single 'Run Node.js code' step immediately after the Slack trigger, replacing steps 4, 5, 6, and 7 in the guide above.
JavaScript — Code Stepimport axios from 'axios';▸ Show code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {... expand to see full code
import axios from 'axios';
export default defineComponent({
async run({ steps, $ }) {
const token = process.env.SLACK_BOT_TOKEN;
const channelId = steps.trigger.event.item.channel;
const messageTs = steps.trigger.event.item.ts;
const reactorUserId = steps.trigger.event.user;
// Fetch message text
const historyRes = await axios.get('https://slack.com/api/conversations.history', {
headers: { Authorization: `Bearer ${token}` },
params: { channel: channelId, latest: messageTs, limit: 1, inclusive: true },
});
if (!historyRes.data.ok) {
throw new Error(`conversations.history failed: ${historyRes.data.error}`);
}
const messageText = historyRes.data.messages?.[0]?.text ?? '(no message text)';
// Fetch reactor display name
const userRes = await axios.get('https://slack.com/api/users.info', {
headers: { Authorization: `Bearer ${token}` },
params: { user: reactorUserId },
});
if (!userRes.data.ok) {
throw new Error(`users.info failed: ${userRes.data.error}`);
}
const displayName =
userRes.data.user?.profile?.display_name ||
userRes.data.user?.real_name ||
'Unknown User';
// Fetch channel name
const chanRes = await axios.get('https://slack.com/api/conversations.info', {
headers: { Authorization: `Bearer ${token}` },
params: { channel: channelId },
});
if (!chanRes.data.ok) {
throw new Error(`conversations.info failed: ${chanRes.data.error}`);
}
const channelName = chanRes.data.channel?.name ?? channelId;
// Build Slack permalink (remove period from timestamp)
const tsForUrl = messageTs.replace('.', '');
const permalink = `https://slack.com/archives/${channelId}/p${tsForUrl}`;
// Truncate task name to 100 chars
const taskName =
messageText.length > 100 ? messageText.slice(0, 97) + '...' : messageText;
// Assemble Asana task notes
const notes = [
`📌 Flagged by ${displayName} in #${channelName}`,
'',
'Full message:',
messageText,
'',
`View in Slack: ${permalink}`,
].join('\n');
return {
taskName,
notes,
permalink,
channelName,
displayName,
};
},
});Workflow canvas > + Add Step > Asana > Create Task
Add the Asana step and connect your account
Click '+' and add a new step. Search for 'Asana' and select the 'Create Task' action. Click 'Connect Account' to link your Asana account via OAuth. Pipedream opens an Asana authorization page — approve the requested permissions. Once connected, you'll see dropdowns for Workspace, Project, and other task fields appear in the configuration panel.
- 1Click '+' below the payload assembly step
- 2Search for 'Asana' and select the Asana app
- 3Select 'Create Task' as the action
- 4Click 'Connect Account' and complete the Asana OAuth flow
Asana step panel > Task configuration fields
Map fields from previous steps to the Asana task
In the Asana 'Create Task' configuration panel, fill in each field by referencing the outputs from your earlier steps using Pipedream's double-brace syntax or the expression picker. Set 'Name' to the task name from your payload step, 'Notes' to the formatted description string, 'Projects' to your target Asana project GID (a numeric ID found in the Asana project URL), and optionally set 'Assignee' if you want to auto-assign based on a Slack-to-Asana user mapping.
- 1Click the 'Name' field and reference your payload step's task name output
- 2Click the 'Notes' field and reference the formatted description output
- 3In the 'Projects' field, paste your Asana project GID (found in the project URL after '/0/')
- 4Optionally set 'Assignee' to a specific Asana user GID or leave blank
Workflow canvas > Test workflow button
Test the full workflow end to end
Click 'Test workflow' at the top of the canvas to run all steps against your captured test event. Watch each step turn green as it executes. Check the Asana project you configured — a new task should appear within 5 seconds with the correct name, description, and context. If a step fails, click it to see the error output and the exact API response that caused the failure.
- 1Click 'Test workflow' in the top toolbar
- 2Watch each step indicator for green (success) or red (error) status
- 3Open your Asana project and verify the new task appeared
- 4Click the task in Asana to confirm the description, channel name, and Slack link are correct
Workflow canvas > Deploy > Settings > Error Notifications
Deploy the workflow and set error alerting
Click 'Deploy' in the top right to make the workflow active. Pipedream will now listen for live Slack reaction events and process them instantly. Go to your workflow settings and enable 'Error notifications' so you get an email if any step fails in production. Test one final time with a real Slack reaction to confirm the live webhook path works, not just the test event replay.
- 1Click 'Deploy' in the top-right corner of the workflow editor
- 2Navigate to the workflow Settings tab after deploying
- 3Enable 'Email me on error' under the Error Notifications section
- 4Go to Slack, add your emoji to a real message, and verify a task appears in Asana within 5 seconds
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 comfort at all and wants the trigger to fire in under 3 seconds. The webhook processing is instant — no polling delays, no 5-minute lag. Pipedream is also the right call here because this workflow requires three chained Slack API calls before touching Asana, and doing that cleanly in a no-code tool means three separate modules with brittle data passing between them. In Pipedream, it's one async function. The one case where you'd pick something else: if your whole team is non-technical and needs to maintain this workflow themselves without touching code — then Make's visual module chain is easier to hand off.
Pipedream's free tier gives you 10,000 credits/month. This workflow uses 3-4 credits per run (one trigger credit plus one per code/action step). At 500 reactions/month, you spend roughly 2,000 credits — leaving 8,000 free for other workflows. At 2,500 reactions/month you'll hit about 10,000 credits and need the $19/month Basic plan (100,000 credits). For comparison, the same workflow on Zapier would cost $49/month on the Professional plan to access multi-step Zaps. Make's free tier would handle this at 1,000 operations/month for free, but you hit Make's 1,000-operation ceiling much faster since each API call counts separately.
Zapier has a purpose-built 'New Reaction' trigger that's cleaner to configure — no OAuth scope research needed, and non-developers can set it up in 10 minutes. The downside is Zapier's free tier caps at single-step Zaps, so this 4+ step workflow immediately requires a paid plan. Make's visual interface makes the channel→project routing logic easier to read and audit, and the router module handles multi-project logic without code. n8n self-hosted is free at any volume and the Function node handles all three Slack API calls identically to Pipedream — pick n8n if you're already running it and want zero platform cost. Power Automate has a native Asana connector but it's limited, poorly documented, and the Slack reaction trigger requires a custom connector that most teams don't have. Pipedream wins here on the combination of instant webhooks, free Node.js execution, and staying free for most team sizes.
Three things you'll hit after a week in production. First: Slack rate limits the conversations.history endpoint at 50 requests per minute per workspace. At normal reaction volumes this is fine, but if your team does a big emoji pass on a long message thread all at once, you'll see 429 errors. Add a try/catch with a 1-second retry in your code step. Second: Slack messages containing special characters like '<', '>', and '&' are returned with HTML entities ('<', '>', '&') from the API. If you paste that raw into an Asana task name, it looks wrong. Add a simple decode step: 'text.replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")'. Third: Asana task names have a hard 256-character limit. Messages longer than that will cause a 400 error from the Asana API. Truncate to 250 characters in your payload step to leave a safe margin.
Ideas for what to build next
- →Post a Slack confirmation when the task is created — Add an Asana step output handler that posts a threaded reply in Slack with a link to the newly created Asana task. This closes the loop so the person who reacted knows the task was actually logged.
- →Route reactions to different Asana projects by channel — Add a code step after the trigger that maps the Slack channel ID to a specific Asana project GID. Reactions in #bugs go to your Bug Tracker project, reactions in #product-feedback go to the Feedback Backlog — no manual sorting needed.
- →Sync Asana task completion back to Slack — Build a second Pipedream workflow triggered by Asana's 'Task Completed' webhook. When a task created from Slack gets marked done, post a message in the original Slack channel thread so the team sees it resolved without opening Asana.
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