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

How to Archive Slack Messages to Notion with n8n

Automatically saves flagged Slack messages or full threads to a designated Notion database page the moment a reaction emoji is added, preserving decisions and discussions in your team's knowledge base.

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 who want to capture Slack decisions into Notion without copy-pasting, triggered by adding a specific reaction emoji to a message.

Not ideal for

Teams that need to archive every message in a channel automatically — that volume requires a different architecture with a database-backed deduplication layer.

Sync type

real-time

Use case type

backup

Real-World Example

💡

A 12-person product team uses this to save any Slack message reacted with 📌 directly into their Notion 'Decisions Log' database. Before the automation, important decisions buried in #product channel were lost within days and new hires had no context. Now every pinned message appears in Notion within 10 seconds, tagged with the channel, author, and timestamp.

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 n8n

Copy the pre-built n8n blueprint and paste it straight into n8n. 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 workspace admin access or permission to create and install Slack apps in your workspace
A Slack bot token with scopes: reactions:read, channels:history, users:read, channels:read (or groups:history for private channels)
A Notion account with an existing database that has properties for Message Text, Author, Channel, Timestamp, and Message Link
A Notion integration created at notion.so/my-integrations with the database shared to that integration inside Notion
A self-hosted or cloud n8n instance with a publicly accessible URL so Slack can reach your webhook endpoint

Field Mapping

Map these fields between your apps.

FieldAPI Name
Required
Message Text
Author
Channel
Timestamp
Message Link
3 optional fields▸ show
Archived By
Reaction Used
Thread Timestamp

Step-by-Step Setup

1

api.slack.com/apps > Create New App > Event Subscriptions

Create a Slack App and Configure Event Subscriptions

You need a custom Slack App to receive reaction events via webhook — n8n's built-in Slack node uses this app under the hood. Go to api.slack.com/apps, click 'Create New App', choose 'From scratch', name it something like 'n8n Archiver', and select your workspace. Once created, navigate to 'Event Subscriptions' in the left sidebar and toggle it ON. You'll paste your n8n webhook URL here in step 3, after n8n generates it.

  1. 1Go to api.slack.com/apps and click 'Create New App'
  2. 2Choose 'From scratch', enter app name 'n8n Archiver', select your workspace
  3. 3Click 'Event Subscriptions' in the left sidebar
  4. 4Toggle 'Enable Events' to ON
  5. 5Leave the Request URL field empty for now — you'll return after step 3
What you should see: You should see the Event Subscriptions page with the toggle switched ON and a yellow banner saying 'Request URL is required to enable events'.
Common mistake — Do not install the app to your workspace yet. Installing before adding scopes in step 2 means you'll need to reinstall after adding them — Slack doesn't apply scope changes to already-installed apps automatically.

This Code node runs between the users.info HTTP Request and the Notion node. It converts Slack's raw Unix timestamp to ISO 8601, builds a clean Slack message deep-link URL, and passes all transformed fields forward so your Notion node only needs simple expression references. Paste it into a Code node set to 'Run Once for Each Item' mode.

JavaScript — Code Node// Runs in n8n Code node — 'Run Once for Each Item' mode
▸ Show code
// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node
const webhookData = $('Webhook').item.json.body.event;

... expand to see full code

// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node

const webhookData = $('Webhook').item.json.body.event;
const messageData = $('HTTP Request').item.json.messages[0];
const userData = $('HTTP Request 1').item.json.user;

// Convert Slack's Unix timestamp (with microseconds) to ISO 8601
const slackTs = messageData.ts; // e.g. '1701432187.000400'
const tsSeconds = parseFloat(slackTs);
const isoTimestamp = new Date(tsSeconds * 1000).toISOString();

// Build a direct Slack message deep-link
// Slack deep-links require the timestamp without the period
const tsForUrl = slackTs.replace('.', '');
const channelId = webhookData.item.channel;
const messageLink = `https://slack.com/archives/${channelId}/p${tsForUrl}`;

// Resolve author name with fallback from real_name if display_name is empty
const authorName = 
  userData.profile.display_name ||
  userData.profile.real_name ||
  'Unknown User';

// Resolve the reactor (person who added the emoji) — separate from message author
const reactorId = webhookData.user;

