Files
twenty/packages/twenty-apps/public/slack
Abdul Rahman 42aa566e32 feat(twenty-slack): link records and format the assistant reply footer (#23539)
Follow-up to the Slack bot branch, improving how assistant replies read
in Slack.

## Problem

The assistant never had the workspace URL. `buildSlackAssistantPrompt`
injected the request, requester and thread context, and the agent prompt
said nothing about links, so a record it created or found came back as
plain text with no way to open it. The reply was also a single
`markdown_text` blob with `_Answered in 3s_` appended to the answer.

## Changes

**Record deep links.** `fetchWorkspaceBaseUrl` resolves the workspace
URL from `currentWorkspace { workspaceUrls }`, preferring a custom
domain over the subdomain. It runs in parallel with the existing Slack
context fetch, so no extra latency. The prompt carries the base URL plus
the `[Record Name](base/object/<objectNameSingular>/<recordId>)` rule.
When the URL cannot be resolved the prompt explicitly forbids writing
any Twenty URL, so a failed lookup degrades to plain record names rather
than invented links.

**Reply structure.** The answer now goes out as Block Kit: a `markdown`
block for the body and a `context` block for the duration, so it reads
as a footer rather than italic text tacked onto the answer.
`getSlackChatMessageBodyFields` grew a blocks variant that keeps the
message text as Slack's notification and screen-reader fallback, and
`slackUpdateMessageHandler` now falls back to plain text on
`invalid_blocks` for blocks as well as markdown.

## Screenshots

### Before
<img width="344" height="161" alt="Screenshot 2026-07-30 at 8 13 55 AM"
src="https://github.com/user-attachments/assets/c4a76654-b4bc-4f3d-a8ad-a00073ec5674"
/>

### After
<img width="408" height="126" alt="Screenshot 2026-07-30 at 8 26 15 AM"
src="https://github.com/user-attachments/assets/ba2ec249-0520-42ca-87d1-c272515bddef"
/>


---
_Generated by [Claude
Code](https://claude.ai/code/session_0148FpKn9T41aVHsZZ2d1Lrw)_

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/23539?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
2026-08-05 08:45:46 +00:00
..
2026-07-31 14:38:33 +00:00
2026-07-31 14:38:33 +00:00
2026-07-31 14:38:33 +00:00

Slack

Your CRM, in the conversation — ask Twenty anything from Slack and post back to any channel.

What you get

  • A CRM assistant in Slack@twenty how many open opportunities do we have? or @twenty create a company called ACME. It answers in-thread, remembers the thread, and can read, create, update and soft-delete records
  • Follow-ups without re-mentioning — once it has replied in a thread you can keep talking to it for 24 hours
  • Slack steps for your workflows — post, update or delete messages, send ephemerals, add reactions, list channels
  • Send from anywhere in Twenty — the Send Slack message command opens a side panel to pick a channel and post

🤖 The assistant

Mention the bot in a channel or DM it. It replies in the thread with your CRM data, using the recent conversation as context.

Anyone who can message the bot acts with the Slack Assistant role, which by default can read, create, update and soft-delete people, companies, opportunities, notes and tasks. Workspace members stay read-only and hard delete is off. Tighten the role in Settings → Roles if you want a narrower bot.

One Slack workspace answers into one Twenty workspace.

When the bot is added to a channel it introduces itself once, with a short message in the channel and the details (what to ask it, what it reads, and the shared-role caveat above) in a thread reply. It needs the member_joined_channel subscription, so leave that one off if you want the bot to arrive quietly.

🧰 The workflow steps

Step Slack API
slack-post-message chat.postMessage
slack-post-ephemeral-message chat.postEphemeral
slack-update-message chat.update
slack-delete-message chat.delete
slack-add-reaction reactions.add
slack-list-channels conversations.list

Pick a workspace shared or just for me Slack connection; steps run with that token.

💳 Billing

Free — no credits, no metering.

📌 Heads up

You need to create a Slack app and connect it — see SETUP.md. The assistant needs a few extra steps (signing secret and event subscriptions) on top of the base connection.