Intermediate~15 min setupCommunication & Project ManagementVerified April 2026
Slack logo
Asana logo

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-time

Use case type

routing

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

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

Slack account with admin access or permission to install OAuth apps in your workspace
Slack bot scopes granted: reactions:read, channels:history, groups:history, channels:read, users:read
Asana account with access to the project where tasks will be created, and the project's GID from its URL
Asana OAuth scope: tasks:write and projects:read granted during Pipedream connection
Pipedream account (free tier works for up to ~3,000 reactions/month before credit limits apply)

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Namename
Task Notes / Descriptionnotes
Project GIDprojects
6 optional fields▸ show
Slack Channel Name
Flagged By (User Display Name)
Slack Message Permalink
Reaction Emoji
Due Datedue_on
Assignee GIDassignee

Step-by-Step Setup

1

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.

  1. 1Sign in at pipedream.com
  2. 2Click 'New Workflow' in the top-right corner
  3. 3Click the trigger placeholder labeled 'Add a trigger'
  4. 4Type 'Slack' in the search box and select the Slack app
What you should see: You should see the Slack trigger configuration panel open on the right side of the workflow editor.
2

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

  1. 1Select 'New Reaction Added' from the trigger event dropdown
  2. 2Click 'Connect Account' and authorize Slack via OAuth popup
  3. 3In the 'Reaction' field, type the emoji name (e.g., pushpin) without colons
  4. 4Click 'Save and continue' to lock in the trigger
What you should see: The trigger panel shows a green checkmark next to your connected Slack account and displays the reaction name you entered.
Common mistake — The 'New Reaction Added' trigger requires the 'reactions:read' OAuth scope. If your Slack app was authorized before this scope existed, you may need to reconnect your Slack account entirely — editing the existing connection won't add missing scopes.
Pipedream
+
click +
search apps
Slack
SL
Slack
Configure the Slack trigger …
Slack
SL
module added
3

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.

  1. 1Click 'Generate Test Event' in the Slack trigger panel
  2. 2Switch to Slack and add your configured emoji to any message
  3. 3Return to Pipedream and wait 2-3 seconds for the event to appear
  4. 4Click the event in the panel to expand and review the JSON payload
What you should see: You see a JSON object with fields including 'reaction', 'user', 'item.channel', and 'item.ts' populated with real values from your test.
Common mistake — The raw reaction event only contains the message timestamp (item.ts) and channel ID — not the message text itself. You must make a separate Slack API call in the next step to fetch the actual message content.
Pipedream
▶ Deploy & test
executed
Slack
Asana
Asana
🔔 notification
received
4

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.

  1. 1Click the '+' button below the Slack trigger
  2. 2Select 'Run Node.js code' from the step type list
  3. 3Paste the fetch code into the code editor (see Pro Tip below)
  4. 4Click 'Test' to run the step against your test event
What you should see: The step output panel shows a JSON object containing the full Slack message text under 'messages[0].text', along with the sender user ID.
Common mistake — The 'conversations.history' API call requires the 'channels:history' scope for public channels or 'groups:history' for private channels. If the bot isn't a member of the channel where the reaction occurred, the API returns a 'not_in_channel' error.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
5

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.

  1. 1Click '+' below the previous step to add another code step
  2. 2Write a fetch call to 'https://slack.com/api/users.info' with the user ID from the trigger
  3. 3Extract 'user.real_name' or 'user.profile.display_name' from the response
  4. 4Click 'Test' to verify the name resolves correctly
What you should see: The step output shows a readable name like 'Jordan Mills' mapped to the user ID from the trigger event.

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,
    };
  },
});
6

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.

  1. 1Click '+' to add another Node.js code step
  2. 2Fetch 'https://slack.com/api/conversations.info' with the channel ID from the trigger
  3. 3Extract 'channel.name' from the response
  4. 4Click 'Test' and confirm the channel name appears in the output
What you should see: The step output contains the readable channel name, such as 'product-feedback', without the leading hash.
Common mistake — Private channels return a 'channel_not_found' error if your Slack bot hasn't been invited to them. Public channels work automatically as long as the 'channels:read' scope is granted.
7

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]'.

  1. 1Click '+' to add a Node.js code step
  2. 2Assemble the task name by truncating message text to 100 characters
  3. 3Build the description string with channel name, user name, and Slack permalink
  4. 4Export the assembled payload as the step's return value using 'return { name, notes, projects }'
What you should see: The step output shows a clean task object with 'name', 'notes', and 'projects' fields ready to send to Asana.
Common mistake — Slack message timestamps use a period (e.g., '1698765432.123456'). To build a valid permalink, remove the period before appending to the URL — '1698765432123456' not '1698765432.123456'.

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,
    };
  },
});
8

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.

  1. 1Click '+' below the payload assembly step
  2. 2Search for 'Asana' and select the Asana app
  3. 3Select 'Create Task' as the action
  4. 4Click 'Connect Account' and complete the Asana OAuth flow
What you should see: You see a green 'Connected' badge next to your Asana account name, and the task configuration fields appear below it.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
9

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.

  1. 1Click the 'Name' field and reference your payload step's task name output
  2. 2Click the 'Notes' field and reference the formatted description output
  3. 3In the 'Projects' field, paste your Asana project GID (found in the project URL after '/0/')
  4. 4Optionally set 'Assignee' to a specific Asana user GID or leave blank
What you should see: Each field in the Asana step shows a referenced value from an upstream step, displayed as a blue chip with the step name and field path.
Common mistake — Asana project GIDs are numeric (e.g., '1204567891234567'). The Asana project dropdown in Pipedream may time out if your workspace has hundreds of projects — paste the GID directly into the field instead of browsing the dropdown.
Slack fields
text
user
channel
ts
thread_ts
available as variables:
1.props.text
1.props.user
1.props.channel
1.props.ts
1.props.thread_ts
10

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.

  1. 1Click 'Test workflow' in the top toolbar
  2. 2Watch each step indicator for green (success) or red (error) status
  3. 3Open your Asana project and verify the new task appeared
  4. 4Click the task in Asana to confirm the description, channel name, and Slack link are correct
What you should see: All steps show green status indicators. In Asana, you see a new task titled with the Slack message text and a description containing the channel name, user name, and a working Slack message link.
11

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.

  1. 1Click 'Deploy' in the top-right corner of the workflow editor
  2. 2Navigate to the workflow Settings tab after deploying
  3. 3Enable 'Email me on error' under the Error Notifications section
  4. 4Go to Slack, add your emoji to a real message, and verify a task appears in Asana within 5 seconds
What you should see: The workflow status shows 'Active' with a green indicator. A new Asana task appears within 5 seconds of adding the emoji reaction in Slack.
Common mistake — Pipedream's free tier allows up to 10,000 credits/month. Each full run of this workflow (3-4 API calls) costs roughly 3-4 credits. At 1,000 reactions/month you'll use ~4,000 credits — well within free tier. At 3,000+/month, upgrade to the $19/month Basic plan to avoid workflow pausing mid-month.

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

Cost

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.

Tradeoffs

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 ('&lt;', '&gt;', '&amp;') from the API. If you paste that raw into an Asana task name, it looks wrong. Add a simple decode step: 'text.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/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 createdAdd 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 channelAdd 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 SlackBuild 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

Was this guide helpful?
Slack + Asana overviewPipedream profile →