// Truncate very long messages to 2000 chars (Notion rich text block limit)
const rawText = messageData.text || '';
const messageText = rawText.length > 2000
  ? rawText.substring(0, 1997) + '...'
  : rawText;

// Detect if this message is part of a thread
const threadTs = messageData.thread_ts || null;
const isThreadReply = threadTs !== null && threadTs !== slackTs;

return {
  isoTimestamp,
  messageLink,
  authorName,
  messageText,
  channelId,
  reactorId,
  reactionEmoji: webhookData.reaction,
  isThreadReply,
  threadTs: threadTs ? new Date(parseFloat(threadTs) * 1000).toISOString() : null
};
2

api.slack.com/apps > [Your App] > OAuth & Permissions > Bot Token Scopes

Add OAuth Scopes to Your Slack App

Your Slack app needs specific permission scopes before it can read message content and reaction events. In the left sidebar of your app settings, click 'OAuth & Permissions'. Scroll down to the 'Bot Token Scopes' section. You need four scopes: reactions:read to detect when a reaction is added, channels:history to read the message content after a reaction fires, users:read to resolve the user ID to a display name, and channels:read to identify the channel name. Add each one individually.

  1. 1Click 'OAuth & Permissions' in the left sidebar
  2. 2Scroll to 'Bot Token Scopes' and click 'Add an OAuth Scope'
  3. 3Add 'reactions:read'
  4. 4Add 'channels:history'
  5. 5Add 'users:read'
  6. 6Add 'channels:read'
What you should see: You should see all four scopes listed under Bot Token Scopes: reactions:read, channels:history, users:read, and channels:read.
Common mistake — channels:history only works for public channels. If you need to archive messages from private channels, add groups:history instead and ensure the bot is manually invited into each private channel with /invite @n8n Archiver.
3

n8n Canvas > + > Webhook > HTTP Method: POST

Install the App and Set Up the n8n Webhook Trigger

Now install the Slack app to your workspace by clicking 'Install to Workspace' on the OAuth & Permissions page. Copy the Bot User OAuth Token that appears — it starts with xoxb-. Switch to n8n and open your workflow canvas. Click the '+' button to add a node, search for 'Webhook', and select the Webhook node. Set the HTTP Method to POST and copy the generated webhook URL from the 'Webhook URL' field at the bottom of the node panel. This URL goes back into Slack's Event Subscriptions page.

  1. 1Click 'Install to Workspace' on the OAuth & Permissions page and authorize
  2. 2Copy the Bot User OAuth Token (starts with xoxb-)
  3. 3In n8n, click '+' on the canvas and search for 'Webhook'
  4. 4Set HTTP Method to POST
  5. 5Copy the Production Webhook URL shown at the bottom of the node config panel
What you should see: n8n shows a Webhook node on the canvas with a green URL displayed. The URL format looks like https://your-n8n-instance.com/webhook/[unique-id].
Common mistake — Use the Production Webhook URL, not the Test URL. The test URL only works when you manually click 'Listen for Test Event' — it goes inactive the moment you stop listening, so Slack's verification request will fail if you paste the test URL.
n8n
+
click +
search apps
Slack
SL
Slack
Install the App and Set Up t…
Slack
SL
module added
4

api.slack.com/apps > [Your App] > Event Subscriptions > Subscribe to Bot Events

Register the Webhook URL in Slack and Subscribe to Events

Go back to Slack's Event Subscriptions page for your app. Paste the n8n Production Webhook URL into the 'Request URL' field. Slack will immediately send a challenge request to verify the endpoint — n8n's Webhook node handles this automatically by returning the challenge value. You should see a green 'Verified' checkmark appear within 3 seconds. Then click 'Add Bot User Event' and add the event reaction_added. Save the changes.

  1. 1Paste the n8n webhook URL into the 'Request URL' field
  2. 2Wait for the green 'Verified' checkmark to appear
  3. 3Click 'Add Bot User Event' under 'Subscribe to Bot Events'
  4. 4Search for and select 'reaction_added'
  5. 5Click 'Save Changes' at the bottom of the page
What you should see: The Request URL field shows a green 'Verified' badge, and reaction_added appears in the subscribed events list.
Common mistake — Copy the webhook URL carefully — it expires if you regenerate it, and any scenarios using the old URL will silently stop working.
5

n8n Canvas > + > IF Node > Conditions

