Skip to main content

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

  1. Go to Slack API
  2. Click “Create New App” → “From scratch”
  3. Name your app and select workspace
  4. Navigate to “OAuth & Permissions”
  5. Add Bot Token Scopes:
    • channels:history
    • channels:read
    • chat:write
    • groups:history
    • groups:read
    • im:history
    • im:read
    • im:write
    • mpim:history
    • mpim:read
    • users:read (for display names)
  6. Install app to workspace
  7. Copy “Bot User OAuth Token” (starts with xoxb-)
For real-time WebSocket connections:
  1. In your Slack app, go to “Socket Mode”
  2. Enable Socket Mode
  3. Generate App-Level Token:
    • Name: “ZeroClaw Socket”
    • Scopes: connections:write
  4. Copy token (starts with xapp-)
  5. Add to config:
  1. Subscribe to Bot Events:
    • message.channels
    • message.groups
    • message.im
    • message.mpim

User Authorization

Slack uses user IDs (format: U123ABC456):
To find user IDs:
  1. Click user’s profile in Slack
  2. Click ”⋯ More” → “Copy member ID”
Or use API:

Features

Transport Modes

Socket Mode (WebSocket)

Enabled when app_token is configured:
Advantages:
  • Real-time message delivery
  • No polling overhead
  • Instant acknowledgments
  • Lower latency
How it works:
  1. Opens WebSocket connection via apps.connections.open
  2. Receives events in real-time
  3. Sends acknowledgment for each envelope
  4. Auto-reconnects on disconnect
  5. Handles ping/pong for keepalive

Polling Mode

Used when app_token is not set: Behavior:
  • Polls conversations.history every 3 seconds
  • Tracks last seen timestamp per channel
  • Discovers new channels every 60 seconds (when channel_id not set)
  • Rate limit aware with exponential backoff

Channel Scope

Single Channel

Listens only to that channel.

Multiple Channels

All Accessible Channels

Auto-discovers and listens to:
  • Public channels (bot is member)
  • Private channels (bot is invited)
  • DMs
  • Multi-person DMs
Refreshes channel list every 60 seconds.

Display Name Resolution

The channel automatically resolves user IDs to display names:
Resolution order:
  1. profile.display_name
  2. profile.display_name_normalized
  3. profile.real_name_normalized
  4. profile.real_name
  5. user.real_name
  6. user.name
  7. Falls back to user ID if all fail
Caching:
  • Cached for 6 hours
  • Reduces API calls
  • Automatic expiration

Thread Support

Slack threads are fully supported: Incoming:
  • thread_ts extracted from messages
  • Top-level messages use ts as thread identifier
  • Replies include thread_ts from original message
Outgoing:
If incoming message has thread_ts, reply uses same thread.

Group Chat Control

Mention-Only Mode

Bot only responds when:
  • Explicitly mentioned: <@BOT_USER_ID> hello
  • In DMs
  • Sender is in allowed_sender_ids

VIP Senders

These users bypass mention requirements in channels.

Rate Limit Handling

Automatic handling for conversations.history rate limits: Detection:
  • HTTP 429 status
  • error: "rate_limited" in response
Retry Strategy:
  1. Parse Retry-After header (seconds or decimal)
  2. Exponential backoff: retry_after * 2^attempt
  3. Cap at 120 seconds
  4. Add jitter (0-500ms) derived from system time
  5. Max 3 retries before giving up
Logging:

Implementation Details

Source Location

src/channels/slack.rs (1425 lines)

Key Components

SlackChannel Struct

Socket Mode Flow

  1. Call apps.connections.open → get WebSocket URL
  2. Connect to WebSocket
  3. For each envelope:
    • Parse envelope_id and type
    • Handle event (if type = "events_api")
    • Send acknowledgment: {"envelope_id": "..."}
  4. Handle disconnect type → reconnect
  5. On error or close → wait 3s and restart

Polling Mode Flow

  1. Get bot user ID via auth.test
  2. List accessible channels via conversations.list (if wildcard)
  3. For each channel:
    • Track cursor timestamp (bootstrap to current time)
    • Fetch conversations.history with oldest=cursor
    • Process messages (newest-first, reverse to oldest-first)
    • Update cursor to newest message ts
    • Skip subtypes (system messages)
  4. Sleep 3 seconds
  5. Repeat

API Endpoints Used

Channel ID Prefixes

  • C... - Public channel
  • G... - Private channel (group)
  • D... - Direct message
Group detection: Checks if first char is C or G.

Message Deduplication

Prevents processing same message twice: Polling Mode:
  • Tracks last_ts per channel
  • Skips messages with ts <= last_ts
  • Bootstrap cursor to current time on first poll
Socket Mode:
  • Tracks last_ts per channel
  • Skips messages with ts <= last_ts
  • Persistent across WebSocket reconnects

Error Handling

Common Errors

Invalid Token:
Solution: Regenerate bot token Missing Scopes:
Solution: Add required OAuth scopes in Slack app settings Socket Mode Not Enabled:
Solution: Enable Socket Mode in app settings Rate Limited:
Solution: Automatic retry with backoff (no action needed) Channel Not Found:
Solution: Verify bot is member of channel

Error Sanitization

Removes:
  • 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

  1. Use Socket Mode: Better performance and lower latency
  2. Scope Channels: Use channel_id if possible
  3. User IDs: More stable than usernames
  4. Allowlist: Start specific, expand as needed
  5. Display Names: Cached automatically for 6 hours
  6. Mention Mode: Use in public channels to reduce noise

Troubleshooting

Bot Doesn’t Respond

Check Allowlist:
Check Logs:
Look for:
  • Slack channel listening on #channel-name
  • Slack: ignoring message from unauthorized user:
  • Slack poll error:
Check Bot Membership:
  • Invite bot to channel: /invite @YourBot
  • Verify bot appears in channel members

Socket Mode Not Working

App Token Missing:
Wrong Scopes: App-level token needs connections:write Event Subscriptions: Enable in Slack app settings:
  • message.channels
  • message.groups
  • message.im
  • message.mpim

Polling Mode Too Slow

Use Socket Mode: Add app_token for real-time delivery Reduce Channels: Set specific channel_id instead of wildcard

Display Names Not Resolving

Missing Scope: Add users: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
Polling Mode:
  • 3-second poll interval
  • Per-channel sequential processing

Optimization Tips

  1. Socket Mode: Use when available
  2. Channel Scope: Reduce with channel_id
  3. Display Name Cache: 6-hour TTL reduces API calls
  4. 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

Channel Filtering

Archived Channels: Automatically excluded Non-Member Channels: Automatically excluded Private Channels: Only if bot is invited

See Also