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). All four variables are optional at boot; the Slack channel stays disabled until they are set.
| Variable | Required | Purpose |
|---|
DEVOTEL_SLACK_CLIENT_ID | To enable install | Slack App OAuth v2 client id. Used to build the https://slack.com/oauth/v2/authorize URL. |
DEVOTEL_SLACK_CLIENT_SECRET | To enable install | Slack App OAuth v2 client secret. Used (HTTP Basic, with the client id) to redeem the temporary code at https://slack.com/api/oauth.v2.access. |
DEVOTEL_SLACK_SIGNING_SECRET | To accept inbound | HMAC-SHA256 key that verifies inbound Events / slash-command / interactivity requests. Fail-closed (see below). |
DEVOTEL_SLACK_BOT_SCOPES | Optional | Comma-separated OAuth v2 bot scopes requested at install. Defaults to the baseline set baked into packages/shared/src/env.ts; override only if the App adds optional scopes (e.g. assistant:write). |
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:
https://api.orbit.devotel.io/api/v1/integrations/slack/oauth/callback
Default bot scopes
If DEVOTEL_SLACK_BOT_SCOPES is unset, Orbit requests the following baseline scopes:
chat:write, chat:write.public, commands, app_mentions:read,
channels:history, channels:read, groups:history, groups:read,
im:history, im:write, users:read, users:read.email,
reactions:read, links:read, files:write
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
state token and redirects the operator’s browser to Slack’s authorize screen.
GET /api/v1/integrations/slack/oauth/start
-
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.
GET /api/v1/integrations/slack/oauth/callback
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:
POST /api/v1/integrations/webhooks/slack (canonical)
POST /api/v1/webhooks/slack/events (Slack-convention alias)
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:
X-Slack-Signature: v0=<hex>
where v0 = HMAC-SHA256(DEVOTEL_SLACK_SIGNING_SECRET, "v0:" + X-Slack-Request-Timestamp + ":" + rawBody)
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:
DEVOTEL_SLACK_SIGNING_SECRET | Inbound request | Result |
|---|
| Set | Valid signature, fresh timestamp | 200 — processed |
| Set | Bad/missing signature, or timestamp older than 5 min | 401 — rejected |
| Unset | Any | 503 — channel disabled (never accept unsigned events) |
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
| Symptom | Cause | Fix |
|---|
Install returns 503 from /start | DEVOTEL_SLACK_CLIENT_ID / DEVOTEL_SLACK_CLIENT_SECRET unset | Set the App credentials in Secret Manager and redeploy. |
Inbound events return 503 | DEVOTEL_SLACK_SIGNING_SECRET unset | Set the signing secret before enabling the channel. |
Inbound events return 401 | Signing-secret mismatch or stale timestamp (clock skew > 5 min) | Re-copy the signing secret from Basic Information; verify host clock (NTP). |
| Slack disabled the subscription | Repeated non-200s on the Request URL | Fix the secret, then re-enable Event Subscriptions in the Slack App. |