Skip to content

Slack Sensor

What it does

Two paths on one source:

  1. Ingest — polls public channels you list via conversations.history. New messages become conversation events in Corveil.
  2. Chat — a separate Events API path at POST /webhooks/slack/{token}. @corveil mentions enqueue a chat-reply worker 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 the channels allowlist. The poller does not scrape @corveil out of channel history.

Credentials

The source references two vault credentials by id:

CredentialRequiredSecret
slack_bot_tokenyesBot User OAuth Token (xoxb-…)
slack_signing_secretyes for chatSigning 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 (@corveil chat)
  • im:history, im:write (DMs)
  • groups:history (private-channel Events API / ingest; chat still requires @)
  • files:read (download hosted images/files so chat-reply can see them; existing apps must reinstall)
  • files:write (attach generated images to thread replies; existing apps must reinstall)
bash
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

KeyRequiredNotes
channelsyesComma-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

  1. 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).
  2. Under OAuth & Permissions, confirm the bot scopes listed above are present.
  3. Install the app to the workspace and copy the Bot User OAuth Token (xoxb-…) and the Signing Secret from Basic Information.
  4. Invite the bot to each channel you listed (/invite @yourbot) — the bot can only read history for channels it's a member of.
  5. If you updated the manifest to add chat scopes (app_mentions:read, chat:write, reactions:write, im:history, im:write, groups:history, files:read, files:write), 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

json
{
  "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:

  1. Event Subscriptions → enable → Request URL = that webhook_url.
  2. Under Subscribe to bot events, add:
    • app_mention
    • message.channels
    • message.groups
    • message.im
  3. Save. Slack sends a url_verification challenge; Corveil echoes the challenge only when the source has a valid slack_signing_secret wired in — without it the handler returns 401 and Slack will not accept the URL.
  4. App Home → Messages tab → enable Allow users to send Slash commands and messages from the messages tab. Without this, Slack never delivers message.im and 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):

  1. Corveil verifies the Slack request signature (hence the signing secret).
  2. Corveil ACKs the mention with an 👀 reaction on the triggering message.
  3. Corveil enqueues a Crow worker run of kind chat-reply.
  4. 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 in queued when debugging a mention that ACKed but never replied (stuck pending, failed run, or no worker online). The worker sees the Slack thread, including hosted image and file attachments (downloaded after the ACK). Limits: 10 files per thread, images 5 MB (png/jpeg/gif/webp), documents 10 MB (text, JSON, PDF, DOCX). Secret-looking filenames (.env, .pem, id_rsa, …) are skipped. When the worker generates an image, Corveil attaches it to the thread reply (5 files/run, 5 MB, png/jpeg/gif/webp). Existing Slack apps must reinstall so the bot token includes files:read and files:write.

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 hello in an invited channel. You should see the eyes reaction, then a threaded reply once a Crow worker finishes the chat-reply run. A later reply in that thread without @ stays silent; @corveil again 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 bare stop also cancels.