Skip to main content

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

If DEVOTEL_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:
  1. A channel explicitly named on the individual notification, when one is supplied.
  2. The channel chosen when the workspace was connected.
There is no environment-variable or 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)

  1. From the dashboard, navigate to Channels → Slack and click Connect Workspace. This calls the authenticated start route, which mints an HMAC-signed state token and redirects the operator’s browser to Slack’s authorize screen.
  2. The operator approves the requested bot scopes in Slack.
  3. 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:
Both mounts share one handler — the same signature gate, tenant resolution, and dispatch. Orbit verifies every inbound request per Slack’s verifying-requests-from-slack spec:
The request timestamp (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.

Common errors