Filter for Your Chosen Reaction Emoji

Every reaction added to any message in any channel will now hit your webhook — including thumbs-up, fire, and laugh emojis you don't want to archive. Add an IF node in n8n to filter specifically for your archival emoji (e.g., 📌 which Slack sends as 'pushpin'). Connect it after the Webhook node. In the IF node, set Condition 1 to: Value 1 = {{$json.body.event.reaction}}, Operation = 'Equal', Value 2 = pushpin. Only messages reacted with 📌 will continue to the next step.

  1. 1Click '+' after the Webhook node and add an 'IF' node
  2. 2Set Value 1 to expression: {{$json.body.event.reaction}}
  3. 3Set Operation to 'Equal'
  4. 4Set Value 2 to: pushpin (no emoji character — Slack sends the text name)
  5. 5Click 'Add Condition' if you want to support multiple emoji names with an OR condition
What you should see: The IF node shows two output paths: 'true' (reaction matched) and 'false' (all other reactions). Only the true branch connects to subsequent nodes.
Common mistake — Slack sends emoji names as text strings without colons — so 📌 becomes pushpin, not :pushpin:. Check your exact emoji name by looking at the raw webhook payload in step 6 before hardcoding the value here.
Slack
SL
trigger
filter
Condition
matches criteria?
yes — passes through
no — skipped
Notion
NO
notified
6

n8n Canvas > + > HTTP Request > Method: GET

Fetch the Full Slack Message Content

The reaction_added event only tells you which message was reacted to — it gives you a channel ID and a message timestamp, but not the actual message text. You need to call Slack's conversations.history API to retrieve the message. Add an HTTP Request node after the IF node's true branch. Set Method to GET, URL to https://slack.com/api/conversations.history, and add three query parameters: channel = {{$json.body.event.item.channel}}, latest = {{$json.body.event.item.ts}}, limit = 1. Add a Header: Authorization = Bearer xoxb-your-bot-token.

  1. 1Add an 'HTTP Request' node after the IF node's true output
  2. 2Set Method to GET
  3. 3Set URL to https://slack.com/api/conversations.history
  4. 4Under 'Query Parameters', add: channel = {{$json.body.event.item.channel}}
  5. 5Add: latest = {{$json.body.event.item.ts}} and limit = 1
  6. 6Under 'Headers', add: Authorization = Bearer xoxb-YOUR-TOKEN
What you should see: When you test with a real reaction event, the HTTP Request node returns a JSON object with messages[0].text containing the actual message content.
Common mistake — The conversations.history endpoint returns messages in reverse-chronological order. Using latest = [timestamp] with limit = 1 returns exactly that message. If you use oldest instead of latest, you'll get the wrong message in active channels.
7

n8n Canvas > + > HTTP Request > URL: slack.com/api/users.info

Resolve the User ID to a Display Name

The message payload contains a user field like U04XXXXXXXXX — not a readable name. Add another HTTP Request node to call Slack's users.info API and get the display name. Set Method to GET, URL to https://slack.com/api/users.info, Query Parameter: user = {{$json.messages[0].user}}, and the same Authorization header. The response will contain user.profile.display_name which you'll map to Notion in the next step.

  1. 1Add a second 'HTTP Request' node after the first one
  2. 2Set Method to GET
  3. 3Set URL to https://slack.com/api/users.info
  4. 4Add Query Parameter: user = {{$node['HTTP Request'].json.messages[0].user}}
  5. 5Add the same Authorization header with your bot token
What you should see: The node returns a user object. Under user.profile.display_name you should see the actual Slack display name like 'Sarah Chen'.
Common mistake — Some Slack workspaces have users with empty display_name fields — they fill in real_name instead. Use a fallback expression: {{$json.user.profile.display_name || $json.user.profile.real_name}} to avoid blank author fields in Notion.

This Code node runs between the users.info HTTP Request and the Notion node. It converts Slack's raw Unix timestamp to ISO 8601, builds a clean Slack message deep-link URL, and passes all transformed fields forward so your Notion node only needs simple expression references. Paste it into a Code node set to 'Run Once for Each Item' mode.

JavaScript — Code Node// Runs in n8n Code node — 'Run Once for Each Item' mode
▸ Show code
// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node
const webhookData = $('Webhook').item.json.body.event;

