Recipe: Slack intake and approvals
Three pieces, each usable on its own:
- Intake: a top-level message from a person in a chosen channel becomes one card, with its attached files. Replies go to that message's thread.
- Review posts: when a draft needs a review, BakedBrie posts it to Slack with Approve and Request changes buttons, then updates the message after a decision.
- Approvals: a Slack user who is linked to a BakedBrie member decides the review as that member. Board access, the named reviewer and "the maker cannot approve" apply exactly as in the app.
Reviews need a work folder on the board (set_work_folder). This page uses your own Slack app. When Add to Slack is on, see the Add to Slack variant instead.
Create the Slack app (the person does this)
- At
https://api.slack.com/apps, create an app in your Slack workspace. - Under OAuth and Permissions, add these bot token scopes:
chat:write,files:write,files:read,channels:history,groups:history,users:read. - Install the app to the workspace. Copy the Bot User OAuth Token (
xoxb-...) into theSLACK_BOT_TOKENenvironment variable. - Under Basic Information, App Credentials, copy the Signing Secret into the
SLACK_SIGNING_SECRETenvironment variable. - In the channel, type
/invite @<your app name>.
Event Subscriptions and Interactivity are turned on after the prompt runs, because they need the URL BakedBrie creates.
The prompt
Run it with a Full control token from a workspace owner or admin: Slack approvals count only when an owner or admin set the endpoint's signing secret, and only they can link approvers.
Fill in {{BOARD_NAME}}, {{START_COLUMN}}, {{SLACK_CHANNEL_ID}} (starts with C, or G for an older private channel), {{SLACK_TEAM_ID}} (starts with T), and for each approver their Slack member id and BakedBrie user id.
In BakedBrie, connect the board "{{BOARD_NAME}}" to Slack channel {{SLACK_CHANNEL_ID}} in Slack workspace {{SLACK_TEAM_ID}}. Use only the BakedBrie MCP tools. Start with whoami and stop if inbound, api_workflow, destinations or secrets is off.
For each secret, call prepare_secret with the kind, purpose and environment variable I give, run the command it returns in my shell exactly as given (one command per shell call), then pass the drop_id. Never ask me to paste a secret and never print one.
1. Find the board with list_boards and its columns with read_board.
2. Slack destination: prepare_secret kind slack, purpose destination, env_var SLACK_BOT_TOKEN. Create an API destination with manage_destination: action create, preset slack, name "Slack {{BOARD_NAME}}", slack {"channel_id": "{{SLACK_CHANNEL_ID}}", "team_id": "{{SLACK_TEAM_ID}}"}, credential {"drop_id": "<the drop id>"}.
3. Review posts: with manage_board_hooks action set, send review_requested and review_decided to that destination with their default actions. If I want delivery and publish notices in the thread too, also set delivered and published.
4. Intake: prepare_secret kind inbound_signing, purpose connection, env_var SLACK_SIGNING_SECRET. Create an inbound endpoint with manage_inbound_endpoint: action create, preset slack, name "Slack {{BOARD_NAME}} intake", board_id, start_stage_id the {{START_COLUMN}} column, slack {"channel_ids": ["{{SLACK_CHANNEL_ID}}"], "team_id": "{{SLACK_TEAM_ID}}"}, signing_secret {"drop_id": "<the drop id>"}, fetch_destination_id and reply_destination_id both the Slack destination from step 2.
5. Approvers: for each approver I list, call manage_member_mapping action set with provider slack, external_user_id their Slack member id, team_id {{SLACK_TEAM_ID}} and user_id their BakedBrie user id. Link nobody else.
Approvers: {{APPROVER_SLACK_ID_1}} = {{APPROVER_USER_ID_1}}
6. Read back manage_inbound_endpoint list, manage_board_hooks list and manage_member_mapping list. Reply with the endpoint's url (for Event Subscriptions) and the same url followed by /actions (for Interactivity), the hooks, and the links.
After it runs (the person does this)
- In the Slack app, Event Subscriptions: turn it on, paste the endpoint url as the Request URL (Slack checks it right away), and subscribe to the bot event
message.channels, plusmessage.groupsfor a private channel. - Interactivity and Shortcuts: turn it on and paste the url followed by
/actions. - Make sure each approver can open the board in BakedBrie (invite them in the app; invitations are human only).
- Post a test message in the channel. A card should appear in the start column.
No BakedBrie user id for an approver? Link them in the app instead: Settings, Members, Slack approvals lets an owner or admin pick the member from a list.
What happens in Slack
- Messages from bots, thread replies and other channels never become cards. Every request must carry a valid Slack signature; unsigned, stale or replayed requests create nothing.
- Attached files are downloaded with the bot token into the card and the work folder's Inbox/. The agent run waits until they land.
- A click is refused with a private note when:
- the Slack user is not linked (
NOT_MAPPED), - the linked member cannot open the board (
NO_BOARD_ACCESS), - the member made the draft (
MAKER_CANNOT_APPROVE), or - the board has a named reviewer and it is someone else (
NOT_NAMED_REVIEWER).
- the Slack user is not linked (
- Request changes sends the card back for another round; a note given with it goes to the next run.
Changing it later
- Disable intake:
manage_inbound_endpointactiondisable. Re-enable: actionupdatewithenabled: true. - Rotate the signing secret: a new drop, then
manage_inbound_endpointactionupdatewithsigning_secret. - Remove an approver:
manage_member_mappingactionlist, then actionremovewith themapping_id. - Stop review posts:
manage_board_hooksactionclearwith theevent. - More boards on the same Slack app: give each board its own Slack destination and hooks. A Slack app has one Interactivity URL, so keep the first endpoint's
/actionsURL there: clicks on another board's review message are decided there too, as long as the linked member can decide reviews on that board. A signing secret backs one endpoint, so messages that should become cards on a second board need a second Slack app (or Add to Slack, where one install feeds any number of boards). - Archiving a board turns off its inbound endpoints and hooks.