

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-timeUse case type
routingReal-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.
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 Title | ||
| Reported By | ||
| Status | ||
| Source Link | ||
4 optional fields▸ show
| Channel Name | |
| Slack Message Timestamp | |
| Created At | |
| Priority |
Step-by-Step Setup
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.
- 1Go to pipedream.com and log in
- 2Click 'New Workflow' in the top-right
- 3Enter a workflow name in the title field at the top of the canvas
- 4Click 'Add Trigger' to open the trigger selector
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.
- 1Type 'Slack' in the trigger search box
- 2Click the Slack app card
- 3Select 'New Reaction Added' from the trigger list
- 4Click 'Connect Account' and authorize Pipedream via the Slack OAuth screen
- 5Click 'Save and Continue'
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.
- 1Click 'Generate Test Event' in the trigger panel
- 2Open Slack, find any message, and add a 📋 reaction
- 3Return to Pipedream and wait for the event to appear
- 4Click on the event to expand the full JSON payload
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.
- 1Click '+' below the trigger step
- 2Search for 'Filter' and click the Pipedream Filter action
- 3Set the left-hand value to '{{steps.trigger.event.reaction}}'
- 4Set the operator to 'equals'
- 5Set the right-hand value to 'clipboard' (or your chosen emoji name)
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.
- 1Click '+' to add a step after the Filter
- 2Select 'Run Node.js code'
- 3Paste the code from the Pro Tip section below into the code editor
- 4In the 'Connected Accounts' section of the step, select your Slack account
- 5Click 'Test' to run the step against your test event
channel: {{channel}}
ts: {{ts}}
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.
- 1Click '+' to add another step after the message-fetch step
- 2Select 'Run Node.js code'
- 3Write a fetch to 'https://slack.com/api/users.info' with the user ID from steps.trigger.event.user
- 4Select your Slack Connected Account to inject the token
- 5Test and confirm the output shows a 'displayName' field with the real name
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.
- 1Click '+' to add a new step
- 2Search for 'Notion' and click the app card
- 3Select 'Create Page' from the action list
- 4Click 'Connect Account'
- 5In Notion's OAuth screen, check the box next to your task database before clicking 'Allow Access'
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.
- 1Paste your Notion database ID into the 'Parent Database ID' field
- 2Set 'Title' to '{{steps.fetch_message.messageText}}' (truncated to 100 chars if needed)
- 3Set 'Reported By' to '{{steps.fetch_user.displayName}}'
- 4Set 'Source Link' to the Slack permalink from your message-fetch step
- 5Set 'Status' to the static value 'To Do'
- 6Set 'Created At' to '{{new Date().toISOString()}}'
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.
- 1Click 'Test Workflow' in the top toolbar
- 2Watch each step indicator — green means success, red means it threw an error
- 3Open your Notion database in a browser
- 4Find the newly created page and verify all fields are populated
- 5Click the Source Link and confirm it jumps to the right Slack message
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.
- 1Click the 'Deploy' button in the top-right
- 2Confirm the status badge changes to 'Active'
- 3Open Slack and react to a message with 📋
- 4Wait 5–10 seconds and refresh your Notion database
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
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.
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.
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 loop — When 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 channel — Add 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 priority — Add 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
How to Create Notion Tasks from Slack with Power Automate
~15 min setup
How to Create Notion Tasks from Slack with n8n
~20 min setup
How to Create Notion Tasks from Slack Messages with Zapier
~8 min setup
How to Create Notion Tasks from Slack Messages with Make
~12 min setup
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