Recipe: the social post workflow
Someone asks for a social post in Slack (a brief plus inspiration images) and a card lands on the board. A generator agent makes an image carousel. An AI check agent reviews it, writes the captions, and either passes it or sends it back with notes (at most 3 rounds, then people see it anyway). The draft is posted to Slack with Approve and Request changes buttons. When a linked approver clicks Approve, the post is scheduled on WoopSocial in the next open slot, and BakedBrie posts the live links, then the stats, back in the same Slack thread.
This page is the main path, built from pieces available today:
- AI: the runner, which runs cards with the user's own Claude Code or Codex on their own computer. See Recipe: the runner.
- Slack: the user's own Slack app (a bot token and a signing secret). See Recipe: Slack intake and approvals.
- Work folder: the user's own S3 or R2 bucket. Reviews need a work folder.
- Social: a WoopSocial project. See Recipe: WoopSocial.
When available, three pieces get simpler. Each has its own page, and each page says "not available yet" until it is on:
- Add to Slack replaces your own Slack app.
- BakedBrie storage replaces your own bucket.
- Hosted AI replaces the runner with your own OpenAI key.
Before you run it
The person does these, once:
- Connect the MCP server: Connect Claude Code or Connect Codex. Use a Full control token from a workspace owner or admin (linking Slack approvers needs one).
- Pair the runner on the computer that will do the work, and keep it running: Recipe: the runner.
- Create a Slack app with the scopes in Recipe: Slack intake and approvals, install it, and invite it to the channel.
- Have an S3 or R2 bucket with an access key that can read and write it.
- Put every secret in an environment variable in the shell that runs Claude Code or Codex. The prompt never holds a secret; each one moves through a
prepare_secretdrop:
| Variable | What it holds |
|---|---|
WORK_FOLDER_SECRET_KEY | The S3 or R2 secret access key |
SLACK_BOT_TOKEN | The Slack app's bot token (xoxb-...) |
SLACK_SIGNING_SECRET | The Slack app's signing secret |
WOOPSOCIAL_API_KEY | The WoopSocial API key |
- Fill in the
{{...}}values in the prompt. They are ids, not secrets.
| Value | Where to find it |
|---|---|
{{WORK_FOLDER_ENDPOINT}}, {{WORK_FOLDER_BUCKET}}, {{WORK_FOLDER_ACCESS_KEY_ID}} | Your S3 or R2 settings (R2 endpoint: https://<account>.r2.cloudflarestorage.com) |
{{SLACK_CHANNEL_ID}} | In Slack: the channel name, then the channel id at the bottom of About (starts with C) |
{{SLACK_TEAM_ID}} | Your Slack workspace id (starts with T) |
{{WOOPSOCIAL_PROJECT_ID}} and the account ids | Your WoopSocial project and its connected social accounts |
{{APPROVER_SLACK_USER_ID}} | The approver's Slack profile, More, Copy member ID (starts with U) |
{{APPROVER_MEMBER_ID}} | The approver's BakedBrie user id. No MCP tool lists members, so if you do not have it, delete step 11 and link the approver in the app instead: Settings, Members, Slack approvals |
{{BRAND_COLORS}} | Your brand colors, for example cream #F6EBD9, brown #8A5A2B, red #C2413A |
Missing some of these? Run the prompt anyway without the steps you cannot fill (for example 3, 4, 5, 10 and 11 without the secrets and ids). The board, guidelines, agents and published rules work on their own, and the rest can be added later with the same tools. Never invent an id or a secret.
The prompt
Copy everything in the block into Claude Code or Codex.
Set up my social post workflow in BakedBrie. Use only the BakedBrie MCP tools. Start with whoami and stop if any capability this needs (control, agents, triggers, secrets, ai_accounts, destinations, reviews, inbound, api_workflow, runner) is off; tell me which one.
Secrets: for every key or token below, call prepare_secret with the environment variable I name, run the command it returns in my shell exactly as given, one command per shell call (it reads the value from my environment), then pass the drop_id. Never ask me to paste a secret and never print one. If a drop expires (SECRET_DROP_EXPIRED), call prepare_secret again.
I have already said yes to everything in this message (the board, the AI account binding, the destinations, the hooks, the agents, the rules and the Slack intake), so do not stop to ask me. If a call fails, read the error and its fix, correct what you sent and try again, then carry on.
What the workflow does: someone asks for a social post in Slack (a brief plus inspiration images) and a card lands on the board. A generator agent makes an image carousel following our brand and social guidelines. An AI check agent reviews the draft, writes the captions, and either passes it or sends it back to the generator with notes, at most 3 rounds, after which it goes to people anyway with the notes. The draft, images and captions are posted to Slack for approval. When a person approves, the post is scheduled on our social accounts through WoopSocial in the next open slot, and BakedBrie posts the live links and later the stats back in the same Slack thread.
1. Board: create a board named "Social posts" whose columns are, in order: Inbox, Generating, AI check, Scheduled. Scheduled is the done column. A new board starts with three columns (To do, Doing, Done): rename them with manage_stages and add AI check, so the board has exactly these four.
2. AI: call list_ai_accounts and find my runner account (kind runner). Bind it to this board with bind_ai_account (scope board). If there is no runner account yet, skip the binding, do every other step (published rules wait until an account is bound), and tell me at the end to pair bakedbrie-runner and bind it.
3. Work folder: create an S3 destination named "Social posts work folder" with endpoint {{WORK_FOLDER_ENDPOINT}}, bucket {{WORK_FOLDER_BUCKET}}, prefix social-posts/ and access key id {{WORK_FOLDER_ACCESS_KEY_ID}}; the secret access key is in WORK_FOLDER_SECRET_KEY (prepare_secret kind s3, purpose destination). Make it the board's work folder with set_work_folder, with any board member allowed to review.
4. Slack destination: create an API destination with the Slack preset named "Slack social posts" for channel {{SLACK_CHANNEL_ID}} (team {{SLACK_TEAM_ID}}); the bot token is in SLACK_BOT_TOKEN (prepare_secret kind slack, purpose destination). Then set the board hooks with manage_board_hooks so these board events go to that destination with their default actions: review_requested, review_decided, published and stats.
5. WoopSocial: create an API destination with the WoopSocial preset named "WoopSocial" for project {{WOOPSOCIAL_PROJECT_ID}}, posting to these targets: INSTAGRAM account {{INSTAGRAM_ACCOUNT_ID}}, LINKEDIN_PAGES account {{LINKEDIN_PAGE_ACCOUNT_ID}} and X account {{X_ACCOUNT_ID}}. Time zone America/Toronto, daily times 10:00 and 15:00, no weekends. The API key is in WOOPSOCIAL_API_KEY (prepare_secret kind woopsocial, purpose destination). Make it the board's default destination with manage_destination set_board_default, so approved cards are scheduled there.
6. Board guidelines: read them with manage_board_guidelines get, then set this text with the revision you read (every agent on the board gets it):
Brand. We sound warm, plain and confident. Short sentences, active voice, no jargon, no exclamation marks in headlines. Colors: {{BRAND_COLORS}}. Always show real work, never stock-photo people.
Social. Carousels have exactly 5 slides at 1080x1350. Slide 1 is the hook: one bold line of at most 8 words. Slides 2 to 4 carry one idea each in at most 25 words. Slide 5 is the call to action with the link in bio. Use the inspiration images attached to the card for mood and color, never copy them. Captions are at most 150 characters with exactly 3 hashtags; the X caption stays under 280 characters and LinkedIn may run longer with no hashtags. Never use em dashes, never promise results, never mention competitors.
7. Generator agent: create an agent draft named "Carousel generator" on the board with read_files and write_files allowed and everything else off (write_files lets the runner's Claude Code or Codex save the slide images), with these instructions, then publish it:
You make social image carousels from the card's brief and its inspiration images, following the board guidelines. Make one image per slide named slide-1.png to slide-5.png in your work folder, and write a short plan of the slides above them. When a card comes back with notes from the AI check or a reviewer, fix exactly what the notes ask and keep what worked.
8. Checker agent: create an agent draft named "AI check" on the board (default permissions) with these instructions, then publish it:
You check the carousel draft from the previous step against the card's brief and the board guidelines, and you write the captions. Pass it only when a person would be happy to approve it as is; otherwise fail it with short, specific notes the generator can act on. Write the captions in a fenced block tagged bakedbrie-captions containing JSON {"caption": "...", "hashtags": ["#one", "#two", "#three"], "platform_captions": {"X": "...", "LINKEDIN_PAGES": "..."}} following the board guidelines. Only when the brief or a person's note asks for a specific posting time, add "publish_at" with that local date and time, for example "2026-10-02T14:00", then give your verdict as instructed.
9. Rules: draft two rules with author_workflow_draft and turn each on with publish_workflow.
- "Generate": when a card is in Generating, the Carousel generator works on it, then the card moves to AI check. review none.
- "Check": when a card is in AI check, the AI check agent works on it, then the card moves to Scheduled. It is a check rule: fail_stage_id is the Generating column, max_rounds 3. review auto (people review the draft before it moves on).
10. Slack intake: create an inbound endpoint with manage_inbound_endpoint, preset slack, named "Slack social posts intake" on the board, so a top-level message in channel {{SLACK_CHANNEL_ID}} (team {{SLACK_TEAM_ID}}) becomes a card that starts in Generating. The signing secret is in SLACK_SIGNING_SECRET (prepare_secret kind inbound_signing, purpose connection). Set fetch_destination_id and reply_destination_id to the Slack social posts destination.
11. Approver: link Slack user {{APPROVER_SLACK_USER_ID}} (team {{SLACK_TEAM_ID}}) to the BakedBrie member {{APPROVER_MEMBER_ID}} with manage_member_mapping set, so their Approve or Request changes click in Slack counts as their own decision. Link nobody else.
When everything is done, read it back (read_board, list_triggers, manage_board_hooks list, manage_board_guidelines get, manage_inbound_endpoint list, manage_member_mapping list) and reply with a short summary: the board id, the columns in order, both rules with their columns and settings, the hooks, the destinations, the inbound endpoint with the two URLs to paste into the Slack app (Event Subscriptions: the url; Interactivity: the url followed by /actions), the approver link, and anything that failed. Do not create any cards.
After it runs, the person does these
- In the Slack app settings, paste the endpoint url under Event Subscriptions, Request URL, and subscribe to the bot event
message.channels(andmessage.groupsfor a private channel). Paste the url followed by/actionsunder Interactivity and Shortcuts, Request URL. - Invite the approver to the board in the BakedBrie app. Invitations are human only, and an approver who cannot open the board is refused in Slack (
NO_BOARD_ACCESS). - Post a test brief with an image in the channel.
What to expect
- The generator and the check run on the paired computer. If it is offline, cards wait until the runner comes back.
- A failed check moves the card back to Generating with the notes. After 3 failed rounds the draft goes to people anyway, with the notes shown.
- An unlinked Slack user who clicks Approve gets a private note (
NOT_MAPPED). The maker of a draft cannot approve it (MAKER_CANNOT_APPROVE). - Request changes with a note sends the card back to Generating, and the generator gets the note.
list_resultsshows each delivery's receipt and its followups: the publish status and the 24 hour and 7 day stats.
If something fails
CAPABILITY_OFF: a feature this recipe needs is off in the workspace. Callwhoamito see which, and stop.REVISION_CONFLICTon the guidelines: read them again withgetand set with the new revision.INVALID_INPUT: thefixnames the field. Correct it and call again.- Every code, with what to do next: Refusals.