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 @)
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), 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 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 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.