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

93 lines
5.9 KiB
Markdown

# 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](https://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](https://docs.slack.dev/authentication/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:
```text
<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
- `messageFormat` — `markdown` 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