Slack Onboarding: Connect a Workspace End to End
This guide walks you from a new Slack App at api.slack.com to a working Slack channel in Orbit: OAuth v2 install, signed inbound delivery, webhook routing, and a pinned notifications channel. It complements the Slack channel page, which covers the operator-level configuration and the full error catalog — this guide is the ordered path you follow the first time, and re-run whenever you install or re-verify the App.This is the tenant-facing Slack App — the one your workspace installs to receive Events API, slash commands, and interactivity payloads. It is not the internal ops-alert webhook that posts Orbit’s own operational alerts to a Devotel-internal channel; that path is not part of your setup.
1. What the Slack App does
There are three inbound surfaces and one outbound surface, all tied to the same Slack App:- Events API — Slack POSTs workspace events (
app_mention,message,message.channels,message.im,app_home_opened,team_join,reaction_added,link_shared) to Orbit’s inbound endpoint. Every event is persisted and re-ACK’d safely on retry (duplicate: true). - Slash commands — the
/orbit ...command returns an immediate ephemeral response and fans out the payload as a tenant webhook. - Interactivity — Block Kit buttons, shortcuts, and
view_submissionpayloads follow the same signed, persisted, fanned-out path. - Outbound notifications — Orbit posts tenant-side events (new conversation, campaign completed, SLA breach, voice-queue SLA warning, and similar notifications) to the channel you pin (see section 6).
2. Create the Slack App at api.slack.com
- Sign your workspace in to api.slack.com/apps and create an app with Create an app → From scratch.
- On the app’s Basic Information page, copy the Client ID and Client Secret. You need these on the Orbit operator; see the Slack channel page — operator setup.
-
Go to OAuth & Permissions → Redirect URLs and add:
-
On the same page, request the bot scopes your Slack App will ask for at install. The baseline set Orbit requests by default is:
If your operator has overridden the default scope list for your deployment, use that exact list; otherwise the baseline above covers install, inbound events, slash commands, interactivity, and posting.
3. Run the OAuth v2 install
The install is browser-driven and fully round-trips before anything is stored:- From the Orbit dashboard, open Channels → Slack and click Connect Workspace.
- Orbit mints an HMAC-signed
statetoken (10-minute TTL, nonce-based, one use) and redirects your browser to Slack’s approve screen. - Approve the scopes Slack asks for. Slack redirects back to the callback URL above.
- Orbit verifies the signed state, resolves your tenant from it, redeems the temporary code, and stores the workspace credentials in your tenant schema.
state is tampered or older than 10 minutes, the callback returns 401 INVALID_STATE — restart the install from the dashboard. A 503 SLACK_NOT_CONFIGURED means the operator credentials are not set on the deployment; see the Slack channel page — operator setup and retry.
The callback is intentionally public (no session cookie). Do not close the browser redirect flow early or re-run
/callback by hand — trust is anchored to the signed state round-trip, and re-playing the callback a second time returns the same 401 INVALID_STATE.4. Verify the signing secret
Orbit verifies every inbound Events API, slash-command, and interactivity request using the App’s signing secret. Point your Slack App’s Event Subscriptions, Slash Commands, and Interactivity Request URLs at either of the equivalent inbound endpoints:X-Slack-Signature: v0=<HMAC-SHA256(secret, "v0:" + timestamp + ":" + rawBody)> with a 5-minute timestamp window. The behavior is fail-closed — anything that cannot be authenticated is rejected, and an unchecked signing secret disables the channel entirely rather than accept unsigned events:
Avoid the 503 branch by confirming the operator credentials are set before enabling the channel — a deployment that leaves the signing secret unset will dispatch
503 to Slack and eventually have the subscription disabled by Slack after repeated non-200s.
Test the channel with a real Slack delivery: slash commands and Events API retries are the fastest to eyeball. A forged signature or a replayed timestamp returns 401 INVALID_SIGNATURE, which is the correct outcome — do not “fix” this in the receiver.
5. Route inbound events to webhooks
Events fan out as three webhook event types — subscribe the ones your integration needs and keep one handler for all three:
Two routing notes:
- Events dedupe — every Events API delivery is keyed on Slack’s
event_id. A retry within the dedupe window returns200withduplicate: true, so your webhook handler should treatslack.event_receivedas at-most-once perevent_id. - Slash commands ack immediately — the ephemeral reply to the user is returned synchronously; the
slack.slash_commandwebhook fan-out happens asynchronously, so a slow subscriber never holds the Slack ack open.
6. Pin the post channel
Outbound notifications (new conversations, campaign completion, SLA breaches, voice-queue SLA warnings, and similar) post to the channel you pin. Until you pin one, the publishers resolve tono_channel and drop the notification silently — the fail-open behavior is deliberate so a Slack misconfiguration never blocks the action behind it.
Pick a channel from the workspace and pin it:
chat:write.public (in the default scope set) to post to a public channel without a /invite. Otherwise Slack returns channel_not_found and the post fails. A pinned id that is not a public or private channel (a DM, or a name instead of an id) returns 400 INVALID_SLACK_CHANNEL_ID.
7. Troubleshooting
Match the symptom to its cause before touching the App:
For anything not covered above — scopes, per-event-type handling, or the full operator variable set — see the Slack channel page.