... expand to see full code

// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node

const webhookData = $('Webhook').item.json.body.event;
const messageData = $('HTTP Request').item.json.messages[0];
const userData = $('HTTP Request 1').item.json.user;

// Convert Slack's Unix timestamp (with microseconds) to ISO 8601
const slackTs = messageData.ts; // e.g. '1701432187.000400'
const tsSeconds = parseFloat(slackTs);
const isoTimestamp = new Date(tsSeconds * 1000).toISOString();

// Build a direct Slack message deep-link
// Slack deep-links require the timestamp without the period
const tsForUrl = slackTs.replace('.', '');
const channelId = webhookData.item.channel;
const messageLink = `https://slack.com/archives/${channelId}/p${tsForUrl}`;

// Resolve author name with fallback from real_name if display_name is empty
const authorName = 
  userData.profile.display_name ||
  userData.profile.real_name ||
  'Unknown User';

// Resolve the reactor (person who added the emoji) — separate from message author
const reactorId = webhookData.user;

// Truncate very long messages to 2000 chars (Notion rich text block limit)
const rawText = messageData.text || '';
const messageText = rawText.length > 2000
  ? rawText.substring(0, 1997) + '...'
  : rawText;

// Detect if this message is part of a thread
const threadTs = messageData.thread_ts || null;
const isThreadReply = threadTs !== null && threadTs !== slackTs;

return {
  isoTimestamp,
  messageLink,
  authorName,
  messageText,
  channelId,
  reactorId,
  reactionEmoji: webhookData.reaction,
  isThreadReply,
  threadTs: threadTs ? new Date(parseFloat(threadTs) * 1000).toISOString() : null
};
8

n8n Canvas > + > Notion > Resource: Database Page > Operation: Create

Connect n8n to Notion and Create the Database Page

Add a Notion node after the user lookup step. In the node's Credential settings, click 'Create New' and authenticate with your Notion account — this opens a Notion OAuth screen where you must explicitly share the target database with the integration. Set the Operation to 'Create' and Resource to 'Database Page'. In the 'Database ID' field, paste the ID of your Notion archival database (it's the 32-character string in the database URL after the workspace name and before the ?v= parameter).

  1. 1Add a 'Notion' node after the users.info HTTP Request node
  2. 2Click 'Credential to connect with' and choose 'Create New'
  3. 3Complete the OAuth flow and grant n8n access to your workspace
  4. 4Set Resource to 'Database Page'
  5. 5Set Operation to 'Create'
  6. 6Paste your Notion database ID into the 'Database ID' field
What you should see: The Notion node shows your database name next to the Database ID field after you tab out, confirming n8n can reach it.
Common mistake — You must manually share the Notion database with your n8n integration inside Notion itself — OAuth alone doesn't grant access. In Notion, open the database, click '...' in the top right, go to 'Connections', and add your n8n integration. If you skip this, the node returns a 'Could not find database' 404 error regardless of your credentials being valid.
9

n8n Canvas > Notion Node > Properties > Add Property

Map Slack Fields to Notion Database Properties

Your Notion database needs matching properties before you can map data into it. Create these properties in Notion if they don't exist: Message Text (Text), Author (Text), Channel (Text), Timestamp (Date), Message Link (URL), and Archived By (Text). Back in n8n's Notion node, click 'Add Property' for each field. Map Message Text to {{$node['HTTP Request'].json.messages[0].text}}, Author to {{$node['HTTP Request 1'].json.user.profile.display_name}}, and Timestamp to a formatted date using the Code node output from the pro tip below.

  1. 1In the Notion node, click 'Add Property'
  2. 2Select 'Message Text' and set value to {{$node['HTTP Request'].json.messages[0].text}}
  3. 3Add 'Author' property mapped to {{$node['HTTP Request 1'].json.user.profile.display_name || $node['HTTP Request 1'].json.user.profile.real_name}}
  4. 4Add 'Channel' mapped to {{$json.body.event.item.channel}}
  5. 5Add 'Timestamp' as a Date property with formatted value from the Code node
  6. 6Add 'Message Link' as URL: https://slack.com/archives/{{$json.body.event.item.channel}}/p{{$json.body.event.item.ts.replace('.', '')}}
What you should see: Each property in the Notion node shows a mapped expression in the value field. No empty red-bordered fields remain.
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

