Artur
Artur
Founder

n8n Slack Integration: Notifications & Bot Workflow (2026)

June 19, 2026

n8n-slackslack-integrationblock-kitslack-triggerworkflow-automation

TL;DR: There are two distinct n8n Slack jobs and they need different setups. For sending notifications and alerts, use the Slack node with OAuth2 credentials and the Message resource - and send Block Kit JSON, not plain text, the moment your message has more than one line. For a bot that reacts to Slack events, use the Slack Trigger node, which requires an API access token (not OAuth2) plus Event Subscriptions on a Slack app. Both share one Slack app, but the trigger and the action use different credential types. Below is a working Block Kit example, the exact scopes, and the failure modes that aren't in the happy-path docs.

The two builds, and why they don't share a credential

Most "n8n Slack" tutorials blur two completely different things: pushing messages out, and reacting to things happening in Slack. They have different nodes and, importantly, different credential requirements.

Sending out is the Slack node (an action node). Reacting is the Slack Trigger node. The catch that trips people up: per n8n's own credential docs, OAuth2 is the preferred method for the Slack action node but is incompatible with the Slack Trigger, which needs an API access token. So a workflow that both listens and replies often ends up with two Slack credentials pointing at the same Slack app.

Decide which build you need before touching credentials:

  • You want alerts, daily digests, error notifications, or approval prompts pushed into a channel. That is the Slack node. Build one.

  • You want something to fire when a user posts, mentions your bot, or reacts. That is the Slack Trigger. Build two.

Setting up the Slack app and credentials

You create one Slack app and reuse it. Go to api.slack.com/apps, choose Create New App, From scratch, name it, and pick your workspace.

For the Slack node (sending), use OAuth2 if you can. When self-hosting n8n, copy the Client ID and Client Secret from the app's Basic Information page into the n8n credential, then copy n8n's OAuth Callback URL into the app under OAuth & Permissions, Redirect URLs. Add your scopes, install to the workspace, and authorize.

For the Slack Trigger (listening), you need the API access token method. Under OAuth & Permissions, add bot scopes, click Install to Workspace, then copy the Bot User OAuth Token (it starts with xoxb-) into the n8n access-token credential.

The scopes you actually need

Don't paste every scope. Add what the operations you use require. n8n's credential docs list these bot scopes as the working set, and you should treat it as a menu, not a checklist:

channels:read, channels:history, chat:write, files:read, files:write, groups:read, groups:history, im:read, im:history, mpim:read, mpim:history, reactions:read, reactions:write, usergroups:read, usergroups:write, users.profile:read, users:read, and search:read.

For a send-only notification workflow, chat:write plus channels:read covers most of it. The Slack Trigger documents a hard minimum of conversations.list and users:list, with extra scopes depending on the event - for example, reaction events need reactions:read, and reading message content needs the matching *:history scope.

One real gotcha: after you add a scope to an installed app, you must reinstall the app to the workspace. New scopes do not take effect on an existing install. This is the single most common reason a Slack node suddenly returns a missing_scope error after working fine.

Build one: rich notifications with Block Kit

Send Block Kit JSON, not the Text field, as soon as your message has structure. The Text field is fine for a one-line "deploy finished." Anything with a header, fields, a divider, or a button needs blocks - and Slack's plain text rendering will mangle multi-line content in ways Block Kit won't.

Configure the Slack node with the Message resource and the Send operation. Pick the channel by name, ID, or URL. Then set the message type to Blocks and paste a Block Kit payload. Here is a deploy-status alert that renders cleanly:

{
  "blocks": [
    {
      "type": "header",
      "text": { "type": "plain_text", "text": "Deploy finished: production" }
    },
    {
      "type": "section",
      "fields": [
        { "type": "mrkdwn", "text": "*Service:*\napi-gateway" },
        { "type": "mrkdwn", "text": "*Status:*\nSuccess" },
        { "type": "mrkdwn", "text": "*Commit:*\n`a1b2c3d`" },
        { "type": "mrkdwn", "text": "*Duration:*\n2m 14s" }
      ]
    },
    { "type": "divider" },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": { "type": "plain_text", "text": "View logs" },
          "url": "https://example.com/logs/run/8821",
          "style": "primary"
        }
      ]
    }
  ]
}

A few things that aren't obvious. When you send blocks, Slack ignores your Text field for the visible message, but you should still set it - it becomes the notification preview and the accessibility fallback. Build and preview payloads in Slack's Block Kit Builder before pasting them into n8n; debugging a malformed block from inside an n8n execution log is slower than catching it in the builder.

If the message body is dynamic, generate the blocks array in a Code node upstream and pass it in via an expression rather than hand-templating JSON with inline expressions. String-concatenating Block Kit JSON with {{ }} expressions is where most "invalid_blocks" errors come from. For the patterns to assemble that array cleanly, see our n8n Code node examples.

Threading: keep noise out of the channel

