Slack
Configure a deployment-owned Slack app for OAuth, Events API, and intent-based thread capture.
Overview
Self-hosted Slack uses the same connector flow as hosted ctx|, but your deployment owns one Slack app. Organisations authorise that app into their workspace; they do not create their own Slack apps or webhook URLs.
Capture is intent-based: mentioning the bot starts a small in-thread agent.
Bare mention or “capture this” snapshots that thread as Markdown under slack/
in a GitHub context repository (typically ctxpipe-context), then ingest runs
like any other repo. Other mention text gets a capability reply — v1 does not
answer questions in Slack. There is no continuous channel mirror. Uninstalling
the Slack app does not purge git.
For general workspace search and read from developer agents, point operators at Slack’s own MCP server — that is separate from this connector. See the product Slack connector guide.
What you need
- A public HTTPS
AUTH_BASE_URLreachable by Slack. - A Slack app with OAuth and Event Subscriptions enabled.
- A GitHub connector and a repository that will receive captured threads.
- Backend env:
SLACK_CLIENT_ID,SLACK_CLIENT_SECRET,SLACK_SIGNING_SECRET. - Model env (
MODEL_PROVIDER*/ Bedrock) so the mention agent can interpret extra text after@mention. A bare mention still captures without the model. - The same stable
AUTH_SECRETand, when set,CONNECTION_SECRETS_ENCRYPTION_KEYon backend and worker so the worker can decrypt stored bot tokens.
1. Create the Slack app
In the Slack API dashboard, create an app (from scratch is fine). For higher rate limits in production, prefer distributing it as a Marketplace or internal org app rather than a throwaway development app.
If organisations will install the app into workspaces other than the app owner's development workspace, activate Manage Distribution in Slack. Marketplace listing is separate and is not required for private multi-workspace distribution.
OAuth & Permissions
Add bot token scopes at least:
app_mentions:read(receive capture requests from bot mentions)channels:history,channels:readgroups:history,groups:read(private channels; bot must be invited)chat:write(in-thread capturing → captured status replies)files:readusers:read
When adding a scope after a workspace has already installed the app, that workspace must complete OAuth again. Updating Slack app settings does not upgrade an existing bot token.
Register this OAuth redirect URL:
{AUTH_BASE_URL}/api/v1/connectors/slack/oauth/callbackSet SLACK_CLIENT_ID and SLACK_CLIENT_SECRET on the backend from the
app credentials. Set SLACK_REDIRECT_URI only when the public callback
differs from the URL derived from AUTH_BASE_URL.
Event Subscriptions
Enable Event Subscriptions and set the Request URL to:
{AUTH_BASE_URL}/api/v1/webhook/slackSlack verifies the URL with a signed url_verification challenge. ctx| checks
X-Slack-Signature using SLACK_SIGNING_SECRET.
Subscribe the bot to:
app_mention
Do not subscribe to message.channels / message.groups for the connector
ingest path. Continuous message fan-out is not used.
Turn Socket Mode off. If Socket Mode is enabled, Slack will not POST to the Request URL (“You won’t need a Request URL”) and mentions never reach ctx|.
The Events URL is deployment-wide. Do not ask each organisation to register its own webhook.
2. Wire deployment secrets
Set all three Slack values together. A partial configuration leaves OAuth or event verification unusable.
Docker Compose
Add the values to the root .env used by the deploy profile, then recreate
backend and worker:
SLACK_CLIENT_ID=...
SLACK_CLIENT_SECRET=...
SLACK_SIGNING_SECRET=...docker compose --profile deploy up -d --force-recreate backend workerRailway Terraform
Set slack_client_id, slack_client_secret, and slack_signing_secret in the
Terraform input or CI secrets used for the environment. Apply Terraform, then
confirm both backend and worker are running the intended environment.
AWS CDK
Pass Slack credentials through connectorSecrets:
new CtxPipe(stack, "CtxPipe", {
// ...required props
connectorSecrets: {
slackClientId: cdk.SecretValue.secretsManager("slack-client-id"),
slackClientSecret: cdk.SecretValue.secretsManager("slack-client-secret"),
slackSigningSecret: cdk.SecretValue.secretsManager("slack-signing-secret"),
},
})Use your normal secret-management convention; do not put plaintext credentials in source control.
3. Complete connector setup in the UI
After the deployment Slack app is configured, each organisation can:
- Authorise the Slack workspace (popup OAuth).
- Bind a sync-target (context) repository.
- Invite the bot into channels where capture is needed (
/invitethe bot's @handle, which may differ from its display name). - Mention that handle in an existing thread. Say capture this or mention with no extra text to snapshot it. A mention at the top of the channel is refused. Later mentions on the same thread update the same file. Other intents receive a short capability reply.
Direct messages are not supported in v1. Captures are auditable snapshots committed to the bound default branch. Uninstall does not delete git.
One Slack workspace can be connected to one ctx| organization. Reinstalling or rotating the Slack app drops channel membership. Invite the bot again in every channel where capture should work. Hosted and self-host deployments share the Slack app’s rate quota across organisations.
4. Layout
Threads are written under:
slack/channels/<slug>--<channelId>/threads/<yyyy>/<mm>/<threadTs>/index.mdAttachments are recorded as Markdown links to the Slack file permalink. ctx| does not download image or file binaries into git.
Operator troubleshooting
“Slack is not configured on this deployment”
The backend does not have both OAuth values. Confirm SLACK_CLIENT_ID and
SLACK_CLIENT_SECRET are non-empty in the backend runtime, then redeploy or
restart backend. Setting repository or CI secrets alone does not update an
already-running service.
OAuth returns invalid_redirect_uri
Register the exact callback:
{AUTH_BASE_URL}/api/v1/connectors/slack/oauth/callbackOnly set SLACK_REDIRECT_URI when intentionally using a different public
callback. Its value must exactly match a redirect URL registered in Slack.
Slack cannot verify the Events Request URL
- Confirm the public route is
{AUTH_BASE_URL}/api/v1/webhook/slack. - Confirm
SLACK_SIGNING_SECRETis Slack's Signing Secret, not the legacy verification token. - Confirm the public proxy forwards the raw request body and
X-Slack-Signature/X-Slack-Request-Timestampheaders unchanged. - An unsigned test POST should return 401. A 404 means routing is wrong; a 503 means the backend has no signing secret.
Mentions do not create commits
- Confirm Socket Mode is off and the bot Event Subscription includes
app_mention. - Confirm the connector is live (repository bound) for that workspace.
- Confirm the bot is a member of the channel. After an app reinstall, invite it again.
- Mention the bot's @handle (Slack autocomplete), not only a display name typed as plain text.
- Inspect the OpenWorkflow worker logs for
slack-mention-agent. Backend and worker must use the same database and stable encryption material (AUTH_SECRETand, when set,CONNECTION_SECRETS_ENCRYPTION_KEY). The selected repository's GitHub connection must still have contents write access. Extra text after a mention requiresMODEL_PROVIDER*(or Bedrock). A second org cannot OAuth-install a workspace that is already connected.
Capture commits but the repository never indexes
Capture writes git via the GitHub App. Indexing starts only when this
deployment receives the GitHub push webhook for that repository. Confirm
the GitHub App webhook URL points at this origin
({AUTH_BASE_URL}/api/v1/webhook/github/...), not another environment, and
that the worker and codesearch services are running.