The built-in app, the standalone bot, and the cloud connector are three separate Slack apps (different manifest, different scopes, different transport) — do not point one at another’s server.
Built-in Slack App
The Slack app that ships inside thecognee package: slash commands and a message shortcut, served by your own self-hosted backend, with memory scoped to each linked member. Once a workspace is connected, anyone in it can search memory with /cognee-ask, save new facts with /cognee-remember, capture existing messages with the Remember this shortcut, and review every answer privately before sharing it with the channel.
Requires cognee >= 1.5.0. On older releases /cognee-remember falls through to “Command /cognee-remember is not yet supported.” while /cognee-ask and Remember this work.
Why Use This Integration
- Ask from anywhere in Slack:
/cognee-ask <question>searches your memory and replies privately, with a Share button to post the answer to the channel once you’ve checked it, or Discard to drop it. - Save what Slack never saw:
/cognee-remember <text>stores free text nobody typed into Slack yet — a decision from a call, a conclusion recorded after the fact. - Save what Slack already has: the Remember this message shortcut (in a message’s ⋯ menu) stores any Slack message into Cognee memory, tagged with who said it and where.
- Per-user, not per-workspace: each member runs
/cognee-linkonce so their asks and saves use their own Cognee account — without it, Cognee refuses to act for them rather than falling back to anyone else’s memory. - Self-hosted, no middleman: your Slack app talks directly to your Cognee backend. No data passes through a third-party relay.
One Slack app is installed once per workspace, but memory is per-person — that’s what
/cognee-link exists to resolve. A member who skips it is refused, not silently attributed: the only unlinked member Cognee will act for is the one who completed the workspace’s OAuth Connect (their own account is used), and everyone else gets “I don’t know which Cognee account you are yet. Run /cognee-link to connect yours, then try again.” — for /cognee-ask, /cognee-remember, and Remember this alike. Nothing is written into someone else’s memory as a fallback. Workspaces connected before the installer’s Slack id was recorded have no installer exception at all, so every member there must link.Prerequisites
- Cognee
>= 1.5.0or adevcheckout — see the version note above. - A running Cognee backend (and frontend, for the
/cognee-linkconfirmation page) reachable over HTTPS from the public internet — Slack will not calllocalhost. For local development, use a tunnel like ngrok (below); for production, this is your deployment’s normal public domain. - A Cognee account for each Slack member who wants to link their own memory.
Local Development: ngrok Tunnel
Slack requires a public HTTPS URL for every request URL. ngrok tunnels a public HTTPS host to your local port 8000 — that host is what every<public-host> placeholder below refers to. Skip this section if your backend already has a public domain.
- Install ngrok:
brew install ngrokon macOS, or download the binary from ngrok.com/download. - Authenticate once: sign up for a free account, copy your authtoken from the dashboard, and run
ngrok config add-authtoken <token>. - Start the tunnel:
Forwarding https://<random>.ngrok-free.app -> http://localhost:8000. That https://<random>.ngrok-free.app host goes into the manifest and .env below. Leave the process running the whole time you’re testing — closing it kills the tunnel and every request URL stops resolving.
Free-tier ngrok shows a “Visit Site” interstitial page to browsers opening the tunnel URL directly. It does not apply to Slack’s server-to-server requests (commands, events, interactivity) — those go straight through, so it never blocks the integration itself.
Setup
1. Create the Slack App from the Manifest
- Go to api.slack.com/apps → Create New App → From an app manifest.
- Paste the manifest below, replacing every
<public-host>with your backend’s public host. - On the app’s Basic Information page, copy Client ID, Client Secret, and Signing Secret — you’ll need them for
.envin step 2. - Install the app to your workspace when prompted.
Deliberately no
channels:history — bulk channel ingestion isn’t part of this integration (see the dlt integration for pulling Slack history via dlt’s slack_source instead). Slack’s non-Marketplace rate limits on history reads make it impractical here anyway.2. Configure Cognee
Copy the app’s credentials into your backend’s.env:
Then bring the stack up from source — it runs exactly the checkout you have, with no image layer in between to go stale:
uv sync matters more than it looks: branches move pyproject.toml/uv.lock, and a venv left over from another branch fails in ways that read as application bugs rather than a dependency mismatch. Restart the backend after any .env change.3. Install and Connect
- Installing the app from the manifest (step 1) prompts the workspace install once.
- In the Cognee frontend, go to Integrations → Connect under Slack. This runs the OAuth exchange and stores the workspace-level bot credential.
- In Slack, run
/cognee-linkand open the link it replies with in a browser where you’re already logged in to Cognee, then click Confirm. The link is a short-lived signed code (10-minute expiry), not an API key — nothing secret is ever typed into Slack. This is what points/cognee-askand the save actions at your memory — and for anyone who didn’t run Connect, what makes them work at all (see the per-person note above). - Try
/cognee-ask What does Cognee do?— you should get a private reply with Share / Discard buttons.
Using It
Ingest something before you ask.
/cognee-ask only searches — it never writes, so it cannot create the slack dataset. On a completely empty memory it fails with CollectionNotFoundError: Collection 'DocumentChunk_text' not found, surfaced as “Search failed. Please try again.” — which reads like a broken integration but just means there is nothing to search. Run /cognee-remember (or ingest via the CLI) first.app_home_opened, app_uninstalled, and tokens_revoked only — there is no app_mention handler. Use the slash commands. If you want @cognee <question> answered from channel memory, that is what the standalone channel-memory bot does.
No /invite @cognee is needed for any of the above either. Every reply — including Share, which posts to the channel — is delivered by Slack on the app’s behalf regardless of channel membership.
Restricting Which Channels Can Run Commands
An opt-in, per-workspace channel allowlist is available from the Integrations page, backed by two authenticated endpoints:GET /api/v1/slack/channels lists the workspace’s public channels and flags which are currently allowed, PUT /api/v1/slack/channels sets the list. An empty list (the default) means unrestricted — a workspace that never touches this setting keeps working everywhere.
This requires the channels:read scope (already in the manifest above). If a workspace connected before that scope existed, the channel list call fails — the fix is to disconnect and reconnect the workspace, not to re-save the allowlist.
Once a non-empty allowlist exists, every command in every other channel returns “Cognee isn’t enabled in this channel.” — worth remembering when testing from a scratch channel.
Removing the App from a Channel vs. Uninstalling
- Removing/kicking the bot from a channel only stops it from posting there — it does not revoke anything; the workspace connection and everyone’s
/cognee-links stay intact. - Uninstalling the app (or a token revoke) fires
app_uninstalled/tokens_revoked, which revokes the stored workspace credential server-side — commands then fail with “not connected” until reconnected from Integrations.
Multiple Environments
Slack apps are tied to one set of OAuth credentials and one redirect URL each. If you run more than one Cognee deployment (local dev, staging, production), create a separate Slack app per environment rather than editing one app’s config back and forth — each environment gets its ownSLACK_CLIENT_ID/SLACK_CLIENT_SECRET/SLACK_SIGNING_SECRET/SLACK_REDIRECT_URI. This avoids local testing ever breaking a shared staging or production connection.
This is separate from how many Slack workspaces one deployment can serve: any number of teams can each connect their own workspace to the same Cognee deployment through the same Connect flow — that’s already multi-tenant by design and needs no extra setup per workspace.
Troubleshooting
Command `/cognee-remember` is not yet supported: most likely your backend is on a Cognee release older than1.5.0, which has the integration but not this command. Runpip install -U cognee, or run thedevcheckout directly.- Same message, correct version installed: either the manifest you installed from predates the command and the app was never reinstalled (see the reinstall warning above — this is the most common cause), or you are running stale code — restart the backend process (or rebuild the container if containerized).
- Same message, but a different app name answers: another Cognee app in the workspace owns the command. Slack shows all of them in autocomplete — pick yours explicitly, or test in a workspace where only your app is installed.
- Typing the command produces nothing at all, no request reaches your server: the command is not registered on your app. Check api.slack.com/apps → your app → Slash Commands; if it’s missing, the manifest save silently failed. Add it via Create New Command, then reinstall.
- “Something went wrong when authorizing Cognee” during Connect: the
redirect_uriyour backend sent (fromSLACK_REDIRECT_URI) isn’t byte-identical to an entry under the app’s redirect URLs, or.envstill holds a different app’sSLACK_CLIENT_ID/SLACK_CLIENT_SECRETthan the workspace you’re installing into. Re-sync.envto the exact app you’re testing and restart. - Callback redirects to
?slack=error_exchange_failed: the callback never 500s — it catches everything and redirects. The traceback is in the backend log. Usual causes:SLACK_REDIRECT_URImismatch, or a missing/malformedINTEGRATION_CREDENTIALS_KEY. - Signature verification fails on every request (401):
SLACK_SIGNING_SECRETin.envdoesn’t match the app you actually installed — easy to hit when juggling more than one test app. Copy it again from Basic Information and restart the backend. - Request URL mismatch after restarting ngrok: free-tier ngrok mints a new random host every restart. Update every request URL and redirect URL in the app’s Slack settings — or use a reserved/static domain so this never happens.
- Slack shows “dispatch_failed” instead of a reply: the slash command’s request URL is missing or wrong — it must point at
/api/v1/slack/commands, not/api/v1/slack/eventsor/api/v1/slack/interactive. - “This Slack workspace is not connected to Cognee”: the OAuth install (Connect in the Cognee UI) hasn’t completed for this workspace yet, or the connection was later disconnected/revoked.
- “I don’t know which Cognee account you are yet”: the member running the command has no active
/cognee-link, and isn’t the member who completed the workspace’s Connect — so the command is refused before it reads or writes anything, rather than falling back to the installer’s memory. Run/cognee-linkand confirm the link in a browser where you’re logged in to Cognee. No API key is involved; if the reply instead tells you to run`/cognee-link <api_key>`, you’re on code from before this was fixed — that argument never existed. - Can’t delete a dataset — deletion hangs or errors: a running Cognee backend holds an exclusive lock on the graph database via a worker subprocess. Stop the server first, then delete.
- Asking returns an “I don’t know”-phrased answer as if it were real: known limitation — the ask handler substring-matches common refusal phrasings (“cannot answer”, “no relevant information”, “does not contain”, …) before forwarding an answer, but new phrasings can still slip through.
Standalone Channel-Memory Bot
cognee-integration-slack is a separate Slack bot from the cognee-integrations repository. Instead of per-person slash commands, it gives a workspace per-channel memory: once a channel opts in, the bot silently ingests its messages into that channel’s own Cognee dataset, and anyone can ask @cognee <question> or /recall <question> to get an answer with cited links back to the source Slack messages.
It is a thin client to a running Cognee server over HTTP (POST /api/v1/add, /cognify, /search, /forget) — no in-process Cognee and no LLM key of its own, since the server holds those. Slack I/O runs over Socket Mode, so it needs no public URL, no tunnel, and none of the manifest or OAuth setup above.
Install
The package is not published on PyPI yet. Install it from the repository:slack-bolt, aiohttp, and httpx. uv sync creates a project venv without activating it, so run the bot through uv run (below) or activate .venv first; pip install -e . into an already active environment needs neither.
Create the Slack App
In api.slack.com/apps, create an app with Socket Mode enabled, then:- Copy the bot token (
xoxb-…) and the app-level app token (xapp-…). - Subscribe the bot to the
message.channelsandapp_mentionevents. - Add the slash commands
/recall,/cognee-optin,/cognee-optout, and/cognee-forget. - Install the app to the workspace and invite the bot to the channels it should remember.
Configure and Run
The bot reads everything from environment variables (a.env.example ships in the package directory):
Commands
An empty question gets a usage hint back. A channel with no dataset yet — never opted in, or opted in with nothing cognified — returns “I couldn’t find anything about that in this channel’s memory yet.”: the server answers the search with a
404 for the unknown dataset, which the bot treats as an empty result. Once the channel has memory, an unanswerable question gets whatever the LLM produces from the available context; unlike the built-in app, the bot does not filter refusal phrasings. A server-side failure (LLM auth, rate limit, a failed cognify) returns “Sorry — I hit an error answering that.” rather than silence; the cause is in the bot’s logs.
How It Works
- One dataset per channel. Messages land in the dataset
slack_<channel_id>, tagged with a node set of[<channel_id>]. The dataset is the boundary/cognee-forgetclears, so forgetting is per channel, never per person. - Batched graph building. Each message is added with a cheap
addcall;cognifyruns once a channel hasCOGNEE_SLACK_COGNIFY_BATCHpending messages, and always before answering a question, so an answer reflects everything said so far. Concurrent cognify runs on one channel are serialized. - Cited answers. Answering runs two searches:
GRAPH_COMPLETIONfor the prose, andCHUNKSfiltered to the channel’s node set for the source messages. Each stored message carries a one-line provenance header ([cognee-slack] channel=… ts=… author=… permalink=…) inside its text, which is parsed back out of the retrieved chunks to build the links. The reply is a Block Kit message: the answer, then up to five sources as links labelled#<channel ID> · <user ID> · <UTC time>(raw Slack IDs, not names — the link itself opens the original message) with a+N morenote for the rest. A message whose permalink could not be resolved shows as plain text, never a broken link. - What gets ingested. Messages and thread replies in opted-in channels. Edits, deletions, joins, bot messages, the bot’s own posts, messages that mention the bot, and empty messages are skipped. Public channels only, via
message.channels. Edited or deleted Slack messages are not re-synced.
Tests
The suite runs without Slack, Cognee, or LLM credentials — the adapter runs against a fake HTTP client, the handlers against mocks:Related
Channel-Memory Bot Source
Source and tests for
cognee-integration-slack.dlt Integration
Pull Slack channel history in bulk via dlt, instead of a live bot.
Cloud Integrations UI
Where “Connect Slack” lives in the Cognee UI.
Node Sets
How per-channel node-set tags scope retrieval.