Intermediate~15 min setupCommunication & ProductivityVerified April 2026
Slack logo
Notion logo

How to Create Notion Tasks from Slack with Pipedream

Watches for a specific emoji reaction on any Slack message, then creates a new task entry in a Notion database with the message text, sender, channel, and timestamp — no manual copy-paste required.

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

Best for

Engineering or ops teams who triage action items in Slack and need them tracked in a Notion project board without leaving the conversation.

Not ideal for

Teams already using Linear, Jira, or Asana for task tracking — build toward those integrations instead of duplicating work in Notion.

Sync type

real-time

Use case type

routing

Real-World Example

💡

A 12-person product team manages feature requests and bug reports in a busy #product-feedback Slack channel. Anyone can react with a 📋 emoji to flag a message as a task, and Pipedream fires within seconds to create a Notion database entry with the full message text, the reporter's name, and a direct link back to the Slack thread. Before this, the PM spent 20 minutes every morning copy-pasting messages into Notion and still missed things.

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 or app-installation permissions so you can authorize Pipedream's OAuth app and grant reactions:read and channels:history scopes
Notion account with edit access to the database where tasks will be created — viewer access is not enough
A Notion database already set up with at least these properties: a Title field, a text field for description, a text or person field for reporter, a select field for status, and a URL field for the Slack link
Pipedream account — free tier is sufficient to start, but you'll need to monitor credit usage if the channel is high-traffic
Slack bot or app with permissions to read message history in the channels you want to monitor (the default Pipedream Slack app handles this, but custom workspace apps may need manual scope additions)

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Task Title
Reported By
Status
Source Link
4 optional fields▸ show
Channel Name
Slack Message Timestamp
Created At
Priority

Step-by-Step Setup

1

pipedream.com > Workflows > New Workflow

Create a new Pipedream workflow

Go to pipedream.com and click 'New Workflow' in the top-right corner. Give it a name like 'Slack Reaction → Notion Task' so you can find it later. You'll land on the workflow canvas, which shows a trigger slot at the top and an empty step slot below it. Everything builds from here.

  1. 1Go to pipedream.com and log in
  2. 2Click 'New Workflow' in the top-right
  3. 3Enter a workflow name in the title field at the top of the canvas
  4. 4Click 'Add Trigger' to open the trigger selector
What you should see: You should see the workflow canvas with an empty trigger slot labeled 'Add a trigger to get started.'
Common mistake — Pipedream free tier limits you to 10,000 invocations/month and credits are shared across all workflows. Name your workflow clearly now — you can't rename it from the dashboard without opening it.
2

Workflow Canvas > Add Trigger > Slack > New Reaction Added

Configure the Slack trigger (Reaction Added)

In the trigger selector, search for 'Slack' and choose it. From the trigger list, select 'New Reaction Added'. This fires instantly via Slack's Events API every time any user reacts to any message in your workspace — you'll filter to a specific emoji in a later step. Connect your Slack account using Connected Accounts when prompted.

  1. 1Type 'Slack' in the trigger search box
  2. 2Click the Slack app card
  3. 3Select 'New Reaction Added' from the trigger list
  4. 4Click 'Connect Account' and authorize Pipedream via the Slack OAuth screen
  5. 5Click 'Save and Continue'
What you should see: You should see a green 'Connected' badge next to your Slack account name and a test event option appear below the trigger config.
Common mistake — The Slack app you authorize must have the 'reactions:read' and 'channels:history' scopes. If your workspace uses a custom Slack app with restricted scopes, the trigger will connect but never fire. Check your Slack App settings at api.slack.com/apps.
Pipedream
+
click +
search apps
Slack
SL
Slack
Configure the Slack trigger …
Slack
SL
module added
3

Workflow Canvas > Trigger > Generate Test Event

Test the Slack trigger with a real reaction

Click 'Generate Test Event' in the trigger panel. Pipedream will prompt you to go into Slack and add a reaction to any message. React with 📋 on a real message in your workspace right now. Return to Pipedream — within 5–10 seconds you should see a JSON payload appear in the trigger panel showing the reaction name, the message text, the user ID, and the channel ID.

  1. 1Click 'Generate Test Event' in the trigger panel
  2. 2Open Slack, find any message, and add a 📋 reaction
  3. 3Return to Pipedream and wait for the event to appear
  4. 4Click on the event to expand the full JSON payload
What you should see: You should see a test event with fields including 'reaction' (the emoji name), 'item.ts' (message timestamp), 'user' (Slack user ID), and 'item.channel' (channel ID).
Common mistake — The 'reaction' field contains the emoji name without colons — so 📋 shows as 'clipboard', not ':clipboard:'. Note the exact string here because you'll match against it in the next step.
Pipedream
▶ Deploy & test
executed
Slack
Notion
Notion
🔔 notification
received
4

