Send a Slack message from a bot
chat.postMessage returns HTTP 200 even when it fails — so a bot that looks like it “works” is often silently dropping messages with not_in_channel. These are the failure modes documented in Waymark's live route corpus, for Slack and the other chat platforms agents post to.
Sourced from 4 live route records · regenerated 2026-07-15
Documented failure modes
slack.comsampled
- Slack returns HTTP 200 with {"ok":false,"error":"not_in_channel"} — always check the ok field, not the status code
- Channel NAMES don't work reliably; resolve to channel ID via conversations.list
- Rate limit ~1 msg/sec/channel (tier-based); on 429 honor Retry-After
microsoft-graphsampled
- Application permissions cannot send channel messages (only migration imports) — use delegated auth or channel webhooks
- Incoming Webhooks (O365 connectors) were deprecated 2024+ in favor of Workflows-based webhooks
- HTML body is sanitized; unsupported tags are stripped silently
discord.comsampled
- Embeds are capped: 10 embeds, 6000 chars total across all embeds
- Webhook URLs are credentials — anyone with the URL can post; rotate if leaked
- 2000-char content limit; longer must be split or sent as a file
telegram.orgsampled
- MarkdownV2 requires escaping . - ! ( ) and other special chars — unescaped text causes 400 Bad Request
- Group chat_ids are negative numbers; supergroup ids start with -100
- Bots can message groups only after being added; privacy mode hides non-command messages unless disabled
The working procedure
- Create an app, add bot scope chat:write, install to workspace to get xoxb- token
- Invite the bot to the channel (/invite @bot) or use chat:write.public scope for public channels
- POST chat.postMessage with channel id (C...) and text or blocks
- Check ok:true in the JSON body — HTTP status is 200 even on failure
Source: Send a Slack message to a channel via Web API — a live route record (status: sampled). Route content is community/operator contributed and re-checked over time; read the live record for the current version.
How agents avoid this automatically
Agents wired to Waymark's MCP server pull this route (and its gotchas) at plan time instead of rediscovering the failure modes in production. Reads are free and keyless.
# Any MCP client — one call, no key:
waymark_query({ "task": "send a slack message to a channel from a bot" })
# Or plain HTTP:
curl -s "https://mcp.waymark.network/search?q=send%20a%20slack%20message%20to%20a%20channel%20from%20a%20bot"