Connect Slack once, invite Fredrin into the channels you want to use, and tell
it where each message should go: @Fredrin send to #engineering: The update is ready. Mentions get replies in their original channel/thread. A later
request can send to another joined channel without changing settings.
No alert channel is required. Automatic ticket-status alerts are a separate, optional setting and start off on new connections. Existing saved alert preferences are preserved.
Fredrin generates the Slack app as a manifest you paste into Slack. Add the bot token and signing secret in Fredrin, then invite the bot into your channels.
It is bring-your-own-app: you create a small Slack app in your own Slack workspace and paste its bot token into Fredrin. Fredrin hosts no shared Slack app, so nothing about your Slack workspace is visible to anyone else; the token and signing secret are stored encrypted and never logged.
What you get
-
Optional ticket alerts, on the columns you choose. When enabling alerts, these three columns are suggested:
- Review — a Worker finished and opened a PR:
🔍 Ready for review — FRED-XXXX <title> - Blocked — a Worker stopped and needs a human:
⛔ Agent blocked — needs your input — FRED-XXXX <title> - Completed — the ticket shipped, whether you dragged it there or a merge
completed it:
🎉 Done — FRED-XXXX <title>
Use Automatic status alerts in the Slack row’s menu to choose a channel and columns, including custom steps (
➡️ Moved to <Column>). Switch alerts off to pause them while keeping the saved channel, columns, and messaging. - Review — a Worker finished and opened a PR:
-
@Fredrinmentions — say what you want in your own words and the bot works out what to do, answering in a thread. It can file tickets, look them up, list what is sitting in a column (with each ticket's pull-request state), read one ticket in full, comment on them, move them between columns, assign them, start an AI Worker building one — and answer questions about the codebase itself by reading the real repository on your paired machine. -
No re-tagging inside its own threads (the manifest subscribes the events this needs) — once the bot has replied in a thread, keep talking to it normally. Plain replies are picked up, so a follow-up is "and make it high priority", not "@Fredrin and make it high priority", and a confirmation is just "yes". Elsewhere in the channel nothing changes: the bot only listens inside threads it has posted in.
-
/fredrinslash command (optional) — the same assistant, answered privately to you. It can also send a requested message to a joined channel. It has no thread, so it can file, search and comment but not move, assign or start a build.
Prerequisites
- A Fredrin project (in the desktop app) you want to wire up.
- Permission to create and install apps in your Slack workspace.
- About two minutes.
- A Fredrin deployment with
CONNECTOR_ENCRYPTION_KEYset. It is required to save any pasted-key connector; without it the Connectors panel shows a "connecting is unavailable" banner. (Self-hosting only — it is already set onfredrin.com. That banner also names OAuth client IDs, which Slack does not need.)
1. Create the Slack app from Fredrin's manifest
- In the Fredrin desktop app, open your project → Project settings → Connectors → Slack → Connect. (Workspace owners: use Settings → Workspace → Connectors instead to serve every project in the workspace with one app.)
- The form shows a ready-made app manifest for your Fredrin server —
scopes, bot user, events, Request URL and the
/fredrincommand, all filled in. Click Copy manifest. - Click Open Slack (or go to api.slack.com/apps) → Create New App → From an app manifest → pick your Slack workspace → paste → Create.
- OAuth & Permissions → Install to Workspace → authorize.
- Copy two values — you'll paste both into Fredrin:
- the Bot User OAuth Token (OAuth & Permissions; starts
xoxb-) - the Signing Secret (Basic Information → App Credentials)
- the Bot User OAuth Token (OAuth & Permissions; starts
The manifest asks for exactly the scopes the integration uses and nothing else:
chat:write (post requested messages, alerts, and replies), channels:read +
groups:read (list channels and verify requested destinations), channels:join (let Fredrin add itself to the
public channel you pick), app_mentions:read (receive @Fredrin), and
channels:history + groups:history (read the thread it is already replying
in, which is what makes follow-ups and untagged replies work).
Running Fredrin on
localhost? Slack checks the Request URL while creating the app and cannot reach your machine, so it will refuse the manifest. Delete the"event_subscriptions"block before pasting: alerts and everything outbound still work;@Fredrinmentions need a reachable URL.
2. Connect in Fredrin — no alert channel required
In Settings → Connectors, choose the workspace or project scope, open
Slack → Connect, and paste the Bot User OAuth Token and Signing Secret.
The signing secret enables mentions, thread replies, and /fredrin; it does
not grant channel access. Click Connect Slack. Leave the optional alert
channel empty to use on-demand messaging only.
In Slack, invite Fredrin into each channel you want it to use with
/invite @Fredrin. Public and private channels both require membership for
on-demand sends. Fredrin does not join extra destinations during a send.
The optional alert-channel dropdown can reserve a destination for later; choosing
one while connecting does not enable automatic alerts. It always stays a
dropdown during loading or failure. Use Retry channels after correcting a
permission/connection problem, or Refresh channels after inviting the bot.
The list includes up to 1,000 channels across five pages, excluding archived ones.
If listing lacks permissions, add channels:read and groups:read under
OAuth & Permissions → Bot Token Scopes, reinstall the Slack app, then retry.
To rotate a bot token, choose Update credentials on the connected row. Blank signing-secret and channel fields keep their stored values, and the alert switch and column choices carry forward. Disconnecting revokes the credential; a new connection starts with alerts off.
3. Turn on automatic status alerts (optional)
Open the connected Slack row’s ⋯ → Automatic status alerts. Turn the switch on, pick an Alert channel, select at least one column, and choose Save status alerts. Review, Blocked, and Completed are suggested when there is no saved column selection. Fredrin joins a public alert channel for you; invite it to a private channel yourself.
Turn the switch off and save to stop automatic alerts. The saved channel and column choices remain available when you turn it back on. Mentions and requested messages keep working throughout.
Existing connections with a saved alert channel retain their previous behavior, including custom columns or an explicitly empty set that silences alerts. A connection with no saved channel stays off. Each project override has its own alert preferences; inherited connections are managed in workspace settings.
4. Check the event Request URL (mentions only)
The manifest already set it, and Slack verified it while creating the app. If
you removed the event_subscriptions block (localhost), or you are wiring an
app you created by hand, set it now:
- Event Subscriptions → Enable Events on.
- Request URL:
https://www.fredrin.com/api/webhooks/slack— it should show Verified within a moment. - Subscribe to bot events → add
app_mention(required), plusmessage.channelsandmessage.groups(what lets you reply inside the bot's own threads without re-tagging it — optional; without them, tag the bot every time). - Save Changes, and reinstall the app if Slack prompts for it.
5. (Optional) the /fredrin slash command
The manifest already creates it. Its replies are private to whoever ran it
instead of threaded in the channel; mentions cover intake, so use it only if
your team prefers that. To add it to a hand-made app: Slash Commands →
Create New Command: command /fredrin, Request URL
https://www.fredrin.com/api/webhooks/slack, description
File and track Fredrin tickets, usage hint <what you need> | status | help.
6. Try it
- In a channel the bot is in:
@Fredrin file a ticket to test the Slack integration. The bot replies in a thread with the ticket identifier and column; the ticket is on your board's Backlog. @Fredrin status→ per-column counts.@Fredrin status review→ the tickets in that column, each with its identifier, title and PR state.- If you enabled automatic alerts, move any ticket into Review (or wait for a Worker to finish one) — the
alert appears in your alert channel. Drag it on to Completed and you get
the
🎉 Doneline; so does a PR merge that auto-completes it.
Using it day to day
-
Send to the channel you name. From any joined channel, ask
@Fredrin send to #engineering: The update is ready. Later, ask for#designinstead. A Slack channel mention keeps its ID, so a renamed channel or two identical names cannot silently change the destination. The bot reports the actual destination after Slack accepts the message. It sends one message to one channel per request, with no fallback to the alert channel and no bulk broadcast. Include the content to send; the whole source thread is not automatically forwarded. Messages are limited to 4,000 characters and broadcast pings are removed. -
A destination problem is actionable. Missing or ambiguous names ask you to correct the name or mention the exact channel. An unjoined channel needs
/invite @Fredrin; an archived one must be unarchived or replaced. A name lookup past the five-page bound asks for a mention or ID, and a different Slack workspace is refused. If delivery cannot be confirmed after a network failure, check the destination before retrying to avoid a duplicate. -
Just say it. There is no command grammar to learn. "hey can you make a ticket for the login bug", "the signup page is broken, file that", "what's in review?", "move FRED-AB12CD to review", "assign it to Sam", "start FRED-AB12CD" all work. The bot writes a board-shaped title rather than pasting your message, files into the project's entry column (Backlog on a default board), and answers in the thread. Attribution goes to the member who connected Slack, and a title identical to one filed in the last 24 hours is rejected as a duplicate.
-
Follow-ups work in a thread, and don't need the tag. Once the bot has replied in a thread, just keep talking: "actually put that in the other project". It re-reads the thread for context each time and stores nothing between messages — the thread itself is the memory. (This needs the
message.channelsevent, which the manifest subscribes; on a hand-made app without it, mention the bot again each time.) Outside its own threads the bot stays quiet: a new top-level message in the channel still needs@Fredrin, and a thread it has never posted in is none of its business. -
It asks before anything you'd want back. Completing a ticket, taking a ticket off its current assignee, and starting a Worker (which spends real compute) each get a confirmation first. Reply in the same thread to go ahead — a plain
yesis enough. (The ask says@Fredrin yes, which always works and is the only form that works without themessage.channelsevent.) Only the person who asked can confirm, and a confirmation goes stale after ten minutes. -
Several projects on one Slack app? Name one: "file it in my-project",
@Fredrin status my-project. With a single connected project you never need to. If it is ambiguous the bot asks rather than guessing. -
Board glance.
@Fredrin statuslists per-column counts and answers instantly — it is the one phrase that skips the assistant entirely, so it costs nothing and never queues. Add a column and it names the tickets instead of counting them:@Fredrin status review,@Fredrin status building. The column can be its board name, its short key, or enough of the name to be unambiguous — if two columns match, the bot asks which. Asking in your own words ("what's in review?", "which tickets are running?") works too and goes through the assistant. -
A listing shows the PR. Each ticket in a column listing (or a search result) ends with what the board card shows: its pull request and whether it merged, how its verification run went, who it is assigned to, its priority and its due date —
FRED-AB12CD Fix the login bug — PR #1234 open · 5/6 verification checks · Jae Lee · High · due 2026-08-20. So "what's in review?" answers with which tickets are actually waiting on a merge, not just their titles. A ticket with none of that adds nothing to its line. The verification run is stated, never graded — same as on the board, because most of those checks are not merge-blocking and a partial run is not a failure. -
One ticket, in full. Ask about a specific ticket — "what's the status of FRED-AB12CD?", "did that one ship?", "is its PR merged?", "what did people say on it?" — and you get its column, every linked PR with its state and check outcome, the assignee and creator, labels, priority, due date, goals, anything still blocking it, its description and its latest comments. Long descriptions and comments are shortened to a line each, and the reply says the real comment count when it is showing only the newest few.
-
Ask about the code. "how does auth work in this project?", "where is the rate limiter?", "what does
webhook-slack.tsdo?" — the bot hands the question to the project's own coding agent, which reads the actual repository on the paired machine of the member who connected Slack and posts what it finds back into the thread, usually within a minute or two. The run is read-only (it cannot edit anything) and uses that machine's own Claude subscription. It needs that member's paired machine to be online, already holding a clone of the project, and running an up-to-date Fredrin agent — the bot tells you in the thread when one of those is missing. Codebase questions work from mentions and thread replies, not from/fredrin(the answer needs a thread to land in). -
Privacy. Slash replies are ephemeral (only you see them); mention replies are threaded in the channel. Alerts carry the ticket identifier, title, project and column name only. Nothing the bot posts is agent conversation content — no Worker prompts or replies, no terminal output, no screenshots. A ticket's own description and comments are board data and can be read back into the channel; if that is not wanted in a shared channel, use the bot from a private one.
-
Acting on an alert. Alerts name the ticket (
FRED-XXXX); open the Fredrin desktop app to review the diff and ship or send back. -
Automatic alerts are per connection. One automatic-alert channel per Slack connection; on-demand destinations are chosen per request. Connect at the workspace level and every project alerts into that channel; give one project its own Slack connection (Override for this project) and it gets its own channel and its own alert events.
Troubleshooting
- The event Request URL won't verify in Slack. Check it is exactly
https://www.fredrin.com/api/webhooks/slackand that the Fredrin server is reachable from the internet (alocalhostdev server is not — see step 1). You do not need to connect the connector first any more: the handshake is answered before any credential is involved, precisely so Slack can verify the URL while creating the app from the manifest. @Fredringets no reply. The bot must be in the channel (/invite @Fredrin); the app must be subscribed to theapp_mentionevent (step 4); and the connector must be connected with the signing secret.- Untagged replies in a thread are ignored. Three usual causes: the
message.channelsbot event is not subscribed (step 4 —message.groupsfor private channels); thechannels:historyscope is missing, so the bot cannot check whether the thread is one of its own; or the bot has not actually posted in that thread yet. It deliberately only answers untagged messages in threads it has spoken in — start the conversation with@Fredrinand every reply after that works without the tag. - The bot says it can't read the thread. Add the
channels:historyscope (groups:historyfor private channels) in OAuth & Permissions and reinstall the app. Without it the bot can answer a one-shot request but cannot see a thread, so follow-ups, confirmations and untagged replies do not work. - It replies "Slack intake needs an OpenRouter key". The assistant runs on
an LLM, and this Fredrin deployment has no OpenRouter key configured. An
admin adds one in the admin console;
statusandhelpkeep working meanwhile. - It says it is being asked a lot in this channel. Each channel gets a
budget of about five assistant replies a minute so a busy channel cannot run
up a bill. Wait a moment and ask again;
statusis never throttled. /fredrinshows a Slack error ("dispatch_failed"). Same causes: request URL wrong, or the connector was connected without the signing secret → reconnect and paste it.- No automatic alerts arrive. New connections start with alerts off. Open Automatic status alerts, turn them on, choose a channel and at least one column, then save. The bot must be in the channel; private channels need an invitation. These settings do not affect requested messages.
- A column I ticked posts nothing. The alert set belongs to the credential that this project resolves to. If the project inherits the workspace Slack connection, change the set in Settings → Workspace → Connectors; if it has its own override, change it on the project row. The two are separate.
- "Slack rejected the bot token." The token was revoked or reinstalled
away. Reinstall the app to your Slack workspace and reconnect with the fresh
xoxb-token — blank optional fields keep their stored values. - Commands rejected after long queues or clock skew. Requests older than five minutes are refused (replay protection). Just re-run the command.
Security notes
- The bot token and signing secret are stored AES-256-GCM-encrypted in your Fredrin workspace and are never logged or shown again.
- Every inbound request that does anything — a mention, a thread reply, a slash command — is verified against your app's signing secret (timing-safe HMAC with a five-minute replay window) before anything is read or created. Unverified requests are rejected. The one exception is Slack's URL-verification handshake, which is answered without a signature because it has to work before your app exists: the reply is the challenge string Slack itself just sent, echoed back. It reads nothing, identifies no workspace and writes nothing.
- Redelivered events are deduplicated, and the bot never acts on bot-authored messages (no reply loops).
- Alerts are one-way notifications with ticket metadata only — identifier, title, project and column name; disconnecting the connector stops both directions immediately, and turning off Automatic status alerts stops status notifications while retaining on-demand messaging.
- The assistant reads the thread it is replying in, and sends it to an LLM. When you talk to the bot inside a thread, that thread's recent messages are read at that moment and included in the request to the model provider (OpenRouter) so follow-ups make sense. It is never read from any other channel or thread, and none of it is stored — not by Fredrin, not between messages. The only things written to your board are the ones you asked for: a ticket, a comment, a move. A request to send a message posts only the requested content to the named channel; that content is not stored by Fredrin.
- What
message.channelsdoes and does not expose. Subscribing to it means Slack delivers every message in the channels the bot is in. Fredrin acts on one slice of that — a reply inside a thread its own bot has posted in — and reads a thread's contents only for such a reply. A message anywhere else, and every thread the bot has not spoken in, is discarded without being read, answered, sent to a model, or stored. If that is more than you want in a shared channel, leave the event unsubscribed and keep tagging the bot; everything else works unchanged. - A codebase question leaves Slack for your own machine. When someone asks about the code, the question (as the assistant rephrases it) is sent to the paired machine of the member who connected Slack and answered there by that machine's Claude Code, reading the local clone — on that member's own subscription. The run is read-only, and nothing about it is stored by Fredrin: not the question, not the answer, no session, no transcript. The answer — which is derived from your repository's contents — is posted into the channel thread, so treat channels the bot is in as places repo detail may appear.
- Anyone who can mention the bot can act as the member who connected Slack. Slack identities are not mapped to Fredrin accounts, so the bot carries out every request with that member's permissions — including starting a Worker, which spends compute. The in-thread confirmation is there to catch mistakes, not to authorize: whoever can ask can also confirm. Invite the bot only to channels whose members you would give that access to.