n8n Canvas > + > Code Node > JavaScript Mode

Add a Code Node to Format the Slack Timestamp

Slack timestamps look like 1701432000.000000 — a Unix timestamp with microseconds. Notion's Date property needs an ISO 8601 string like 2023-12-01T14:00:00.000Z. Insert a Code node between the user lookup and the Notion node to handle this conversion. Paste the code from the Pro Tip section below. The node outputs a clean isoTimestamp field you'll reference in the Notion node's Timestamp property as {{$node['Code'].json.isoTimestamp}}.

  1. 1Click '+' between the users.info node and the Notion node
  2. 2Add a 'Code' node
  3. 3Set Mode to 'Run Once for Each Item'
  4. 4Paste the timestamp formatting code from the Pro Tip section
  5. 5Click 'Execute Node' to verify the output
What you should see: The Code node output shows a new field isoTimestamp with a value like '2023-12-01T14:00:00.000Z' and a messageLink field with a working Slack URL.
Common mistake — Map fields using the variable picker — don't type field names manually. Hand-typed variable names often have invisible spacing errors that produce blank output.

This Code node runs between the users.info HTTP Request and the Notion node. It converts Slack's raw Unix timestamp to ISO 8601, builds a clean Slack message deep-link URL, and passes all transformed fields forward so your Notion node only needs simple expression references. Paste it into a Code node set to 'Run Once for Each Item' mode.

JavaScript — Code Node// Runs in n8n Code node — 'Run Once for Each Item' mode
▸ Show code
// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node
const webhookData = $('Webhook').item.json.body.event;

... expand to see full code

// Runs in n8n Code node — 'Run Once for Each Item' mode
// Place this node between the users.info HTTP Request and the Notion node

const webhookData = $('Webhook').item.json.body.event;
const messageData = $('HTTP Request').item.json.messages[0];
const userData = $('HTTP Request 1').item.json.user;

// Convert Slack's Unix timestamp (with microseconds) to ISO 8601
const slackTs = messageData.ts; // e.g. '1701432187.000400'
const tsSeconds = parseFloat(slackTs);
const isoTimestamp = new Date(tsSeconds * 1000).toISOString();

// Build a direct Slack message deep-link
// Slack deep-links require the timestamp without the period
const tsForUrl = slackTs.replace('.', '');
const channelId = webhookData.item.channel;
const messageLink = `https://slack.com/archives/${channelId}/p${tsForUrl}`;

// Resolve author name with fallback from real_name if display_name is empty
const authorName = 
  userData.profile.display_name ||
  userData.profile.real_name ||
  'Unknown User';

// Resolve the reactor (person who added the emoji) — separate from message author
const reactorId = webhookData.user;

// Truncate very long messages to 2000 chars (Notion rich text block limit)
const rawText = messageData.text || '';
const messageText = rawText.length > 2000
  ? rawText.substring(0, 1997) + '...'
  : rawText;

// Detect if this message is part of a thread
const threadTs = messageData.thread_ts || null;
const isThreadReply = threadTs !== null && threadTs !== slackTs;

return {
  isoTimestamp,
  messageLink,
  authorName,
  messageText,
  channelId,
  reactorId,
  reactionEmoji: webhookData.reaction,
  isThreadReply,
  threadTs: threadTs ? new Date(parseFloat(threadTs) * 1000).toISOString() : null
};
11

n8n Canvas > Test Workflow button > Active toggle (top bar)

Test End-to-End and Activate the Workflow

With all nodes connected, click 'Test Workflow' in n8n. Go to Slack and add a 📌 reaction to any message in a channel where your bot is present. Return to n8n within 30 seconds — you should see green execution indicators on every node in the chain. Check your Notion database for the new page. If it appears with correct text, author, and timestamp, the workflow is working. Click the toggle at the top of the n8n canvas to set the workflow from Inactive to Active.

  1. 1Click 'Test Workflow' in the n8n top bar
  2. 2In Slack, add a 📌 reaction to any message in a public channel your bot is in
  3. 3Watch each node light up green in n8n (takes 5-10 seconds)
  4. 4Open your Notion database and verify the new page appears
  5. 5Click the 'Inactive' toggle in the top bar to switch the workflow to 'Active'