Workflow Canvas > + Add Step > Filter

Add a Filter step to check the emoji name

Click the '+' button below the trigger to add a new step. Search for 'Filter' and select the built-in Pipedream Filter action. Set the condition to: 'steps.trigger.event.reaction' equals 'clipboard' (or whatever emoji name appeared in your test event). This stops the workflow dead if someone uses a different reaction, so you don't create Notion tasks for every emoji in your workspace.

  1. 1Click '+' below the trigger step
  2. 2Search for 'Filter' and click the Pipedream Filter action
  3. 3Set the left-hand value to '{{steps.trigger.event.reaction}}'
  4. 4Set the operator to 'equals'
  5. 5Set the right-hand value to 'clipboard' (or your chosen emoji name)
What you should see: The Filter step should show a green checkmark when you test it with the clipboard reaction and a red 'workflow will halt' message when you test it with a different emoji name.
Common mistake — Filters are the most common place setups break. Double-check the field name and value exactly match what your app sends — a single capital letter difference will block everything.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Notion
NO
notified
5

Workflow Canvas > + Add Step > Run Node.js Code

Add a Node.js step to fetch the Slack message text

The reaction event gives you the message timestamp and channel ID, but not the actual message text. Add a new step, choose 'Run Node.js code', and use Slack's conversations.history API to retrieve the full message. You'll use your connected Slack account's OAuth token to authenticate the API call. The message text is at response.messages[0].text.

  1. 1Click '+' to add a step after the Filter
  2. 2Select 'Run Node.js code'
  3. 3Paste the code from the Pro Tip section below into the code editor
  4. 4In the 'Connected Accounts' section of the step, select your Slack account
  5. 5Click 'Test' to run the step against your test event
What you should see: The step output should show a 'messageText' field containing the full text of the Slack message you reacted to, plus the permalink URL.
Common mistake — Private channels require the 'groups:history' scope. Public channels use 'channels:history'. If the API returns 'channel_not_found', the bot hasn't been invited to that channel. Run '/invite @YourPipedreamApp' in the channel first.
message template
🔔 New Record: {{text}} {{user}}
channel: {{channel}}
ts: {{ts}}
#sales
🔔 New Record: Jane Smith
Company: Acme Corp
6

Workflow Canvas > + Add Step > Run Node.js Code

Fetch the Slack user's display name

The reaction event gives you a Slack user ID like 'U04ABCXYZ', not a human name. Add another Node.js step that calls users.info with that user ID to resolve it to a real display name. Without this step, your Notion tasks will show raw user IDs in the 'Reported By' field, which is useless for anyone reading the board.

  1. 1Click '+' to add another step after the message-fetch step
  2. 2Select 'Run Node.js code'
  3. 3Write a fetch to 'https://slack.com/api/users.info' with the user ID from steps.trigger.event.user
  4. 4Select your Slack Connected Account to inject the token
  5. 5Test and confirm the output shows a 'displayName' field with the real name
What you should see: Step output should include 'displayName' with a value like 'Jamie Torres' rather than 'U04ABCXYZ'.
Common mistake — If your workspace has 'Hide full names' enabled in Slack's Display Settings, users.info may return a profile.display_name that's blank. Fall back to profile.real_name in your code if display_name is an empty string.
7

Workflow Canvas > + Add Step > Notion > Create Page

Connect your Notion account

Add a new step and search for 'Notion'. Select the 'Create Page' action. Click 'Connect Account' — this opens Notion's OAuth screen. During authorization, Notion will ask which pages or databases you want to give Pipedream access to. You must explicitly select your task database here or Pipedream won't be able to write to it. This is the most common setup mistake on Notion integrations.

  1. 1Click '+' to add a new step
  2. 2Search for 'Notion' and click the app card
  3. 3Select 'Create Page' from the action list
  4. 4Click 'Connect Account'
  5. 5In Notion's OAuth screen, check the box next to your task database before clicking 'Allow Access'
What you should see: You should see a green Connected badge and a 'Parent Database ID' field appear in the step configuration below.
Common mistake — Notion OAuth access is database-specific. If you give access to the wrong page or no pages, the step will throw a 'Could not find database' error at runtime. You can fix access at notion.so/my-integrations without re-authorizing in Pipedream.
Pipedream settings
Connection
Choose a connection…Add
click Add
Slack
Log in to authorize
Authorize Pipedream
popup window
Connected
green checkmark
8

Workflow Canvas > Notion Step > Create Page > Field Configuration

Configure the Notion Create Page fields

