PRFlow

July 24, 2026 · 6 min read

Send a Slack notification when a PR is ready for review

Nobody wants a Slack ping every time a colleague pushes to a draft. Here is a GitHub Action that fires only when a pull request actually becomes ready for review, plus the exact cases where filtering on one event isn't enough.

Review channels get muted for one reason: they fire too early. Someone opens a draft to run CI, pushes six times, and the channel lights up for every one of them. The fix is to notify on a single moment, when the PR leaves draft and is ready for a human, and stay quiet the rest of the time. This post builds that with a filtered GitHub Action, then walks through the cases where a one-event filter quietly does the wrong thing.

The 60-second version

  • The default GitHub Slack app's pulls feed notifies on draft PRs marked ready and on every other PR change, so drafts leak into the channel.
  • The clean trigger is the ready_for_review pull_request event, which fires only when a PR leaves draft.
  • Subscribe to opened and ready_for_review, then gate on draft == false so every ready PR pings exactly once. Full workflow below.
  • The gap: after changes requested, the PR never re-enters draft, so no ready_for_review fires for the re-review.
  • Each ping is a fresh, standalone message with no CI status and no thread — fine until you want the PR's whole life in one place.

Why the default integration pings on drafts

The official GitHub + Slack app subscribes a channel to a repo's pulls feed, which the app documents as covering PR changes and draft PRs marked ready. In practice that means a work-in-progress draft still generates channel activity, and there's no built-in "ready only" switch. If your team opens draft PRs early (a good habit for CI and visibility), the channel pays for it, and people mute it. Once it's muted, the one notification that mattered, "this is ready for you," goes unseen.

GitHub gives you a precise hook for exactly this moment. The pull_request webhook and workflow event has a ready_for_review action that fires when a pull request transitions out of draft. Filter on that and the noise problem largely goes away.

The DIY version: filter on ready_for_review

Store your Slack incoming webhook URL as a repository secret named SLACK_WEBHOOK_URL, then add .github/workflows/ready-for-review.yml:

name: Ready-for-review ping
on:
  pull_request:
    types: [opened, ready_for_review]

jobs:
  notify:
    # ready_for_review is always non-draft; this also filters out
    # PRs that are *opened* directly as drafts.
    if: github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    steps:
      - name: Post to Slack
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          PR_URL: ${{ github.event.pull_request.html_url }}
          PR_TITLE: ${{ github.event.pull_request.title }}
          PR_NUM: ${{ github.event.pull_request.number }}
        run: |
          payload=$(jq -n \
            --arg text "Ready for review: <${PR_URL}|#${PR_NUM} ${PR_TITLE}>" \
            '{text: $text}')
          curl -sS -X POST -H 'Content-type: application/json' \
            --data "$payload" "$SLACK_WEBHOOK_URL"

Two events, one guard. Subscribing to opened catches PRs that are created in the ready state, and ready_for_review catches the draft-to-ready transition; the draft == false job condition means an opened draft posts nothing. Using jq -n to build the JSON keeps the PR title safe even when it contains quotes or backticks, which a naive string interpolation into curl would break on.

Full disclosure: we make PRFlow, which treats "ready for review" as the start of a message, not a one-off ping. It posts a single Slack message per PR that then updates in place with CI/CD status and threaded comments, on GitHub and GitLab. The workflow below is a solid start; the next section is where it starts asking for more of your time.

Where filtering on one event breaks

The trigger is correct. The trouble is that "ready for review" is a moment, and a review is a loop.

Changes-requested doesn't re-notify. A reviewer asks for changes, the author pushes fixes and wants another look, but the PR was never a draft during that round, so no ready_for_review fires. The channel stays silent on the re-review, which is often the ping people most want. Covering it means also handling the review_requested action, and now you're maintaining two notification paths with different payloads.

Draft ping-pong double-fires. Convert to draft, convert back to ready, and ready_for_review fires again, so a PR that bounced states pings twice. Guarding against that needs state you don't have in a stateless workflow.

Every ping is a new, standalone message. A Slack incoming webhook can only create messages, not edit or thread them (Slack's docs note incoming webhooks do not allow you to delete a message). So the "ready" ping, any later re-review ping, the eventual merge, and the CI result are all separate posts scattered down the channel instead of one message that tells the PR's story.

No CI status. "Ready for review" doesn't mean "the pipeline passed." To show that, you correlate a separate check_suite or status event with this PR and edit the original message, which a webhook can't do. That's a bot token, chat.update, and stored message timestamps, in other words a small service.

Per-repo sprawl. This file lives in each repo. Ten repos means ten copies to keep in sync, and the same secret configured ten times. Org-wide behavior wants org-level tooling.

Build it or skip it?

  • Ship the workflow if a single "it's ready" ping per PR is all you need. It's a real improvement over the draft-noisy default and costs nothing.
  • Add review_requested if re-review pings matter, and accept that you now maintain two paths.
  • Use a maintained tool once you want one message per PR that carries the ready state, CI status, and review comments together instead of scattered posts.

The mirror image of this problem, reminding reviewers about PRs that went quiet, is covered in automatic Slack reminders for GitHub pull requests. If you're on GitLab, the terminology and the native options differ; start with merge request vs pull request and every way to connect GitLab and Slack.

Bottom line

Filtering on ready_for_review plus a draft == false guard is the correct fix for draft noise, and for many teams it's enough on its own. Just know its edges: it says nothing on a re-review, it can double-fire on draft ping-pong, and every notification is a separate message with no CI context. Ship the workflow, add review_requested if you need it, and move to a maintained tool when you want the whole PR in one updating message instead of a trail of pings.

Frequently asked questions

Notifying Slack only when a PR is ready, answered

How do I only send a Slack notification when a PR is ready for review?
Trigger a GitHub Action on the pull_request event with types [opened, ready_for_review], and gate the job with if github.event.pull_request.draft == false. The ready_for_review action fires when a PR leaves draft, and the draft check stops a PR that was opened directly as a draft from notifying. Post to a Slack incoming webhook from the job.
Does ready_for_review fire when a PR is opened as non-draft?
No. ready_for_review only fires on the transition from draft to ready. A PR opened directly in the ready state fires opened, not ready_for_review. That is why you subscribe to both opened and ready_for_review and filter out drafts with a draft == false condition, so every genuinely-ready PR notifies exactly once.
Why doesn't my ready-for-review workflow fire after changes are requested?
Because the PR was never a draft during that cycle. After a reviewer requests changes and the author pushes fixes, the PR stays in the ready state, so no ready_for_review event fires. To re-notify on a re-review, handle the review_requested action separately. This is the main gap in the event-filter approach.

Want the whole PR in one message, not a trail of pings?

PRFlow posts one Slack message per pull request when it's ready, then updates that same message with CI/CD status and review comments, on GitHub and GitLab. No draft noise, no scattered posts.

Try PRFlow Free

Or read about Slack reminders for pending PRs.