Files
twenty/packages/twenty-apps/public/slack/SETUP.md
T
Abdul Rahman 72322a4d72 feat: Slack conversational assistant (#22984)
## 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"
/>
2026-07-30 09:36:38 +00:00

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

  1. Create a Slack app at api.slack.com/apps. Use a dedicated app — do not reuse one across Twenty apps.

  2. OAuth & Permissions → Bot Token Scopes. Twenty uses Slack's bot OAuth (oauth/v2/authorize with scope=…), 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:read conversations.list and the channel picker (public channels)
    chat:write post, update, delete, ephemeral
    chat:write.public post to public channels without the bot joining
    groups:read list private channels the bot is in
    reactions:write add reactions
    app_mentions:read assistant: mentions of the bot
    channels:history assistant: thread follow-ups in public channels
    groups:history assistant: thread follow-ups in private channels
    im:history assistant: direct messages

    Adding or removing scopes later means existing installs must re-authorize: disconnect and Add connection again.

  3. 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 usually http://localhost:3000 (confirm the port twenty-server / SERVER_URL actually uses).

    PKCE and localhost: if you enable PKCE on the Slack app, Slack treats http://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 an https:// redirect (ngrok, Cloudflare Tunnel), register it in the Slack app, and point SERVER_URL at the same base URL. See Slack's Using PKCE docs. This is separate from Twenty sending a PKCE challenge on the authorize request.

  4. Copy the Client ID and Client Secret.

2. Twenty

  1. Install this app (slack) on your Twenty server.
  2. Settings → Applications → Twenty Slack → Application registration (admin only), set SLACK_CLIENT_ID and SLACK_CLIENT_SECRET.
  3. 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.

  1. Signing secret. In Application registration, set SLACK_WEBHOOK_SECRET from your Slack app (Basic Information → App Credentials). The server verifies every Slack Events request with it.

  2. 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-084f3c8b0b09
    

    That ID is the slack-events-resolver logic function. Slack signs the handshake, so SLACK_WEBHOOK_SECRET must 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 channel
    • message.im — direct messages to the bot
    • message.channels — replies in public-channel threads, for un-mentioned follow-ups
    • message.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.

  3. Reconnect so the token picks up the assistant scopes.

  4. Role. The slack-assistant agent 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 ID
  • messageText, newMessageText — body to post / the replacement on update
  • messageTimestamp — Slack's per-message id, same value as the tool output slackTs when chaining steps
  • parentMessageTimestamp — thread replies only
  • messageFormatmarkdown sends the body as Slack markdown_text (**bold**), plain sends text with markup disabled, omitted uses Slack's default for text
  • recipientSlackUserId — ephemeral steps
  • emojiName — Slack shortcode, for example white_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 bot
  • POST /slack/messages — posts a message