answerLoopsanswerLoops Docs
Integrations

Slack

Connect a Slack workspace to answerLoops via 1-click OAuth, an optional Events API webhook, or polling mode with no admin approval required.

Overview

answerLoops connects to Slack in one of two ways, and the two are independent of each other:

  • OAuth install — an admin authorizes answerLoops from the workspace's Add to Slack screen. answerLoops receives a bot token (xoxb-…) and a channel picker loads automatically. Message delivery into answerLoops then happens either via the Events API webhook (real-time) or, on self-hosted deployments only, by falling back to polling.
  • Polling mode (manual setup) — a bot token, team ID, and channel IDs are pasted in by hand, with no OAuth authorization and no public webhook at all. This is the path for someone who can't get a Slack admin to click "Authorize," or who can't expose a public URL for Slack to call.

Every connected channel appears in the unified ticket list with a Slack source badge. Slack is a Standard-plan-and-above feature — saveSlackIntegrationAction, saveSlackChannelsAction, and the /api/slack/install route all gate on the slack_integration entitlement and return an error before touching the workspace if the org's plan doesn't include it.

Automatic Deflections is off by default for every newly connected platform. While it's off, even a high-confidence AI answer is held as a draft on the dashboard awaiting Approve/Edit/Dismiss — Slack gets a brief acknowledgment reaction/reply instead of the real answer. Turn it on per-workspace in Integrations → Slack → Edit channels → Automatic Deflections toggle → Save channels once you've reviewed enough approved drafts to trust the AI's answers going out unsupervised.


  1. In Integrations → Slack (or the onboarding wizard), click Add to Slack
  2. answerLoops requests the channels:history, channels:read, channels:join, chat:write, reactions:write, and users:read scopes and redirects to Slack's authorization screen
  3. An admin picks the workspace and approves
  4. Slack redirects back to /api/slack/callback, which exchanges the code for a bot token and stores it (along with the team ID and an internally-generated bot_secret used to authenticate the bot's own traffic back to answerLoops)
  5. The channel picker opens automatically, listing every public channel in the workspace via conversations.list
  6. Select the channels to monitor and click Save channels

No token to copy, no manifest to upload. The state parameter round-tripped through the OAuth redirect expires after 10 minutes — if you leave the authorization screen open too long before approving, you'll be sent back with an invalid_state error and need to click Add to Slack again.

Saving channels joins the bot automatically

Slack never adds a bot to a channel just because a scope was granted — the bot has to explicitly call conversations.join. Both saveSlackChannelsAction (editing channels post-connect) and saveSlackIntegrationAction (manual setup) call this for every selected channel as part of the save, so picking a channel and clicking Save is enough to make it work immediately — there's no separate "invite the bot" step for public channels.

Private channels are the one exception. Slack doesn't let any bot self-join a private channel — this is a platform limit, not something answerLoops works around. If a save includes a private channel, the join call fails with not_in_channel-adjacent errors and the UI shows a warning like:

Joined 2/3 channels automatically. Private channels need a manual invite in Slack (Channel → Integrations → Add apps): C09876ZYXWV

You still need to /invite @answerLoops (or add the app via that channel's Integrations → Add apps) for any private channel yourself.


Events API webhook (real-time delivery)

The Events API webhook is what gives OAuth-connected workspaces instant delivery. It's automatic on managed cloud (the platform has one shared webhook URL already configured) and requires one extra step for self-hosted deployments:

  1. In your Slack app's Event Subscriptions, set the Request URL to {YOUR_DOMAIN}/api/slack/events — the self-hosted Integrations page surfaces this exact URL once Slack is connected
  2. Subscribe to the message.channels and reaction_added bot events
  3. Slack sends a url_verification challenge on save; /api/slack/events answers it before any signature check runs
  4. Every subsequent event is verified against SLACK_SIGNING_SECRET using the x-slack-request-timestamp and x-slack-signature headers — a request that fails verification gets a 403, and an event for a team answerLoops has no integration record for is rejected the same way

Once wired up, new messages create tickets in real time, and 👍/👎 or 1️⃣–5️⃣ reactions on a bot reply are read as feedback votes or CSAT ratings respectively.


Option 2: Polling mode (no admin, no webhook)

Self-hosted only — every managed-cloud org already gets real-time delivery through the shared Events API webhook, so cloud never starts the polling loop at all. Polling exists for operators who can't get Slack admin approval for an OAuth install, or who can't expose a public webhook URL to Slack for security-review or firewall reasons.

  1. Go to Integrations → Slack and click Set up manually instead (only shown on self-hosted deployments, next to Add to Slack)
  2. Paste a bot token (xoxb-…), the workspace's Team ID (T…), and one or more channel IDs
  3. A signing secret is optional here — only required if you also plan to wire up the Events API webhook alongside polling
  4. answerLoops calls conversations.join on every listed channel the same way OAuth does, then starts polling conversations.history on that channel every SLACK_POLL_INTERVAL_SECONDS (default 60, minimum recommended 30)

A channel's first poll never backfills history. The poller seeds its cursor to the newest message already in the channel and tickets nothing on that pass — exactly like Discord's gateway, which never sees anything posted before the bot joined. Only messages posted after the first poll become tickets. This is deliberate: without it, connecting Slack to an active community channel would flood the ticket list with the channel's entire recent history on day one.

Why some deployments choose polling over OAuth:

  • No inbound HTTP from Slack to your servers — the bot only ever calls out to slack.com
  • No public webhook URL required, so nothing new to expose past a firewall
  • Narrower security review scope: one outbound API call per poll interval, versus an inbound endpoint that has to verify every request's signature
  • A read-only-scoped token is enough — no OAuth app, no admin authorization flow
SLACK_POLL_INTERVAL_SECONDS=60  # default. Minimum recommended: 30.

Polling and the Events API webhook aren't mutually exclusive on self-hosted deployments — a workspace connected via manual token entry can still have a signing secret set and receive webhook events if you wire up Event Subscriptions for it. In practice most operators pick one or the other.


Feature comparison

OAuth + Events API webhookOAuth (self-hosted, polling)Manual polling
Admin authorization neededYesYesNo
Public webhook URL requiredYesNoNo
Message latencyInstantPoll interval (default 60s)Poll interval (default 60s)
Channel pickerAutomaticAutomaticManual (paste IDs)
Signing secret neededYes, to verify inbound eventsNoOptional
Auto-joins selected channelsYes (public only)Yes (public only)Yes (public only)
Available on managed cloudYes (default)NoNo

Escalation, confidence, and deflection settings

The channel picker's save form (Integrations → Slack → Edit channels) also sets three per-workspace fields stored on the integration record:

  • Escalation User Group ID (optional) — a Slack user group or user ID (S… or U…) pinged when the AI's confidence is below threshold
  • Confidence threshold (0–1, default 0.8) — the AI answer confidence required before answerLoops treats a question as answerable at all
  • Automatic Deflections — off by default; see the callout above

These map directly to the escalation_role_id, confidence_threshold, and auto_deflect_enabled fields on the integration record, alongside bot_token, team_id, and channel_ids.


Environment variables reference

VariableRequiredDescription
SLACK_CLIENT_IDFor OAuth installOAuth app Client ID — enables the Add to Slack 1-click flow
SLACK_CLIENT_SECRETFor OAuth installOAuth app Client Secret, paired with SLACK_CLIENT_ID
SLACK_SIGNING_SECRETFor Events API webhookVerifies inbound webhook payloads at /api/slack/events; platform-wide, not per-workspace
SLACK_POLL_INTERVAL_SECONDSNoPolling interval in seconds. Default 60, minimum recommended 30
AUTH_URLYes (self-host)Public app URL — Slack redirects to {AUTH_URL}/api/slack/callback after authorization
BOT_TARGET_URLFor pollingInternal URL the poller uses to forward ingested messages to the app's /api/ingest — no trailing slash

Add {AUTH_URL}/api/slack/callback to your Slack app's OAuth & Permissions → Redirect URLs before anyone connects via OAuth.


Troubleshooting

On this page