channel/join
Makes the bot join a public Slack channel so it can post messages, read history, and manage membership. This is a bot-self-action — use channel/invite to add other users. Works only on public channels; private channels require invitation. Requires channels:join scope.
When to Use
- Pre-post bot join: Ensure the bot is a member of a monitoring or alerting channel before attempting to post alerts to it — prevents
not_in_channelerrors. - New channel entry: After a new project channel is created by a user (not by the bot), join it to begin monitoring or posting workflow notifications.
- Rejoin after removal: If the bot was manually removed from a channel, use this node to re-enter it as part of a health-check or recovery workflow.
- Announcement channel posting: Join a high-visibility announcement channel to post time-sensitive updates without requiring manual bot configuration.
- Content moderation: Join community channels to monitor and moderate content via automated flagging workflows.
Configuration
Connection
| Field | Type | Required | Description |
|---|---|---|---|
botToken | string | Required | Bot Token starting with xoxb-. Requires channels:join scope. Only works on public channels. |
Operation Fields
| Field | Type | Required | Description |
|---|---|---|---|
channelId | string | Required | The public channel ID to join (starts with C). The bot cannot join private channels — it must be invited. |
Public Channels Only:
channel/join only works for public channels. To add the bot to a private channel, a workspace admin must invite it manually, or use channel/invite from an admin bot token with appropriate scopes.
Sample Configuration
{
"operation": "channel/join",
"connection": {
"botToken": "{{credentials.slackBotToken}}"
},
"fields": {
"channelId": "{{$json.channelId}}"
}
}
Validation Errors
| Error Code | Cause | Resolution |
|---|---|---|
channel_not_found | The channel ID does not exist or is a private channel. | Verify the channel ID. For private channels, the join operation is not available — the bot must be invited. |
missing_scope | Bot token lacks channels:join scope. | Re-authorize the Slack app with the channels:join scope. |
is_archived | The channel is archived and cannot be joined. | Unarchive the channel first with channel/unarchive, then join. |
Output
Success Port
| Field | Type | Description |
|---|---|---|
channelId | string | The channel the bot has joined. |
status | string | success or error. |
errorCode | string | Slack error code when status is error. |
payload | object | Full raw Slack API response including channel details post-join. |
Error Port
On failure with Continue on Error enabled, routes to the error port with status: "error" and populated errorCode.
Sample Output
{
"channelId": "C08AB3XYZ12",
"status": "success",
"errorCode": "",
"payload": {
"ok": true,
"channel": {
"id": "C08AB3XYZ12",
"name": "monitoring-alerts",
"is_member": true
},
"already_in_channel": false
}
}
Expression Reference
| Expression | Result |
|---|---|
{{$node["channel-join"].json.channelId}} | The channel ID joined — pass to subsequent message/send or channel/getHistory nodes. |
{{$node["channel-join"].json.status === "success"}} | Boolean gate — confirm join before proceeding with channel operations. |
{{$node["channel-join"].json.payload.already_in_channel}} | True if the bot was already a member — use to skip redundant join notifications. |
Node Policies & GuardRails
| Policy Area | Recommendation |
|---|---|
| Scope requirements | Requires channels:join scope. This only works for public channels — private channel access requires an invitation. |
| Rate limiting | Tier 2 (20+ req/min). For workflows that join many channels, add delays between join calls to avoid rate limiting. |
| No hard-coded IDs | Always source channelId dynamically from upstream node output. Never hard-code channel IDs. |
| Idempotent joins | Joining a channel the bot is already in returns success with already_in_channel: true. This is safe to call repeatedly — use it as a guard before posting. |
| Archive before join | Check isArchived using channel/get before joining. You cannot join an archived channel — unarchive first. |
| Channel proliferation | Avoid joining every channel automatically. The bot should only join channels where it has a specific purpose to avoid cluttering member lists. |
Examples
Example 1 — Ensure Bot Membership Before Alerting
A monitoring pipeline that joins the alert channel before posting — safe to call even if already a member.
// Node 1: channel/join (idempotent guard)
{ "channelId": "C08MONITORING" }
// Node 2: message/send (now guaranteed to succeed)
{
"channelId": "C08MONITORING",
"text": "ALERT: CPU usage at {{$json.cpuPercent}}% on {{$json.host}}"
}
Example 2 — Join All New Project Channels
Triggered by a project creation event — the project tool creates the Slack channel, then this workflow ensures the bot joins it.
// Trigger: webhook from project management tool
// $json.slackChannelId = newly created channel
// Check if archived first
{ "operation": "channel/get", "fields": { "channelId": "{{$json.slackChannelId}}" } }
// IF: isArchived === false
// True → channel/join
{ "operation": "channel/join", "fields": { "channelId": "{{$json.slackChannelId}}" } }
Example 3 — Bot Recovery After Removal
Health-check workflow that verifies the bot is a member of all required channels and rejoins any it was removed from.
// Required channels list from config
const requiredChannels = ["C08ALERTS01", "C08DEPLOYS01", "C08ANNOUNCE01"];
// channel/getMany to get current bot channels
// Code node: find channels bot is NOT in
const missing = requiredChannels.filter(
id => !$node["bot-channels"].json.channels.some(c => c.channelId === id)
);
// Loop: channel/join for each missing channel