Thread follow-up messages instead of flooding the channel. The Send operation exposes a Reply to a message thread option (it maps to Slack's thread_ts). The flow is: send the parent message, read the timestamp from the node output, then send subsequent updates with that timestamp as the thread parent.

This is the right pattern for anything with a lifecycle - a job that posts "started," then "running," then "done" should post the first message to the channel and thread the rest. A monitoring alert that recovers should thread the "resolved" note under the original "firing" alert so on-call can read the whole incident in one place. The output of the first Send node contains the ts value; map it into the thread field of the later nodes.

Build two: a bot that responds to events

Use the Slack Trigger node, and reach for a plain Webhook node only when you need something the Slack Trigger doesn't model. The Slack Trigger wraps Slack's Events API and handles the subscription handshake and signature verification for you. A raw Webhook node makes you implement Slack's URL verification challenge and HMAC signature check by hand. Use the Slack Trigger unless you're doing interactivity that needs a custom response shape (slash command payloads, interactive message acks) where you need full control of the HTTP response.

The Slack Trigger supports these events: Any Event, App Home Opened, Bot / App Mention, File Made Public, File Shared, New Message Posted to Channel, New Public Channel Created, New User, and Reaction Added.

To build a mention bot, drop a Slack Trigger node, pick the access-token credential, and select Bot / App Mention. By default the trigger watches a single channel you specify by list, ID, or URL. There is a Watch Whole Workspace option that listens across every channel the app is in - n8n's docs flag that it bills one execution per event, so on a busy workspace it can generate a lot of runs. Leave it off unless you genuinely need workspace-wide coverage.

After the trigger, parse the event, do your logic, then respond with a Slack node (Message, Send) threaded under the incoming message. A minimal mention-responder is three nodes: Slack Trigger (App Mention), an optional Code or AI node to build the reply, and a Slack node that sends back into the same thread using the incoming event's timestamp.

Wire up Event Subscriptions and verify signatures

The trigger won't fire until Slack is pointed at it. In your Slack app under Event Subscriptions, enable events and set the Request URL to the production webhook URL from the n8n Slack Trigger node. Subscribe to the specific bot events you need (for a mention bot, app_mention). Then reinstall the app so the new event subscription and any new scopes take effect.

Turn on signature verification. From n8n version 1.106.0, you can set a Slack Signing Secret in the Slack credential - copy the Signing Secret from the app's Basic Information page into the credential's signature field. With it set, the Slack Trigger automatically rejects requests that don't carry a valid Slack signature, which closes the door on anyone hitting your public webhook URL directly. If you're hand-rolling a Webhook node instead, you own that verification - the mechanics are covered in our n8n webhook node guide.

The gotchas that actually bite

Most n8n Slack failures come from a short list of issues, and almost none of them show up in a clean tutorial.

The "works in testing but not production" trap is the big one. The Slack Trigger has separate test and production webhook URLs. If you registered the test URL in Slack's Event Subscriptions, events stop the moment you deactivate test mode. Register the production URL, and make sure the workflow is active.

Token expired errors usually mean the OAuth install went stale or the app was reinstalled elsewhere - reconnect the credential. Missing scope errors, as noted, almost always mean you added a scope but never reinstalled the app. And a bot that can't post to a channel it isn't a member of will fail silently-ish with a not_in_channel error; invite the bot to the channel first.

Rate limits are real on high-volume sends. Slack's chat.postMessage has per-channel limits, so a workflow looping over hundreds of items and posting one message each will get throttled. Batch into a single Block Kit message where you can, or add a small wait between sends rather than firing them as fast as the loop runs.

Choosing between the two, fast

If you only push messages, you don't need the Slack Trigger or its access token at all - one OAuth2 credential and the Slack node is the whole build. If you only react to Slack, you still often want both credentials, because your reaction is usually "post a reply." The common production shape is one Slack app, two n8n credentials (OAuth2 for sending, access token for the trigger), and a workflow that listens with the trigger and answers with the action node.

If you want a Slack integration built and handed over running - notifications, an event bot, or both - that is the kind of thing we set up. No obligation to read further than this: the builds above are enough to ship something yourself this afternoon.

FAQ

Do I need OAuth2 or a bot token for n8n Slack?

Both, depending on the node. The Slack action node (sending) prefers OAuth2. The Slack Trigger node (listening) requires an API access token, the xoxb- Bot User OAuth Token, and is incompatible with OAuth2. A workflow that both listens and replies typically uses two credentials against one Slack app.

What scopes does the n8n Slack node need?

Add only the scopes your operations use. For send-only notifications, chat:write and channels:read cover most cases. The Slack Trigger needs at minimum conversations.list and users:list, plus event-specific scopes like reactions:read. After adding any scope, reinstall the app to the workspace or it won't take effect.

How do I send Block Kit messages from n8n?

In the Slack node, choose the Message resource and Send operation, set the message type to Blocks, and paste a Block Kit JSON payload. Build the payload in Slack's Block Kit Builder first, and still set the Text field as the notification fallback. For dynamic content, assemble the blocks array in a Code node rather than templating JSON inline.

Why does my Slack Trigger work in test but not when active?

The Slack Trigger has separate test and production webhook URLs. If you registered the test URL in your Slack app's Event Subscriptions, events stop once you leave test mode. Register the production webhook URL in Slack and make sure the n8n workflow is activated.

Can I reply in a Slack thread instead of the main channel?

Yes. The Send operation has a Reply to a message thread option that maps to Slack's thread_ts. Send the parent message, read the ts value from the node output, then send follow-ups with that timestamp as the thread parent. This is the right pattern for any message with a lifecycle, like job-status or incident updates.

Should I use the Slack Trigger or a plain Webhook node?

Use the Slack Trigger for almost everything - it handles Slack's Events API subscription handshake and, from version 1.106.0, signature verification via a Signing Secret. Reach for a raw Webhook node only when you need full control of the HTTP response, such as slash command or interactive component acknowledgements.


n8n Slack Integration: Notifications & Bot Workflow (2026) | n8nlogic