Slack Channel
The Slack channel enables ZeroClaw to communicate via Slack’s API with support for both Socket Mode (WebSocket) and polling modes, thread support, and user display name resolution.Overview
- Channel Name:
slack - Transport: Socket Mode (WebSocket) or REST API polling
- Authentication: Bot token (xoxb-) + optional App token (xapp-)
- Public Port Required: No
- Supports: Text messages, threads, channel discovery, display names
Configuration
Required Settings
Complete Configuration
Environment Variables
Authentication
Creating a Slack App
- Go to Slack API
- Click “Create New App” → “From scratch”
- Name your app and select workspace
- Navigate to “OAuth & Permissions”
- Add Bot Token Scopes:
channels:historychannels:readchat:writegroups:historygroups:readim:historyim:readim:writempim:historympim:readusers:read(for display names)
- Install app to workspace
- Copy “Bot User OAuth Token” (starts with
xoxb-)
Socket Mode Setup (Recommended)
For real-time WebSocket connections:- In your Slack app, go to “Socket Mode”
- Enable Socket Mode
- Generate App-Level Token:
- Name: “ZeroClaw Socket”
- Scopes:
connections:write
- Copy token (starts with
xapp-) - Add to config:
- Subscribe to Bot Events:
message.channelsmessage.groupsmessage.immessage.mpim
User Authorization
Slack uses user IDs (format:U123ABC456):
- Click user’s profile in Slack
- Click ”⋯ More” → “Copy member ID”
Features
Transport Modes
Socket Mode (WebSocket)
Enabled whenapp_token is configured:
- Real-time message delivery
- No polling overhead
- Instant acknowledgments
- Lower latency
- Opens WebSocket connection via
apps.connections.open - Receives events in real-time
- Sends acknowledgment for each envelope
- Auto-reconnects on disconnect
- Handles ping/pong for keepalive
Polling Mode
Used whenapp_token is not set:
Behavior:
- Polls
conversations.historyevery 3 seconds - Tracks last seen timestamp per channel
- Discovers new channels every 60 seconds (when
channel_idnot set) - Rate limit aware with exponential backoff
Channel Scope
Single Channel
Multiple Channels
All Accessible Channels
- Public channels (bot is member)
- Private channels (bot is invited)
- DMs
- Multi-person DMs
Display Name Resolution
The channel automatically resolves user IDs to display names:profile.display_nameprofile.display_name_normalizedprofile.real_name_normalizedprofile.real_nameuser.real_nameuser.name- Falls back to user ID if all fail
- Cached for 6 hours
- Reduces API calls
- Automatic expiration
Thread Support
Slack threads are fully supported: Incoming:thread_tsextracted from messages- Top-level messages use
tsas thread identifier - Replies include
thread_tsfrom original message
thread_ts, reply uses same thread.
Group Chat Control
Mention-Only Mode
- Explicitly mentioned:
<@BOT_USER_ID> hello - In DMs
- Sender is in
allowed_sender_ids
VIP Senders
Rate Limit Handling
Automatic handling forconversations.history rate limits:
Detection:
- HTTP 429 status
error: "rate_limited"in response
- Parse
Retry-Afterheader (seconds or decimal) - Exponential backoff:
retry_after * 2^attempt - Cap at 120 seconds
- Add jitter (0-500ms) derived from system time
- Max 3 retries before giving up
Implementation Details
Source Location
src/channels/slack.rs (1425 lines)
Key Components
SlackChannel Struct
Socket Mode Flow
- Call
apps.connections.open→ get WebSocket URL - Connect to WebSocket
- For each envelope:
- Parse
envelope_idandtype - Handle event (if
type = "events_api") - Send acknowledgment:
{"envelope_id": "..."}
- Parse
- Handle
disconnecttype → reconnect - On error or close → wait 3s and restart
Polling Mode Flow
- Get bot user ID via
auth.test - List accessible channels via
conversations.list(if wildcard) - For each channel:
- Track cursor timestamp (bootstrap to current time)
- Fetch
conversations.historywitholdest=cursor - Process messages (newest-first, reverse to oldest-first)
- Update cursor to newest message
ts - Skip subtypes (system messages)
- Sleep 3 seconds
- Repeat
API Endpoints Used
| Method | Purpose |
|---|---|
auth.test | Get bot user ID |
conversations.list | Discover channels |
conversations.history | Fetch messages (polling) |
chat.postMessage | Send messages |
users.info | Get user display name |
apps.connections.open | Get Socket Mode URL |
Channel ID Prefixes
C...- Public channelG...- Private channel (group)D...- Direct message
C or G.
Message Deduplication
Prevents processing same message twice: Polling Mode:- Tracks
last_tsper channel - Skips messages with
ts <= last_ts - Bootstrap cursor to current time on first poll
- Tracks
last_tsper channel - Skips messages with
ts <= last_ts - Persistent across WebSocket reconnects
Error Handling
Common Errors
Invalid Token:Error Sanitization
- Authorization headers
- Token values
- Internal details
Runtime Commands
Available in Slack:/new- Start new conversation/model- Show/switch current model/models- Show/switch provider
Best Practices
- Use Socket Mode: Better performance and lower latency
- Scope Channels: Use
channel_idif possible - User IDs: More stable than usernames
- Allowlist: Start specific, expand as needed
- Display Names: Cached automatically for 6 hours
- Mention Mode: Use in public channels to reduce noise
Troubleshooting
Bot Doesn’t Respond
Check Allowlist:Slack channel listening on #channel-nameSlack: ignoring message from unauthorized user:Slack poll error:
- Invite bot to channel:
/invite @YourBot - Verify bot appears in channel members
Socket Mode Not Working
App Token Missing:connections:write
Event Subscriptions:
Enable in Slack app settings:
message.channelsmessage.groupsmessage.immessage.mpim
Polling Mode Too Slow
Use Socket Mode: Addapp_token for real-time delivery
Reduce Channels:
Set specific channel_id instead of wildcard
Display Names Not Resolving
Missing Scope: Addusers:read to bot token scopes
Cache Expiration:
Display names cached for 6 hours, then refetched
Performance
Message Throughput
Socket Mode:- Real-time delivery (<100ms)
- No polling overhead
- 3-second poll interval
- Per-channel sequential processing
Optimization Tips
- Socket Mode: Use when available
- Channel Scope: Reduce with
channel_id - Display Name Cache: 6-hour TTL reduces API calls
- Rate Limit Backoff: Automatic, no tuning needed
Security
Token Protection
- Bot tokens: Never logged
- App tokens: Never logged
- Sanitized from error messages
User Verification
- Allowlist checked before processing
- Bot self-messages always ignored
- Channel scope enforced