In the Notion step, set the Parent Database ID to the ID of your task database (copy it from the Notion URL — it's the 32-character string after the last slash and before the '?'). Then map each Notion database property to the corresponding Pipedream step output. You'll map task name, description, reporter, status, and the Slack message link. Set 'Status' to a static value like 'To Do' so every new task starts in the same column.

  1. 1Paste your Notion database ID into the 'Parent Database ID' field
  2. 2Set 'Title' to '{{steps.fetch_message.messageText}}' (truncated to 100 chars if needed)
  3. 3Set 'Reported By' to '{{steps.fetch_user.displayName}}'
  4. 4Set 'Source Link' to the Slack permalink from your message-fetch step
  5. 5Set 'Status' to the static value 'To Do'
  6. 6Set 'Created At' to '{{new Date().toISOString()}}'
What you should see: All mapped fields should show green pill tokens referencing previous step outputs. No fields should show raw placeholder text.
Common mistake — Notion property names are case-sensitive and must exactly match the column names in your database. If your column is 'Reported by' (lowercase b) and you type 'Reported By', the step will silently create the page without that field populated.
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
9

Workflow Canvas > Test Workflow (top bar)

Test the full workflow end-to-end

Click 'Test Workflow' at the top of the canvas. This runs all steps in sequence using the test event you captured earlier. Watch each step turn green. Then open your Notion database and confirm the new page appeared with correct values in every field. Check that the Slack permalink actually links back to the right message.

  1. 1Click 'Test Workflow' in the top toolbar
  2. 2Watch each step indicator — green means success, red means it threw an error
  3. 3Open your Notion database in a browser
  4. 4Find the newly created page and verify all fields are populated
  5. 5Click the Source Link and confirm it jumps to the right Slack message
What you should see: A new Notion page should exist with the task title, reporter name, status 'To Do', and a working Slack permalink. All steps should show green in the Pipedream canvas.
10

Workflow Canvas > Deploy (top-right button)

Deploy the workflow

Click the 'Deploy' button in the top-right corner of the canvas. The workflow flips from 'Development' to 'Active' mode. From this point, Pipedream's webhook listener is live and will fire within 1–3 seconds of any 📋 reaction in your Slack workspace. Go into Slack and add a real reaction to confirm the live workflow creates a Notion task.

  1. 1Click the 'Deploy' button in the top-right
  2. 2Confirm the status badge changes to 'Active'
  3. 3Open Slack and react to a message with 📋
  4. 4Wait 5–10 seconds and refresh your Notion database
What you should see: A live Notion task should appear within 10 seconds of the Slack reaction. The workflow's 'Invocations' count in the Pipedream dashboard should increment by 1.
Common mistake — Deploying doesn't pause existing invocations in your event queue. If you had a backlog of test events sitting in the trigger, they may fire immediately on deploy and create duplicate Notion pages. Clear the event queue in the trigger settings before deploying.

This Node.js step goes in Step 5 of your Pipedream workflow — the 'Run Node.js Code' step right after the Filter. It fetches the full Slack message text and permalink using the conversations.history API, resolves the user's display name in the same step, and includes deduplication logic using Pipedream's built-in data store so reacting twice to the same message only creates one Notion task. Paste the entire block into the code editor and select your Slack Connected Account in the step's account selector.

JavaScript — Code Stepimport { axios } from '@pipedream/platform';
▸ Show code
import { axios } from '@pipedream/platform';
export default defineComponent({
  props: {

... expand to see full code

import { axios } from '@pipedream/platform';

export default defineComponent({
  props: {
    slack: {
      type: 'app',
      app: 'slack',
    },
  },
  async run({ steps, $ }) {
    const channelId = steps.trigger.event.item.channel;
    const messageTs = steps.trigger.event.item.ts;
    const userId = steps.trigger.event.user;
    const reactionName = steps.trigger.event.reaction;

    // Only proceed for the clipboard emoji
    if (reactionName !== 'clipboard') {
      $.flow.exit('Reaction is not clipboard — halting.');
    }

    // Deduplicate: skip if this message was already converted to a task
    const db = $.service.db;
    const dedupKey = `slack-task-${messageTs}`;
    const alreadyProcessed = await db.get(dedupKey);
    if (alreadyProcessed) {
      $.flow.exit(`Message ${messageTs} already has a Notion task — skipping duplicate.`);
    }

    // Fetch the full message text
    const historyResponse = await axios($, {
      method: 'GET',
      url: 'https://slack.com/api/conversations.history',
      headers: {
        Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
      },
      params: {
        channel: channelId,
        latest: messageTs,
        inclusive: true,
        limit: 1,
      },
    });

    if (!historyResponse.ok) {
      throw new Error(`Slack conversations.history failed: ${historyResponse.error}`);
    }

    const message = historyResponse.messages?.[0];
    if (!message) {
      throw new Error(`No message found for timestamp ${messageTs} in channel ${channelId}`);
    }

    const messageText = message.text || '(no text)';

    // Fetch the permalink
    const permalinkResponse = await axios($, {
      method: 'GET',
      url: 'https://slack.com/api/chat.getPermalink',
      headers: {
        Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
      },
      params: {
        channel: channelId,
        message_ts: messageTs,
      },
    });

    const permalink = permalinkResponse.ok ? permalinkResponse.permalink : null;

    // Resolve the user's display name
    const userResponse = await axios($, {
      method: 'GET',
      url: 'https://slack.com/api/users.info',
      headers: {
        Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
      },
      params: { user: userId },
    });

    const profile = userResponse.user?.profile || {};
    const displayName =
      profile.display_name?.trim() ||
      profile.real_name?.trim() ||
      'Unknown User';

    // Fetch the channel name for the Notion record
    const channelResponse = await axios($, {
      method: 'GET',
      url: 'https://slack.com/api/conversations.info',
      headers: {
        Authorization: `Bearer ${this.slack.$auth.oauth_access_token}`,
      },
      params: { channel: channelId },
    });

    const channelName = channelResponse.ok
      ? `#${channelResponse.channel.name}`
      : 'Unknown Channel';

    // Mark this message as processed to prevent duplicates
    await db.set(dedupKey, true);

    return {
      messageText,
      permalink,
      displayName,
      channelName,
      messageTs,
      createdAt: new Date().toISOString(),
    };
  },
});

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 includes at least one developer who can write basic JavaScript. The code steps are the entire point — fetching message text, resolving user IDs, and deduplicating reactions all require API calls that no-code tools handle badly or not at all. Pipedream fires the webhook within 1–3 seconds of the reaction, and the Node.js runtime means you can handle edge cases (deleted messages, private channels, multi-reaction deduplication) in the same step without duct-taping together five separate actions. The one scenario where you'd skip Pipedream: if your team has zero coding comfort and just needs the simplest possible version of this workflow. Go with Zapier's Slack + Notion integration in that case — it's slower (1–5 minute polling on free) and can't fetch message text without Formatter workarounds, but it requires no code.

Cost

Pipedream's free tier gives you 10,000 invocations and 3.33 compute hours per month. This workflow runs 5 steps per invocation. At 300 reactions/month (a busy team), you're at 3% of your free tier — essentially free. At 3,000 reactions/month you're still well within limits. The first paid tier is $19/month for 100,000 invocations and 330 compute hours. Compare that to Zapier, where 3,000 tasks/month on the Starter plan costs $49.99/month. For a workflow that fires on every reaction, Pipedream is $30/month cheaper at that volume.

Tradeoffs

Zapier's Slack + Notion integration is genuinely easier to set up — about 8 minutes, no code, reaction trigger included — but it polls every 1–5 minutes instead of firing instantly, and it can't fetch the full message text from a reaction event without a Formatter step that often mangles the output. Make handles the same workflow with HTTP modules for the Slack API calls and a decent JSON parser, but Notion's Make module has historically lagged behind the Notion API and sometimes misses newer property types like 'status'. n8n gives you the same Node.js power as Pipedream but you're hosting it yourself or paying for n8n Cloud — more operational overhead for the same outcome. Power Automate has a Slack connector but it's shallow: no reaction trigger as of early 2024, so you'd need a workaround via webhook that makes the setup more complex than Pipedream's native trigger. Pipedream wins here because the Slack reaction trigger is native, the webhook is instant, and the Node.js code step handles everything that would otherwise require three separate no-code actions.

Three things you'll discover after this is running in production. First, Slack's conversations.history API has a rate limit of 1 request per second per token. If your channel gets a burst of reactions (like when someone shares a big thread and five people react at once), you'll hit 429 errors. Add a 1-second delay or retry logic in your code step. Second, Notion's API enforces a limit of 3 requests per second per integration — at high reaction volumes, the Create Page calls will start returning 429s and Pipedream will log errors without retrying by default. You need to add explicit retry logic in your Node.js step. Third, Slack message text can contain user mention tokens like '<@U04ABCXYZ>' instead of readable names. Your Notion task title will look like 'Can <@U04ABCXYZ> handle this by Friday?' — you'll want to add a regex replacement step that resolves those mention tokens to display names before writing to Notion.

Ideas for what to build next

  • Add a Notion → Slack status update loopWhen a Notion task status changes to 'Done', send a message back to the original Slack thread confirming it's resolved. Pipedream can poll the Notion database every 5 minutes for status changes and use the stored permalink to reply to the right thread.
  • Route tasks to different Notion databases by channelAdd a code step that checks the channel name and writes to a different Notion database depending on whether the reaction came from #engineering, #design, or #product. Each team gets their own board without seeing the others' tasks.
  • Parse keywords to auto-assign priorityAdd a text parsing step before the Notion create step that checks the message text for words like 'urgent', 'ASAP', or 'blocker' and sets the Notion Priority field to 'High' automatically. Everything else defaults to 'Medium'.

Related guides

Was this guide helpful?
Slack + Notion overviewPipedream profile →