Slack
The Slack channel lets a tenant install Orbit into their own Slack workspace as a Slack App. Operators connect the workspace through the OAuth v2 authorize → callback flow; inbound Events API messages, slash commands, and interactivity payloads are POSTed back to Orbit and verified with the App’s signing secret.This is the tenant-facing Slack App — distinct from the internal ops-alert webhook (
DEVOTEL_SLACK_OPS_WEBHOOK_URL), which only posts Orbit’s own operational alerts to a Devotel-internal channel.Operator setup
Configure the Slack App credentials once per deployment, in Secret Manager (or.env for local dev). Every variable below is optional at boot; the Slack channel stays disabled until the credentials are set.
In your Slack App configuration (api.slack.com/apps), find the client id + secret under Basic Information → App Credentials, and the signing secret on the same screen. Set the Slack App’s Redirect URL to:
Default bot scopes
IfDEVOTEL_SLACK_BOT_SCOPES is unset, Orbit requests the following baseline scopes:
Default post channel
When the connected Slack App posts a tenant-side event (new conversation, campaign completed, SLA breach, voice-queue SLA warning) it resolves the destination channel in this order:- A channel explicitly named on the individual notification, when one is supplied.
- The channel chosen when the workspace was connected.
general fallback on this path. If no channel
is configured — or the workspace has no active connection — the App drops the
notification silently: it logs at debug level and never throws or fails the action
behind it. This is deliberate: notification delivery is fail-open so that a Slack
misconfiguration can never block the campaign send, conversation create, or SLA
escalation that triggered it. Choose a notifications channel when connecting the
workspace so these events have a place to land.
The bot must be a member of the target channel, or hold the chat:write.public scope
(included in the default scope set above) to post to a public channel it has not
joined; otherwise Slack returns channel_not_found and the post fails.
DEVOTEL_SLACK_DEFAULT_CHANNEL and the general fallback belong to a separate,
legacy internal Slack-forwarding path, not to this OAuth app. They have no effect on
tenant notification delivery through the connected Slack App.Install flow (OAuth v2)
-
From the dashboard, navigate to Channels → Slack and click Connect Workspace. This calls the authenticated start route, which mints an HMAC-signed
statetoken and redirects the operator’s browser to Slack’s authorize screen. - The operator approves the requested bot scopes in Slack.
-
Slack redirects the browser (a top-level navigation) back to the public callback route, which verifies the signed
state, resolves the tenant from the verified payload, redeems the temporary code, and stores the workspace credentials in the tenant schema.
The callback is intentionally registered before Clerk auth (public scope) because browsers do not reliably carry the session cookie across Slack’s cross-site redirect. Trust is anchored on the HMAC-signed
state round-trip, not on a session cookie.Inbound request verification
Point your Slack App’s Event Subscriptions, Slash Commands, and Interactivity Request URLs at either of the equivalent inbound endpoints:X-Slack-Request-Timestamp) must be within 5 minutes of now (Slack’s documented replay-protection window).
Fail-closed behaviour
Per platform invariant #29 the receiver fails closed:
So an operator must set the signing secret before enabling the Slack channel; otherwise inbound deliveries return
503 and Slack will disable the subscription after repeated non-200s.