## Summary Lets workspace members talk to the Twenty CRM agent from Slack — `@mention` the bot in a channel or DM it, and it answers in-thread using the `slack-assistant` agent and its assigned role. ## How it works Slack Events webhook → app route verifies signature → **ack in <3s** and enqueue a `slackAssistantRequest` → worker posts a placeholder immediately, then fetches recent thread/DM history (excluding the current message and placeholder), runs `runAgent`, and updates the placeholder with the answer. After a successful reply, the thread stays subscribed (24h TTL, renewed on each reply) so follow-ups work without re-mentioning. ## App-owned orchestration Protocol + orchestration live in `twenty-apps/public/twenty-slack` (events resolver, enqueue, worker, team claim KV, thread subscription). The server provides shared primitives (app routes, `runAgent`, app KV, connection OAuth). ## Notes - Agent role is bound via `roleUniversalIdentifier` on install. Default **Slack Assistant** role: read/create/update/soft-delete on people, companies, opportunities, notes, and tasks; **workspace members stay read-only**; hard destroy stays off. Admins can tighten the role in Settings. - Setup (signing secret, event subscriptions, scopes) is in the app README. - Long-lived Slack bot tokens (no refresh token) are treated as non-expiring. - Multi-turn: recent Slack thread/DM messages are prepended into the agent prompt. - Replies are non-streaming for now (placeholder + final `chat.update`); progressive streaming is a follow-up. ## Follow-ups - **Streaming replies** — progressive edits while the agent runs. - **Per-user / per-channel permissions** — Slack→Twenty user mapping and optional channel rules (open by default; admins can narrow). - **Other platforms** — Discord/Teams can reuse the same patterns; only Slack protocol is in this PR. ## Screenshots https://github.com/user-attachments/assets/3a72770a-93fa-411d-b4aa-2f741afbcee1 <img width="426" height="686" alt="Screenshot 2026-07-27 at 3 58 38 PM" src="https://github.com/user-attachments/assets/b0a62e7c-c5e4-4c96-9389-5e47d7ef8c77" /> <img width="1053" height="726" alt="Screenshot 2026-07-29 at 12 54 45 AM" src="https://github.com/user-attachments/assets/4e14b3fb-fbe5-4f4d-a380-cc45cc60a01a" />
5.9 KiB
Setup
Two parts: a Slack app you create, and the Twenty side where you paste its credentials and connect. The conversational assistant needs a third part on top.
1. Slack app
-
Create a Slack app at api.slack.com/apps. Use a dedicated app — do not reuse one across Twenty apps.
-
OAuth & Permissions → Bot Token Scopes. Twenty uses Slack's bot OAuth (
oauth/v2/authorizewithscope=…), so scopes must be added here and not only under User Token Scopes, otherwise Slack refuses the install with "doesn't have a bot user to install".The scopes requested at connect time must all appear under Bot Token Scopes (Slack validates the set):
Scope Used for channels:readconversations.listand the channel picker (public channels)chat:writepost, update, delete, ephemeral chat:write.publicpost to public channels without the bot joining groups:readlist private channels the bot is in reactions:writeadd reactions app_mentions:readassistant: mentions of the bot channels:historyassistant: thread follow-ups in public channels groups:historyassistant: thread follow-ups in private channels im:historyassistant: direct messages Adding or removing scopes later means existing installs must re-authorize: disconnect and Add connection again.
-
Redirect URL. Set it to
<YOUR_TWENTY_SERVER_URL>/auth/apps/callback— the origin your Twenty server uses for API routes, not the SPA. Local monorepo dev is usuallyhttp://localhost:3000(confirm the porttwenty-server/SERVER_URLactually uses).PKCE and
localhost: if you enable PKCE on the Slack app, Slack treatshttp://localhost…as a desktop redirect, and desktop redirects cannot request bot scopes — OAuth will fail. For local dev either leave Slack's PKCE opt-in disabled, or use anhttps://redirect (ngrok, Cloudflare Tunnel), register it in the Slack app, and pointSERVER_URLat the same base URL. See Slack's Using PKCE docs. This is separate from Twenty sending a PKCE challenge on the authorize request. -
Copy the Client ID and Client Secret.
2. Twenty
- Install this app (
slack) on your Twenty server. - Settings → Applications → Twenty Slack → Application registration (admin only), set
SLACK_CLIENT_IDandSLACK_CLIENT_SECRET. - Connections → Add connection, choose Just for me or Workspace shared, complete the Slack sign-in.
Workflow steps then use that connection's access token: a workspace connection is preferred when present, otherwise the first connection returned for the Slack provider.
For posting, either invite the bot to the channel or rely on chat:write.public for public channels. Private channels always require membership.
3. Conversational assistant
The assistant reuses the same Slack connection — no second bot identity.
-
Signing secret. In Application registration, set
SLACK_WEBHOOK_SECRETfrom your Slack app (Basic Information → App Credentials). The server verifies every Slack Events request with it. -
Event subscriptions. On the Slack app, enable Event Subscriptions and set the Request URL to:
<YOUR_TWENTY_SERVER_URL>/webhooks/server/9ad6fa20-dff5-4d3f-ad5f-084f3c8b0b09That ID is the
slack-events-resolverlogic function. Slack signs the handshake, soSLACK_WEBHOOK_SECRETmust be set first or Slack reports "didn't respond with the value of the challenge parameter."Under Subscribe to bot events, add:
app_mention— mentions of the bot in a channelmessage.im— direct messages to the botmessage.channels— replies in public-channel threads, for un-mentioned follow-upsmessage.groups— same, for private channels the bot is in
Invite the bot to any channel where it should follow threads. Slack may ask you to reinstall after changing subscriptions.
-
Reconnect so the token picks up the assistant scopes.
-
Role. The
slack-assistantagent binds to the app's Slack Assistant role automatically on install and upgrade. Anyone who can message the bot acts with that role — Slack users are not mapped to individual Twenty members yet, so keep the role scoped to what you're comfortable exposing.
Behaviour notes
- Thread memory. After a successful reply the bot stays active in that thread, so follow-ups need no mention. Channel threads stay active for 24 hours after the last reply (each reply renews it); DM threads never expire.
- One Slack workspace per Twenty workspace. Connecting Slack claims that Slack team for the connecting Twenty workspace. On the same server, a second Twenty workspace connecting the same Slack team is rejected. The claim is not released on disconnect yet, so moving a Slack workspace needs a server admin.
Workflow field names (for step authors)
Fields use camelCase in the step UI:
slackChannelId— channel or DM, name or IDmessageText,newMessageText— body to post / the replacement on updatemessageTimestamp— Slack's per-message id, same value as the tool outputslackTswhen chaining stepsparentMessageTimestamp— thread replies onlymessageFormat—markdownsends the body as Slackmarkdown_text(**bold**),plainsendstextwith markup disabled, omitted uses Slack's default fortextrecipientSlackUserId— ephemeral stepsemojiName— Slack shortcode, for examplewhite_check_mark
HTTP routes
The Send Slack message command menu item is backed by two app routes, both requiring an authenticated Twenty user and using the same Slack connection as the workflow steps:
GET /slack/channels— lists channels visible to the botPOST /slack/messages— posts a message