Slack
Carbon has two Slack integration modes:
- Legacy webhook URL (round 1) — paste an Incoming Webhook URL into Settings → Integrations. One channel per URL, no OAuth. Still works.
- Real Slack app (round 13) — install Carbon into a Slack workspace via OAuth, then subscribe any channel to any subset of Carbon events. This page covers the real app.
One-time server setup
Section titled “One-time server setup”-
Go to api.slack.com/apps → Create New App → From scratch. Name it “Carbon” (or whatever you like).
-
OAuth & Permissions → add these bot token scopes:
channels:readchat:writeincoming-webhook
-
OAuth & Permissions → Redirect URLs → add
https://api.your-carbon-host/v1/slack/oauth-callback(use your API’s public origin;http://localhost:4000/v1/slack/oauth-callbackfor local dev). -
Basic Information → copy the Client ID and Client Secret.
-
Set the following env vars on the Carbon API and workers processes:
Terminal window SLACK_CLIENT_ID=...SLACK_CLIENT_SECRET=...# 32+ char random string — encrypts stored bot tokens at rest.SLACK_TOKEN_ENC_KEY=$(openssl rand -hex 32)# Only needed if the callback URL differs from the default.SLACK_REDIRECT_URI=https://api.your-carbon-host/v1/slack/oauth-callbackDASHBOARD_URL=https://app.your-carbon-host -
Run the
0011_slack_integrationsmigration:Terminal window pnpm --filter @carbon/database migrate
Install into a workspace
Section titled “Install into a workspace”- In the Carbon dashboard, go to Settings → Integrations → Slack.
- Click Connect Slack. You’ll be sent to Slack’s approval screen and
bounced back to
/settings?slack=installed. - The workspace now appears under Slack installations.
Subscribe a channel to events
Section titled “Subscribe a channel to events”For each installation you can add per-channel subscriptions. Each
subscription selects the event actions that should be forwarded — e.g.
snapshot.overwritten, drift.detected, emulator.crashed.
curl -X POST https://api.your-carbon-host/v1/slack/subscriptions \ -H "x-carbon-key: ck_live_admin_…" \ -H "content-type: application/json" \ -d '{ "installationId": "slkinst_...", "channelId": "C0123456789", "channelName": "eng-alerts", "events": ["snapshot.overwritten", "drift.detected", "emulator.crashed"] }'Deliveries are handled by the startSlackNotifier worker in
apps/workers/src/slack-notifier.ts. It polls the events table every 10s
and sends a Block Kit message via chat.postMessage for every matching
subscription.
Uninstall
Section titled “Uninstall”curl -X DELETE https://api.your-carbon-host/v1/slack/installations/slkinst_... \ -H "x-carbon-key: ck_live_admin_…"The server calls Slack’s auth.revoke (best-effort) and hard-deletes the
installation + all its channel subscriptions.
Routes
Section titled “Routes”| Method | Path | Purpose |
|---|---|---|
| GET | /v1/slack/install | 302 → Slack OAuth authorize (public) |
| GET | /v1/slack/oauth-callback | OAuth callback → dashboard (public) |
| GET | /v1/slack/installations | List installations for the caller’s org |
| GET | /v1/slack/subscriptions | List channel subscriptions |
| POST | /v1/slack/subscriptions | Create a channel subscription |
| DELETE | /v1/slack/subscriptions/:id | Remove a channel subscription |
| DELETE | /v1/slack/installations/:id | Uninstall the workspace integration |
All management routes require an admin-scoped API key or a session user with the owner/admin role on the caller’s org.
Security notes
Section titled “Security notes”- Bot tokens are encrypted at rest with AES-256-GCM. Losing
SLACK_TOKEN_ENC_KEYrenders every stored token undecryptable — rotate by re-installing each workspace, not by editing rows. - The OAuth state parameter carries the org id. A production deployment should additionally check a signed cookie against the state before trusting the redirect — the current implementation trusts the state’s org fragment (fine for admin-authenticated dashboards, not for open public installers).
chat.postMessagerequires the bot to be in the target channel. Invite@Carbonto the channel before subscribing it, or the send returnsnot_in_channeland the notifier logs a warning.