How to connect GitHub to Slack without drowning the channel
The only GitHub event that reliably needs a person is somebody being blocked. How to send review requests and CI failures where they will be read, and leave everything else in GitHub.
On this page
- What we'll cover
- The one event that matters
- What not to send
- Building it with CodeWords
- Six messages worth setting up
- Writing a message a reviewer will act on
- Direct message or channel
- CI failures, which need care
- Measuring whether it worked
- Making it survive
- Limits worth knowing about
- What to set up first
- Frequently asked questions
- Related reading
Most GitHub-to-Slack integrations fail the same way. They start by forwarding everything, the channel becomes unreadable within a week, somebody mutes it, and six months later a review request sits for three days because the notification that would have caught it is in a channel nobody opens.
The useful version starts from a narrower question: which GitHub events actually require a person to do something they would not otherwise do? There are about three, and everything else belongs in GitHub where it already is.
What we'll cover
- The one event that matters
- What not to send
- Building it with CodeWords
- Six messages worth setting up
- Writing a message a reviewer will act on
- Direct message or channel
- CI failures, which need care
- Measuring whether it worked
- Making it survive
- Limits worth knowing about
- What to set up first
- Frequently asked questions
The one event that matters
Somebody is blocked waiting on somebody else. That is the event, and in GitHub it usually looks like a review request sitting unanswered.
Review latency is the largest component of how long changes take to ship in most teams — larger than writing the code and larger than CI. It is also invisible: the author does not want to chase, and the reviewer has no idea they are the bottleneck because the request is in an email they archived.
A message that says "you are blocking this" is acted on. A message that says "pull request opened" is not, because it is not addressed to anybody in particular.
The same logic covers the other two: a change blocking a release, and a failure on the main branch. Both mean somebody specific needs to do something now.
Everything else — pushes, comments, labels, milestones, issue edits — is context available in GitHub whenever somebody wants it. Sending it to Slack does not make it more available; it makes the three messages that matter harder to find.
What not to send
Worth being specific, because the temptation is to send it all and filter later.
Every push. Nobody reads them, and they are the single largest source of volume.
Every pull request opened. The author knows. The reviewer will be told when they are requested. A channel listing every open pull request is a worse version of the pull request list.
Every comment. Discussion belongs where it has thread context. A comment relayed into Slack loses the code it was about.
Bot activity. Dependency bots, formatting bots, and automated pull requests generate constant events and essentially never need a person urgently.
Draft pull requests. A draft is explicitly not ready, and notifying on it trains people that notifications do not mean anything.
Failures on feature branches. They matter to the author, who is already looking, and to nobody else.
The test worth applying to each: if this message arrives and the recipient does nothing differently, it should not have been sent.
Building it with CodeWords
CodeWords connects to more than 3,000 integrations, and the connection is made once and reused.
- Open CodeWords and start a new automation.
- Describe what should happen in plain language to Cody, the automation builder: which repositories, which events, and who should hear about each.
- Authorize the connection to GitHub with an app or token scoped to the repositories involved, and to your Slack workspace.
- Describe the exceptions: a bot-authored pull request, a draft, a reviewer who has left, a repository archived since you set it up.
- Point it at a test channel and a test repository before anything reaches the team.
You describe the outcome; Cody builds it, connects it, and deploys it. The free plan covers light use, with Pro at $39 per month and Business at $100 per month as usage grows; details are on the pricing page.
Six messages worth setting up
Review requested, sent to the reviewer. The title, the size, the author, and a direct link. This is the one that changes behaviour.
Review still outstanding after a threshold. A single reminder, not a recurring nag, sent to the reviewer with how long it has been waiting.
Main branch build failed, to the team channel, naming who merged and what broke.
A pull request approved and mergeable, to the author, because the gap between approval and merge is another quiet delay.
Release published, to whichever channel cares — support, product, or the wider team — since this is the one GitHub event non-engineers want.
A weekly summary of what is waiting. Open pull requests by age, and who each is waiting on. One message a week that makes the queue visible does more than any per-event stream.
Writing a message a reviewer will act on
Lead with the ask. "Review needed" before the repository name, because the first words are what gets read in a notification preview.
Say how big it is. Lines changed and files touched, because a reviewer decides whether to do it now or later based on size, and they will open the link to find out otherwise.
Say what changed in words, one sentence, drawn from the pull request description. A title is often uninformative and the description is usually right there.
Link directly to the files view, not the repository. Every extra click is a chance to decide to come back later.
Keep it to a few lines. Something that fills the screen is a document, and in a notification it reads as something to deal with later.
Do not mention the channel. An @channel for a review request is how a channel gets muted, and once muted it stays muted.
Direct message or channel
Worth deciding deliberately, because it changes whether the message works.
Review requests go to the person. A direct message is addressed to somebody, and a channel message is addressed to nobody. This alone accounts for most of the difference in response time.
Main branch failures go to the channel, because anybody could pick it up and because it is information the team needs collectively.
Release announcements go to the wider channel, since the audience is people outside the repository.
Reminders go to the person, never to a channel — a public nag is a different message from a private one, and not a better one.
Ask people where they want their own notifications. Some prefer a personal channel over direct messages, and honouring that costs nothing.
CI failures, which need care
The category most likely to be over-sent, and worth its own rules.
Only the main branch, only the first failure. A repeatedly failing branch producing a message every push is the fastest route to a muted channel.
Name who merged, since that is the person most likely to be able to fix it.
Include the failing job and the relevant log lines, not a link to a log. The point of the message is to say what broke, and a link means opening a browser to find out whether it matters.
Suppress known flakes where you can identify them, or the failure notifications become noise that hides the real ones.
Say when it recovers. A channel that reports failures and not recoveries leaves everybody assuming it is still broken.
Measuring whether it worked
Worth doing, because this is one of the few integrations with an outcome you can actually check.
Time from review requested to first review is the number. Measure it before you build anything, so the comparison exists.
Report the distribution, not the average. The median request is answered quickly in most teams; the problem is the tail, and the tail is what the reminder is for.
Count reminders sent. If the reminder fires on most requests, the first message is not working — usually because it went to a channel rather than a person.
Watch for the channel going quiet. Falling engagement with messages is the early sign that volume crept up, which happens as soon as somebody adds one more event type.
Attribute to the team, not to individuals. Review latency describes a process, and pointed at people it produces reviews cleared without being read.
If the number has not moved after a month, the honest response is to remove the integration rather than add more events to it.
Making it survive
Make it idempotent. Key on the event so a retried webhook does not send twice, which is visible to everybody.
Resolve Slack user identifiers once and store them, mapping from GitHub usernames, and report when somebody cannot be mapped rather than silently dropping the message.
Handle the archived repository and the departed reviewer as cases rather than failures.
Respect Slack rate limits, which matter if a large release generates many messages at once.
Report the outcome. Messages sent, recipients that could not be resolved, and anything suppressed.
Limits worth knowing about
GitHub webhooks are at-least-once and can arrive out of order, so a closed event can precede the opened one.
Slack rate limits apply per method, and posting a burst of messages will be throttled.
User mapping is manual. GitHub usernames and Slack accounts are separate identities, and maintaining the map is ongoing work — automate it from your directory if you can.
Private repository content in a public channel is a real disclosure risk. A pull request title can say more than intended.
Message formatting is limited compared with a rendered page, so code and diffs need trimming rather than pasting.
What to set up first
Review requests delivered to the reviewer as a direct message, with size, a one-line summary, and a link straight to the diff. Nothing else on this list moves a number the team cares about as directly, and it takes an afternoon.
Two habits make the difference. Send it to the person rather than a channel, since that single choice accounts for most of the improvement. And add the single reminder after a threshold at the same time, because the first message handles attention and the reminder handles the ones that slipped anyway.
Frequently asked questions
Should every pull request be announced in a channel?
No. The author already knows and the reviewer will be told directly. A channel listing every open pull request duplicates a list GitHub already maintains and buries the messages that need somebody.
Direct message or channel for review requests?
Direct message. A channel message is addressed to nobody and gets treated accordingly. This is the single largest factor in whether the integration reduces review latency or just adds volume.
How do I map GitHub users to Slack users?
Maintain a mapping, ideally generated from your identity directory rather than by hand, and report failures to map rather than silently dropping messages. The two are separate identities and nothing links them automatically.
What about CI failures?
Main branch only, first failure only, with the failing job and the relevant log lines in the message rather than behind a link. Report recoveries too, or the channel gives the impression everything is permanently broken.
Will this replace GitHub's own Slack app?
For simple event forwarding, GitHub's app is fine and needs no building. Build something custom when you need routing by person, summaries, reminders after a threshold, or events joined with data from outside GitHub.
Is there a disclosure risk?
Yes. Pull request titles and branch names from private repositories appearing in a public channel can reveal more than intended. Check the channel's membership before pointing anything at it, particularly for security-related repositories.
What is the one message nobody builds and everybody wants?
The weekly summary of what is waiting and on whom. It makes the review queue visible without any per-event noise, and in most teams it is the first time anybody has seen the queue as a whole.
How do I know the integration is working?
Measure time from review requested to first review, before and after, as a distribution rather than an average. If the reminder is firing on most requests, the first message is not landing — usually because it went to a channel instead of a person.
What if the channel gets noisy again?
It will, as soon as somebody adds one more event type. Review what is being sent quarterly against whether anybody responded to it, and remove whatever fails that test. Volume creep is the normal failure of every notification integration.