Slack Connector
Continuously ingest Slack messages into CortexDB as conversation experiences.
The Slack connector tails messages from configured channels and writes them to CortexDB as conversation experiences. Threads and file shares are preserved.
Two ways to run this connector
Self-hosted — pip install 'cortexdb-connectors[slack]' then cortexdb-sync sync slack. Reads
your token from ~/.cortexdb/state.json (written by cortexdb init) or from env. Managed —
CortexDB runs the connector as a worker; configure it once from your dashboard. See the
Connectors overview.
1. Prepare credentials in Slack
api.slack.com/apps→ create a new app from scratch.- Add Bot Token Scopes:
channels:history— read public channelschannels:read— view channel infogroups:history— read private channels (optional)users:read— resolve author namesfiles:read— download shared files (required for file ingestion)
- Install the app to your workspace and copy the Bot User OAuth Token (
xoxb-...).
files:read is required for file shares
Without the files:read bot scope, Slack answers file downloads with a 200 HTML login page instead
of the file, and shared files are silently dropped. Add it if you rely on file ingestion.
2. Configure (managed dashboard)
Settings → Connectors → Add Connector → Slack. Paste the bot token, choose channels (default: all
public), set the scope template (default org:<org>/source:slack/channel:{channel}; placeholders
{channel}, {author}, {workspace}, {thread_ts}), pick Real-time (Events API webhook) or Polling
(default 10 s), and Start sync. Backfill preserves original message timestamps as observed_at for
bi-temporal correctness.
What gets written
Each Slack message becomes a POST /v1/experience:
{
"scope": "org:acme/source:slack/channel:engineering",
"modality": "conversation",
"observed_actor": "user:[email protected]",
"content": {
"kind": "message",
"role": "user",
"text": "We should migrate the payments service to CockroachDB."
},
"context": {
"observed_at": "2026-03-15T10:30:00.123Z",
"labels": ["channel:engineering", "workspace:acme-corp", "thread=C123:1710502200.000100"],
"intent": "discussion"
},
"idempotency_key": "slack:C123:1710502200.000100:a1b2c3"
}| Slack event | Maps to |
|---|---|
| Channel message | experience with content.kind=message |
| Thread reply | Same, plus a thread=<channel>:<thread_ts> label (see below) |
| File share | experience with content.kind=blob_ref (file uploaded via POST /v1/blobs first; needs files:read) |
| Bot messages | Skipped by default; toggle "Include bots" to enable |
Corrected fields (vs older docs)
idempotency_keyisslack:{channel}:{ts}:{content-hash}for messages (the trailing content-version hash lets an edit land as a new version instead of colliding — there is no team-id segment). Files useslack:file:{channel}:{file_id}:{version}.- Thread replies attach a
thread=<channel>:<thread_ts>label, notcontext.preceded_by(the connector doesn't have the parent'sevt_…id at emit time). - Reactions are not ingested — emoji reactions never become content.
Run it yourself
pip install 'cortexdb-connectors[slack]'
# One-time: get a CortexDB token + actor
pip install cortexdb-cli
cortexdb init
# Supply the connector's credentials
export SLACK_BOT_TOKEN=xoxb-... # required
# Sync once, or watch on a poll loop (point at your instance if self-hosted)
cortexdb-sync --api-url http://localhost:3141 sync slack
cortexdb-sync --api-url http://localhost:3141 watch slack --interval 60Cursor state persists in ~/.cortexdb/sync_state.json. For real-time ingestion, run
cortexdb-sync serve with SLACK_SIGNING_SECRET set.
See also
- Connectors overview — the shared
cortexdb-syncmechanics - POST /v1/experience · Scopes · Lifecycle