Slack
Send and receive Slack messages as your agent — one workspace per project
Slack gives your agent a presence in your team's workspace. Connect a workspace once, /invite the bot to the channels you care about, and the agent can post messages, reply in threads, and poll for anything said in those channels.
Slack is in admin preview. It's merged and documented, but only enabled for admin accounts while we finish hardening it.
How It Works
Connect: POST /v0/slack/connect → install URL → human authorizes in browser
Send: Agent calls slack.messages.send → Mailgent posts via the bot token
Receive: Slack events → stored server-side → agent polls with a since cursorOne Slack workspace per identity. The bot only sees channels it has been /invite'd to.
Connecting a Workspace
Two ways, same result:
- Console — open console.mailgent.dev → your project → Integrations and click Add to Slack.
- API —
POST /v0/slack/connectreturns aninstallUrl(valid 15 minutes). Open it in a browser and authorize the workspace. The connection appears onGET /v0/slack/connectiononce authorized.
Either way, a human authorizes the install in a browser — the agent never handles the OAuth exchange. After connecting, /invite the bot to each channel it should read or post in.
Inbound Is Store-and-Poll
Messages from channels the bot is in are stored server-side, like a mail inbox. Agents don't need a webhook or a socket — they poll:
GET /v0/slack/messages?since=2026-06-12T09:00:00ZKeep the timestamp of your last check and pass it as since to fetch only what's new. Reply in-thread by passing the message's ts as threadTs when sending.
Scopes
| Scope | Allows |
|---|---|
slack:read | Read connection status, list channels, poll stored messages |
slack:send | Connect/disconnect the workspace, send messages |
MCP Tools
| Tool | Scope | Description |
|---|---|---|
slack_list_channels | slack:read | Channels the bot can see, with membership |
slack_list_messages | slack:read | Poll stored inbound messages (since cursor) |
slack_send_message | slack:send | Post a message, optionally in a thread |
API Endpoints
| Method | Path | Scope | Description |
|---|---|---|---|
GET | /v0/slack/connection | slack:read | Connection status (never includes the token) |
POST | /v0/slack/connect | slack:send | Get an "Add to Slack" install URL |
DELETE | /v0/slack/connection | slack:send | Disconnect the workspace |
GET | /v0/slack/channels | slack:read | Channels the bot can see |
POST | /v0/slack/messages | slack:send | Send a message {channel, text, threadTs?} |
GET | /v0/slack/messages | slack:read | List stored inbound messages (channel, since, limit) |
Example: Send a Message
curl -X POST https://api.mailgent.dev/v0/slack/messages \
-H "Authorization: Bearer mgnt-your-api-key" \
-H "Content-Type: application/json" \
-d '{ "channel": "C0123456789", "text": "Deploy finished ✅" }'Returns { "channel": "C0123456789", "ts": "1718180400.000100" }. Pass that ts as threadTs in a follow-up call to reply in the same thread. If you get not_in_channel, /invite the bot to the channel first.
Example: Poll for New Messages
curl "https://api.mailgent.dev/v0/slack/messages?channel=C0123456789&since=2026-06-12T09:00:00Z&limit=50" \
-H "Authorization: Bearer mgnt-your-api-key"Returns stored messages newest first, each with channelId, userId, text, ts, and threadTs.
Security
- Bot tokens are sealed server-side — encrypted with AES-256-GCM and never returned by any endpoint, including
GET /v0/slack/connection. - Human-in-the-loop install — the OAuth authorization always happens in a browser; agents only receive an install URL.
- Channel-scoped by invitation — the bot only sees channels it has been /invite'd to.
- Audit trail — connects, disconnects, and sends show up in the activity log.