How to notify Slack when Claude Code fixes a reported bug
Claude Code doesn't post to Slack on its own. There's no setting for it. What gets you there is a hook: a command Claude Code runs at a lifecycle event like Stop, which fires once it finishes responding. Point that command at a Slack incoming webhook and you have a notification.
That part is straightforward, and a handful of open-source tools already do it. The part that's easy to miss is what the notification says. Out of the box, a hook fires with a session ID and the model's last message, nothing about which bug this was or which customer is still waiting on it. Getting the fix to the right person's attention takes more than wiring the hook, and that's the gap this guide covers.
What the Stop hook hands you
Hooks are configured in .claude/settings.json (or the user-level ~/.claude/settings.json), keyed by event name. Stop fires when Claude finishes responding in the main session; SubagentStop is the equivalent for a subagent. A command-type hook receives a JSON payload on stdin with fields like session_id, transcript_path, cwd, and last_assistant_message.
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/notify-slack.sh" }] }
]
}
}The script reads that JSON and posts it to a Slack incoming webhook, which expects nothing fancier than {"text": "..."}. A webhook URL is created per Slack app and is locked to the one channel you picked when you authorized it, so a second destination means a second webhook.
#!/bin/bash
input=$(cat)
message=$(echo "$input" | jq -r '.last_assistant_message')
curl -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\": \"Claude Code finished: ${message:0:200}\"}"This is the whole mechanism, and it's exactly what the community has already built. claude-notifications-go is an open-source Go plugin that listens for Stop, SubagentStop, and a few other events and relays them to Slack (plus Discord, Telegram, PagerDuty, and others) as color-coded messages: green for task complete, yellow for a question, orange for a session limit. A paid Mac notifier covers similar ground for desktop alerts. Both are relaying the same payload the hook already gives you.
What lands in the channel
Run the script above and the message that lands in the channel looks like this:
Claude Code: Task complete. Session
a3f9c1: "Fixed the export timeout by increasing the batch size and adding a retry on the S3 write."
That's accurate, and it's also not useful to anyone except the person who happened to start that session. It doesn't say which bug report this was, doesn't say who's waiting on it, and doesn't link back to wherever the request came from. If your team runs more than one Claude Code session at a time, which is most teams past a handful of engineers, a channel full of these reads like a build log, not a set of answers to specific people.
The thread that goes nowhere
Say a customer flags that exporting a quarter's worth of transactions to CSV times out past about 40,000 rows, cutting the file off mid-write with no error shown. The engineer who takes the report files it in Linear, tags it to the customer's account, and kicks off a Claude Code session against the export service repo with the bug report and a repro pasted in.
Forty minutes later, #eng-fixes gets the message from the last section: session ID, one line about a batch size and a retry. The engineer happens to be watching the channel and pieces together that this is probably the export fix, based on timing and the word "S3." A teammate asks in the thread: "is this the Meridian Corp bug or the other export issue from last week?" Nobody can answer from the message alone. Someone has to open the transcript to check.
What's missing isn't effort. The bug came in with an account attached, and a session ran that fixed it. What breaks is the step in between: the notification carries none of that context forward. The hook fires with what Claude Code knows about itself; it doesn't know what Linear issue this session was answering, or that Meridian Corp is the account still waiting.
When a session ID isn't enough
The hook-and-webhook approach holds up fine for a single engineer running one Claude Code session at a time against a small, known set of bugs. It stops working once any of these show up, and they show up fast:
- More than one session running. A shared channel full of "Claude Code finished" messages with only a session ID to tell them apart becomes noise within a day.
- More than one person waiting. A generic ping can't say "this is the fix Meridian Corp asked for" because the hook was never told that in the first place. Someone still has to connect the session back to the request by hand, which is the exact manual step notifications were supposed to remove.
- The requester needs to be told, not just the team. Closing the loop with a customer means the message needs their name and their original ask attached, not a session ID a teammate has to decode.
At that point the fix isn't a better hook script. It's connecting the bug report to the fix before the notification ever gets sent, which means the system needs to know about the customer and the request, not just the Claude Code session.
Full disclosure: Modem is our product, so read what follows as a vendor describing a fix for the gap just laid out, not a neutral verdict. Modem's Claude Code integration delegates work to Claude Code with task descriptions composed from the original bug report, the customer's quotes, and the topic data behind it, not a one-line summary. When that work ships, Modem's Slack integration posts to the channels you choose, and because the topic already carries the people and companies who raised it, the message says whose request this was rather than which session ran. The requester and the fix stay linked from the first report through the notification, which is the piece a Stop hook has no way to know about on its own.
If your bugs mostly arrive as GitHub issues rather than Slack threads, the same requester-linking problem shows up there too, covered in how to prioritize issues by customer impact with Claude Code. And if Slack notifications are one of several ways you're trying to get customer context into a Claude Code session in the first place, the fuller comparison is in the best tools to give Claude Code customer context.
What to actually build first
If you're one engineer with one channel, wire the Stop hook to a Slack webhook exactly as above and move on; it's five minutes of setup and it works for that case. The moment a second person or a second concurrent session enters the picture, add one thing to the script before anything fancier. Have it look up the Linear issue key from your branch name or commit message and put that in the Slack text instead of the session ID. That single change turns "session a3f9c1 finished" into "issue EXPORT-482 finished," which at least gives a teammate something to click on instead of a transcript to dig through.
