# 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](/docs/recipes/social-workflow-add-to-slack) instead.

## Create the Slack app (the person does this)

1. At `https://api.slack.com/apps`, create an app in your Slack workspace.
2. Under **OAuth and Permissions**, add these bot token scopes: `chat:write`, `files:write`, `files:read`, `channels:history`, `groups:history`, `users:read`.
3. Install the app to the workspace. Copy the **Bot User OAuth Token** (`xoxb-...`) into the `SLACK_BOT_TOKEN` environment variable.
4. Under **Basic Information**, **App Credentials**, copy the **Signing Secret** into the `SLACK_SIGNING_SECRET` environment variable.
5. 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.

```text
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)

1. 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`, plus `message.groups` for a private channel.
2. **Interactivity and Shortcuts**: turn it on and paste the url followed by `/actions`.
3. Make sure each approver can open the board in BakedBrie (invite them in the app; invitations are human only).
4. 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`).
- 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_endpoint` action `disable`. Re-enable: action `update` with `enabled: true`.
- Rotate the signing secret: a new drop, then `manage_inbound_endpoint` action `update` with `signing_secret`.
- Remove an approver: `manage_member_mapping` action `list`, then action `remove` with the `mapping_id`.
- Stop review posts: `manage_board_hooks` action `clear` with the `event`.
- 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 `/actions` URL 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.
