Slack Sensor
What it does
Two paths on one source:
- Ingest — polls public channels you list via
conversations.history. New messages become conversation events in Corveil. - Chat — a separate Events API path at
POST /webhooks/slack/{token}.@corveilmentions enqueue achat-replyworker run; Corveil posts a threaded reply. A later reply in that channel thread without@is ignored —@again to continue. Members can also DM the bot directly: a first DM starts a session the same way an@does in a channel, and further DM messages stay follow-ups without another@. DMs do not use thechannelsallowlist. The poller does not scrape@corveilout of channel history.
Credentials
The source references two vault credentials by id:
| Credential | Required | Secret |
|---|---|---|
slack_bot_token | yes | Bot User OAuth Token (xoxb-…) |
slack_signing_secret | yes for chat | Signing Secret from the Slack app Basic Information page — not the bot token |
The bot token needs these scopes:
channels:history,channels:read,users:read,users:read.email(ingest)app_mentions:read,chat:write,reactions:write(@corveilchat)im:history,im:write(DMs)groups:history(private-channel Events API / ingest; chat still requires@)
curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"slack_bot_token","name":"acme-slack-bot","secret":"xoxb-..."}'
curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"slack_signing_secret","name":"acme-slack-signing","secret":"..."}'Config fields
| Key | Required | Notes |
|---|---|---|
channels | yes | Comma-separated channel names without the leading #, e.g. general, engineering. Invite the bot to each channel first. Applies to ingest polling only — DMs bypass this allowlist. |
Provider-side setup
- Create a Slack app at api.slack.com/apps for your workspace (the bundled app manifest in the repo pre-configures scopes and bot events).
- Under OAuth & Permissions, confirm the bot scopes listed above are present.
- Install the app to the workspace and copy the Bot User OAuth Token (
xoxb-…) and the Signing Secret from Basic Information. - Invite the bot to each channel you listed (
/invite @yourbot) — the bot can only read history for channels it's a member of. - If you updated the manifest to add chat scopes (
app_mentions:read,chat:write,reactions:write,im:history,im:write,groups:history), reinstall the app so Slack issues a token with the new scopes. Reinstall is only for scope changes — rotating the Request URL does not require it.
Wire-up
POST /api/sensors
{
"sensor_type": "slack",
"name": "Acme Slack",
"config": { "channels": "general, engineering" },
"credentials": {
"token": "<slack_bot_token credential id>",
"signing_secret": "<slack_signing_secret credential id>"
}
}The create response includes webhook_url once. Finish chat setup in the Slack app:
- Event Subscriptions → enable → Request URL = that
webhook_url. - Under Subscribe to bot events, add:
app_mentionmessage.channelsmessage.groupsmessage.im
- Save. Slack sends a
url_verificationchallenge; Corveil echoes the challenge only when the source has a validslack_signing_secretwired in — without it the handler returns 401 and Slack will not accept the URL. - App Home → Messages tab → enable Allow users to send Slash commands and messages from the messages tab. Without this, Slack never delivers
message.imand DMs are inert.
HTTPS required
Slack Event Subscriptions reject http:// Request URLs. Behind a TLS-terminating ALB, Corveil may mint an http:// URL from the inbound request unless UI_BASE_URL is set to your public https:// origin. Pin UI_BASE_URL on the Corveil deployment before copying the webhook URL into Slack.
If you lose the URL, do not recreate the source or the Slack app: Edit sensor → Get webhook URL (or POST /api/sensors/{id}/rotate-webhook) issues a new URL and immediately invalidates the previous one. Paste only the new Request URL — do not reinstall the Slack app.
Chat (@corveil and DMs)
When someone @corveils in a channel the bot is in, or DMs the bot (including further messages in that DM thread):
- Corveil verifies the Slack request signature (hence the signing secret).
- Corveil ACKs the mention with an 👀 reaction on the triggering message.
- Corveil enqueues a Crow worker run of kind
chat-reply. - When the run completes, Corveil posts a short summary in that thread.
The eyes reaction means the event was accepted — not that a reply was posted. A Crow worker must claim chat-reply runs or the thread stays silent after the eyes land. Check Workforce → Runs for a chat-reply row when debugging a mention that ACKed but never replied (stuck pending, failed run, or no worker online).
Verify
- Run Test connection to confirm the bot token is valid and the scopes are present.
- Once polling, new messages in the listed channels appear as conversation events. If a channel returns
not_in_channel, invite the bot to it. - For chat:
@corveil helloin an invited channel. You should see the eyes reaction, then a threaded reply once a Crow worker finishes thechat-replyrun. A later reply in that thread without@stays silent;@corveilagain starts a new run in the same thread. In a DM, further messages without@keep going. Channel stop is@corveil stop; in a DM, a barestopalso cancels.