What you should see: All 8 nodes show green execution status. A new Notion database page exists with the correct message text, author name, channel ID, and a clickable Slack message link.
Common mistake — The bot must be a member of the channel where you test. Add it by typing /invite @n8n Archiver in the channel before testing. If the bot isn't in the channel, the conversations.history API call will return a 'not_in_channel' error even though the reaction event fires correctly.
n8n
▶ Run once
executed
Slack
Notion
Notion
🔔 notification
received

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 n8n for this if you want full control over the API calls and your team has at least one person comfortable reading JSON. This workflow makes three API calls per trigger — Slack's conversations.history, users.info, and Notion's page create — and n8n's HTTP Request node handles all of them without forcing you into pre-built connectors that hide what's actually happening. The Code node for timestamp conversion is genuinely useful here, not just a workaround. The one scenario where you'd skip n8n: if nobody on your team will maintain it. n8n requires a working instance with a public URL, and if your self-hosted setup goes down, you lose Slack messages silently with no alert. In that case, use Make — it runs on Make's infrastructure and doesn't require you to manage a server.

Cost

Cost math: this workflow runs once per reaction event. Each execution calls 3 external APIs and uses 1 Code node. On n8n Cloud's Starter plan at $20/month, you get 2,500 executions included. If your team pins 50 messages per day, that's 1,500 executions per month — well within the free tier. At 100 messages per day (3,000/month), you exceed the limit by 500 executions, adding roughly $5-10/month depending on your plan. Self-hosted n8n has no execution limits at all — your only cost is the server, typically $5-10/month on a small VPS. Make would cost $9/month for up to 10,000 operations (each run uses ~5 operations), making it slightly cheaper at low volume but with no self-hosted option.

Tradeoffs

Make does one specific thing better here: its Slack module has a built-in 'Watch Reactions' trigger that requires zero Slack app configuration — no api.slack.com setup, no scope management. You connect Slack in 30 seconds and pick the emoji from a dropdown. Zapier's Slack trigger for reactions is also plug-and-play but it polls every 5 minutes, meaning your Notion pages appear up to 5 minutes late — unacceptable for time-sensitive decisions. Power Automate has no native Slack connector worth using; you'd end up routing through a custom webhook anyway, which puts you back at the same complexity as n8n but with a worse debugging experience. Pipedream's Slack event source is excellent — it handles the OAuth and webhook verification automatically — but Pipedream's free tier caps at 100 workflow executions per day, which most teams hit within a week. n8n wins for teams who've already invested in self-hosting or want zero per-execution pricing at higher volumes.

Three things you'll hit after setup. First: Slack rate limits conversations.history at 50 requests per minute per workspace. If multiple people react to messages simultaneously, you won't hit this in normal use, but if you're testing repeatedly or have a very active channel, you'll see 429 errors. Add a Wait node set to 1 second between the history call and the users.info call to add breathing room. Second: Notion's text property has a 2,000-character limit per rich text block. Long Slack messages — code snippets, meeting notes — get silently truncated if you don't handle this. The Code node in this guide includes the truncation logic, but watch for it in your Notion pages. Third: the reaction_added webhook fires even for emoji reactions on messages the bot can't read — like messages in channels it hasn't joined. The conversations.history call will return not_in_channel with a 200 OK status (Slack wraps errors in successful HTTP responses), and your workflow will fail silently at that step unless you add an IF node checking $json.ok === true after each Slack API call.

Ideas for what to build next

  • Archive Full Threads, Not Just Single MessagesExtend the HTTP Request step to call conversations.replies with the thread_ts value instead of conversations.history. This pulls every reply in the thread and concatenates them into one Notion page, giving you complete context for decisions made across multiple messages.
  • Route Different Emojis to Different Notion DatabasesAdd a Switch node after the IF filter and branch on the reaction name — 📌 goes to the Decisions Log database, 📚 goes to a Resources database, and 🐛 goes to a Bug Reports database. Each branch gets its own Notion node pointed at a different database ID.
  • Send a Confirmation Reaction Back to SlackAfter the Notion page is created successfully, add a final HTTP Request node calling Slack's reactions.add API to post a ✅ reaction on the original message. This confirms to the team member who pinned it that the archival worked without them leaving Slack.

Related guides

Was this guide helpful?
Slack + Notion overviewn8n profile →