# Changelog Source: https://docs.prflow.dev/changelog New features in PRFlow, week by week. πŸ’¬ **Conversation comments on GitHub pull requests now sync into the Slack thread.** Inline review comments already did; both kinds now show up as threaded replies. GitHub delivers conversation comments as issue comments, so the App needs one extra read-only permission, Issues. Existing installations get a one-time prompt for an organization owner to approve it; comments on plain issues are ignored. πŸ“ **Draft PRs can now post to Slack, per rule.** Every [routing rule](/channel-routing) has a Draft PRs toggle, off by default: turn it on and the card posts as soon as a draft opens instead of waiting for ready for review. ## πŸŽ‰ PRFlow is on the GitHub Marketplace PRFlow for Slack is now published on the [GitHub Marketplace](https://github.com/marketplace/prflow-for-slack), listed under Code review and Chat with a free Starter plan. You can install the App straight from the listing β€” or from your GitHub organization's Marketplace browse and search results β€” and then link it to your PRFlow workspace on the Connections page. Starting from inside PRFlow still works exactly as before. πŸ”΅πŸŸ πŸ”΄πŸŸ’πŸŸ£ **The notification is now a lifecycle card that updates in place.** A colored rail shows state at a glance: blue in review, amber running, red blocked, green ready, purple merged. The same message updates as the MR progresses, so a channel shows one current card per MR instead of a trail of stale posts. πŸ’¬ **GitHub comments now sync into the Slack thread.** Conversation comments and inline review comments on a pull request appear as threaded replies under the PR's Slack message, with edits updated in place and deletions removed. Comment sync now works the same on GitLab and GitHub. πŸ“¬ **Scheduled PR digests in Slack.** At your chosen times, PRFlow posts a digest of a channel's open PRs, classified into Needs attention, Waiting on review, and Ready to merge, with age and staleness highlights. Configure it right where you route repositories: one dialog covers the schedule, timezone, and PR filters for the destination channel. ## πŸŽ‰πŸŽ‰πŸŽ‰ GitHub support Install the PRFlow GitHub App, pick repositories, and map them to Slack channels just like GitLab projects. One updating Slack message per pull request, with CI status from GitHub Actions and other check providers. No webhook setup required. πŸ“‘ **See when GitLab last delivered a webhook.** The Connections page shows "Last event received N hours ago". πŸ’³ **Manage your subscription entirely inside PRFlow.** Billing settings were redesigned: upgrade in an embedded checkout overlay, update your payment method, cancel or resubscribe in one click, and see per-seat prices and seat usage in one place. βž• **Redesigned Add Repository dialog.** A searchable project list with multi-select lets you connect several projects to a channel in one step. 🩺 **GitLab token health at a glance.** Settings shows expiry and health of your GitLab credential. 🚧 **Merge blockers shown in Slack.** Notifications tell you why an MR can't merge: missing approvals, unresolved discussions, and other merge-status signals. 🏠 **Self-hosted GitLab shipped.** Cloud and self-hosted instances are now both supported. πŸ‘₯ **Teammates and organizations introduced.** Invite teammates and switch organizations from the account menu. πŸ”‘ **Connect GitLab with a service-account token.** Teams on GitLab Premium/Ultimate can register a PAT for webhook management. 🐣 **PRFlow is born.** The first release: GitLab merge requests notify your team in Slack, one message per MR that updates in place as the MR moves along. CI status shows on the message, comments sync into a Slack thread, and a guided setup connects GitLab, installs the Slack app, and maps each project to a channel. # Notification rules Source: https://docs.prflow.dev/channel-routing How PRFlow decides which Slack channel a repository posts to: an ordered list of rules where the first match wins, with specific repositories, wildcard patterns, and a catch-all. PRFlow decides where every notification goes with **notification rules**, managed from the dashboard at [app.prflow.dev](https://app.prflow.dev). A rule has a name, a destination Slack channel, one or more match conditions, and an on/off switch. When an event arrives, PRFlow walks your rules top to bottom and the first enabled rule with a matching condition wins: the repository's notifications post to that rule's channel. A repository that matches no rule posts nowhere. The Notification Rules list: four ordered rules with match conditions, destination channels, and on/off switches ## Creating a rule Connect your source provider (GitLab or GitHub) and Slack first, then click **+ New rule** on the dashboard. The rule editor: destination channel picked, rule name prefilled, one Specific repository condition open * **Destination channel** β€” where matched repositories post. Picking a channel fills in the rule name for you; a name you type yourself is never overwritten. Each channel belongs to at most one rule, so channels already claimed show which rule routes them. PRFlow lists public channels plus private channels it has been invited to β€” if a private channel is missing, run `/invite @PRFlow` in that channel on Slack, then reopen the editor. * **Match conditions** β€” a **Specific repository** condition is already open when the editor appears, and if PRFlow only knows about one repository it is pre-selected too, so picking a channel is the only decision left. Add more conditions with **+ Add condition**; a repository routes to the rule if it matches **any** of its conditions. * **Notifications** β€” the **Draft PRs** switch, off by default, makes the rule post drafts too: the card appears when a draft opens instead of when it is marked ready for review. Rules with it on show a **Drafts** chip in the rules list. The same editor holds the channel's [scheduled digest](/pr-digest) settings, and this is also where you delete a rule. ## The three match types * **Specific repository** β€” pick one or more repositories from the list. Matching is by the repository's exact full name. * **Repository pattern** β€” a wildcard expression. `*` is the only wildcard and matches any run of characters, including `/`. The pattern must match the whole name; there is no substring matching (`web-app` alone matches nothing β€” write `*/web-app`). * **All repositories** β€” matches every repository. This is the catch-all you use for a fallback rule. ### Patterns match different things on GitHub and GitLab On **GitHub**, a pattern matches the **bare repository name**, without the owner: write `payments-*` or `*-service`. A GitHub connection covers a single owner, so the owner prefix carries no routing information β€” an owner-prefixed pattern like `your-org/*` matches nothing on GitHub. To route every repository, use an **All repositories** condition instead. On **GitLab**, a pattern matches the **full path including group and subgroups**: `acme/frontend/*` routes every project under the `frontend` subgroup, and `acme/*/web-app` matches a project named `web-app` in any subgroup. ## Rule order: first match wins Rules are checked top to bottom and the first match wins, so order is part of the configuration. Reorder by dragging a rule up or down by the grip handle on the left of its row (keyboard: focus the grip, Space to lift, arrow keys to move, Space to drop, Escape to cancel). * A **Specific repository** condition has no built-in priority: a pattern rule higher in the list takes a repository even if a rule below names it explicitly. To make a specific-repository rule win, drag it above the pattern. * Keep the catch-all rule **last** β€” anything above it can never lose to it. Nothing enforces this, so the list warns with *"Shadows every rule below"* when an enabled catch-all sits above other enabled rules. * A rule switched off is skipped entirely; the rules below it take over. ## New projects register themselves With a GitLab **group webhook**, GitLab sends events for every project in your group β€” including projects created after you set PRFlow up. PRFlow registers a project from your configured group the first time it sends an event and applies your rules to it immediately, so a pattern or catch-all rule picks up new projects with no extra clicks. Projects outside your group are ignored, and no rules are created or changed automatically. On **GitHub**, PRFlow mirrors the repositories you granted to the PRFlow App and keeps the list in sync when you add or remove repositories on the installation. ## Removed, renamed, and re-added repositories Rules identify a repository by its full name, not by an internal record, so a specific-repository rule survives the repository leaving and coming back β€” for example when a repo is removed from your GitHub installation, or when you disconnect and reconnect a provider. The rule simply starts matching again the moment the repository reappears. One caveat: a repository that comes back under a **new name** is a different name, so the rule keeps looking correct but no longer matches. Edit the rule and pick the repository again. (Names are matched exactly, so a capitalization change counts as a rename too.) # Comment sync Source: https://docs.prflow.dev/comment-sync Review comments on GitLab merge requests and GitHub pull requests appear as threaded replies under the Slack notification β€” new comments post to the thread, edits update in place, and deletions remove the reply. PRFlow keeps one Slack message per merge request and updates it in place as the MR progresses. **Comment sync** does the same for the review conversation: every comment on the MR becomes a threaded reply under that message, so the Slack thread mirrors the discussion as it happens. Slack thread showing a PRFlow merge request message with a review comment synced as a threaded reply, quoting the comment text and attributing it to the commenter's username Reviewers keep commenting where they always have β€” in GitLab or GitHub β€” and teammates who live in Slack follow the whole conversation from the thread. The channel itself stays clean: comments only ever go to the thread, never posted as separate channel messages. ## How it works Each synced comment is a threaded reply that preserves the comment text (up to 2,000 characters) and attributes it to the commenter's GitLab or GitHub username. The thread stays in step with the source: | When someone… | …the Slack thread | | ----------------- | ---------------------------------------- | | posts a comment | gets a new reply under the MR message | | edits a comment | shows the new text on the existing reply | | deletes a comment | has that reply removed | Sync is one-way, from GitLab/GitHub into Slack. PRFlow has read-only access to your code platform, so replying in the Slack thread does not post anything back to the merge request. ## What gets synced **GitLab** β€” comments on merge requests, both general comments and inline diff comments. Notes on issues, commits, and snippets are ignored. Comments arrive via the `Note Hook` webhook, and PRFlow's polling fallback catches comments a missed webhook delivery would have dropped. **GitHub** β€” pull request comments from the Conversation tab, inline review comments on the diff, and the summary body submitted with a review. Comments on plain issues are ignored. Approved and changes-requested summaries include that state in text in the reply, while the outcome also updates the PR message. Editing a review summary updates its reply; GitHub sends no event when a review is deleted, so the delete row above applies to comments, not summaries. ## Good to know * **Each routing rule controls new replies.** In the rule editor, choose **All comments** to post every comment, **Hide bot comments** to skip bots PRFlow recognizes and marks Hidden, or **No comments** to create no new replies. New rules start on Hide bot comments. * **Changing modes is prospective.** A skipped comment is not backfilled if you later choose a more permissive mode. Replies that were already posted still receive edits and deletions, regardless of the rule's current mode. * **The pull or merge request card stays authoritative.** Skipping a reply does not stop the card from tracking review state, CI, or unresolved-thread counts from GitHub or GitLab. * **Comments need a parent message.** If an MR has no Slack message β€” for example, the project was connected to PRFlow after the MR was opened, or the comment was made while the MR was still a draft β€” the comment is not posted, and it is not posted retroactively. * **Exactly one reply per source object.** Duplicate or concurrently delivered webhook events never produce a second reply. A GitHub review summary and its inline comments are distinct source objects, so each appears as its own reply. * See [data handling](/gitlab-integration#data-handling) for how PRFlow retains notification data while delivering and synchronizing updates. ## Setup Comment sync is available for every project you [map to a Slack channel](/channel-routing), on every plan. Open a routing rule's Notifications section to choose its comment mode. New to PRFlow? Start with the [quickstart](/quickstart). Webhook events, API calls, data handling, and security details. The one-click GitHub App: events, permissions, and setup. Questions? Reach us at [hello@prflow.dev](mailto:hello@prflow.dev). # FAQ Source: https://docs.prflow.dev/faq Answers to common PRFlow questions β€” setup and permissions, notifications that didn't arrive, Slack channels and threads, and disconnecting or uninstalling. Short answers to the questions we get most, grouped by topic. Each answer links to the page with the full detail. Can't find yours? Email [hello@prflow.dev](mailto:hello@prflow.dev). ## Setup & permissions ### Why does PRFlow only ask for read-only access? Everything PRFlow does β€” posting the Slack card, showing CI status, syncing comments β€” only requires reading merge request, comment, and pipeline metadata, so that is all the access it requests. On GitLab that is the single [`read_api` scope](/gitlab-integration); on GitHub the App requests four [read-only permissions](/github-integration#permissions-&-data) (Pull requests, Checks, Issues, Metadata). PRFlow cannot modify anything on your code platform, and it never reads repository code or diffs. ### GitHub is asking me to approve a new "Issues" permission for PRFlow. Why? The PRFlow GitHub App added the read-only **Issues** permission in August 2026 to sync comments from a pull request's Conversation tab: GitHub delivers those as issue comments, and an App can only receive them with Issues access β€” there is no PR-scoped alternative. It is still read-only, and PRFlow still has no access to repository code. Comments on plain issues are received and immediately discarded. Your notifications keep working while the request is pending; once an organization owner approves it, conversation comments start appearing in the PR's Slack thread. Details in [Permissions & data](/github-integration#permissions-&-data). ### Can I connect both GitLab and GitHub to the same workspace? No β€” a PRFlow workspace connects one source provider, GitHub or GitLab, plus one Slack workspace. If your team uses both platforms, create a separate PRFlow organization for each; the organization switcher in the navbar moves you between them, and more than one organization can post into the same Slack workspace. ### I added PRFlow to Slack first, from an "Add to Slack" link. Did I do it in the wrong order? No harm done β€” no partial or dangling Slack connection is created. PRFlow works source-first: you connect a GitLab or GitHub repository, then add Slack from within PRFlow, so a Slack-first install lands on a guided sign-in page that explains exactly that. Create an account or sign in and continue from the [quickstart](/quickstart). ### I installed the GitHub App from the GitHub Marketplace first. What now? That path is fully supported: after installing, GitHub sends you to PRFlow to link the installation to your workspace. Sign in (or sign up) and the Connections page shows a "Finish connecting GitHub" prompt with GitHub pre-selected β€” press **Connect GitHub** and you're linked. The end result is identical to starting from inside PRFlow. ### Does PRFlow work with self-managed GitLab or GitHub Enterprise? Self-managed GitLab works: any instance reachable over HTTPS, connected with a `read_api` personal access token (gitlab.com additionally supports OAuth). GitHub Enterprise Server is not supported β€” the [GitHub App](/github-integration) works with github.com organizations and personal accounts. ## When a notification doesn't arrive ### A merge request didn't show up in Slack. What should I check first? Check whether events are reaching PRFlow at all: the webhook panel on **Settings β†’ Connections** shows **"Last event received N hours ago"** once deliveries flow, or "No events received yet" before the first one β€” on GitLab, press **Test** on the webhook and refresh the page to watch the timestamp update. If events are arriving, check your [notification rules](/channel-routing): rules are evaluated top to bottom and the first match wins, so make sure an enabled rule matches the repository β€” and check which rule is actually catching it. ### Why is there no message for my draft merge request? By default, drafts stay silent: the card appears when the MR is marked ready for review (on GitHub, the "Ready for review" action). To get drafts in the channel, turn on the **Draft PRs** switch in the matching [notification rule](/channel-routing). ### Why weren't MRs that were already open announced when I connected the project? PRFlow only announces activity it sees after a project is connected β€” merge requests opened earlier are not posted retroactively. If you want a card for an MR that predates the connection, closing and reopening it posts one: a reopen event creates the message just like an open does. ### A comment never appeared in the Slack thread. Why? Comments need a parent message: if the MR has no Slack card β€” the project was connected after the MR was opened, or the comment was made while the MR was still a draft β€” the comment is not posted, and not retroactively either. Also check it's something PRFlow syncs: GitLab notes on issues, commits, and snippets are ignored, as are GitHub comments on plain issues. The full rules are on the [comment sync](/comment-sync) page. ### GitLab shows webhook deliveries failing with a 404. What's wrong? A 404 means the delivery URL doesn't match your workspace's unique webhook endpoint β€” usually a typo from editing the URL by hand. PRFlow rejects unrecognized webhook paths with 404 precisely so the mistake shows up in GitLab's delivery log instead of looking like a success. Copy the URL again from the webhook panel on **Settings β†’ Connections** and update the webhook in GitLab. ## Slack channels & threads ### Why doesn't a private channel show up in the channel picker? PRFlow can only see private channels it has been invited to. Run `/invite @PRFlow` in the channel, then refresh the page β€” the channel appears in the picker like any other. Public channels never need an invite; the `chat:write.public` scope covers them. More in the [Slack integration](/slack-integration) reference. ### If I reply in the Slack thread, does my reply appear on the MR? No β€” comment sync is one-way, from GitLab or GitHub into Slack. PRFlow has read-only access to your code platform, so nothing written in Slack is posted back. Reply on the merge request itself and your comment will be synced into the thread for everyone following along in Slack. ## Disconnecting & uninstalling ### What happens if I uninstall PRFlow from my Slack workspace? PRFlow is notified immediately and disconnects on its side: the stored access token is deleted and posting stops. If the Slack workspace was connected to more than one PRFlow organization, every one of them is disconnected β€” no organization is left showing "Connected" with a dead token. ### What happens if I disconnect Slack from inside PRFlow? **Disconnect** on the Slack card (admin-only, with a confirmation step) removes the connection and also revokes the bot token at Slack immediately, so it doesn't linger β€” unless another of your PRFlow organizations shares the same Slack workspace, in which case revocation is safely skipped and only your connection is removed. Your repository and channel-mapping setup is kept, and notifications resume when you reconnect. ### What happens if I uninstall the PRFlow GitHub App on GitHub? The App reports its own lifecycle events, so uninstalling it from GitHub's settings cleanly disconnects the integration in PRFlow automatically β€” there is nothing to clean up on the PRFlow side. Changing the App's repository selection works the same way: PRFlow picks up added and removed repositories on its own. ### What happens if I disconnect GitHub from inside PRFlow? Disconnect on the Connections page removes the integration and every repository tracked under it. It does not uninstall the App on GitHub's side β€” if you no longer want it installed, remove it from GitHub's App settings too. ### What happens if I disconnect GitLab from inside PRFlow? PRFlow deletes its stored copy of your credential (OAuth tokens or personal access token), releases the claim on your top-level group, and removes its record of your projects. Because PRFlow has no write access to GitLab, two things are left for you: the webhook you created stays configured in GitLab until you remove it there, and a personal access token remains valid until you revoke it in GitLab. ### What happens when my GitLab token expires or is revoked? PRFlow validates a personal access token when you save it (scope, expiry, and revocation status) and refreshes OAuth tokens automatically before they expire. If GitLab starts rejecting the credential anyway β€” a revoked or expired token answers 401/403 β€” the connection is flagged as unhealthy on your dashboard so you can rotate it. Re-entering a new token works without disconnecting first, even if you also spell out `https://gitlab.com` as the instance URL. ## Still stuck? Email [hello@prflow.dev](mailto:hello@prflow.dev) β€” include a link to the MR/PR and the channel you expected the notification in, and we'll get you sorted. # GitHub integration Source: https://docs.prflow.dev/github-integration How PRFlow connects to GitHub through a one-click GitHub App β€” available on the GitHub Marketplace β€” which pull request events it reacts to, and what it sends to Slack. ## Overview PRFlow delivers GitHub pull request notifications to Slack. For each pull request it posts **one Slack message that updates in place** as the PR progresses β€” CI check status, reviews, and merge state all appear on the original message, and PR comments are synced into a Slack thread under it. Unlike the [GitLab integration](/gitlab-integration), there is nothing to configure on the GitHub side beyond installing the **PRFlow GitHub App**: the App delivers webhook events automatically and grants PRFlow its (read-only) API access in one step. PRFlow works with **github.com** organizations and personal accounts; GitHub Enterprise Server is not supported. The App is published on the GitHub Marketplace as [**PRFlow for Slack**](https://github.com/marketplace/prflow-for-slack), listed under Code review and Chat with a free Starter plan. ## Setup PRFlow Connections settings page with GitHub selected as the source provider and an Install GitHub App button 1. In PRFlow, open **Settings β†’ Connections** and select **GitHub** as the source provider. A workspace connects one source provider β€” GitHub or GitLab, not both at once. 2. Click **Install GitHub App**. GitHub asks you to pick the account or organization and which repositories the App may see (all, or a selected list), and shows the read-only permissions it requests β€” the full list, and why each one is needed, is in [Permissions & data](#permissions-&-data) below. 3. Back in PRFlow, connect Slack if you haven't yet, then map repositories to Slack channels from the dashboard. That's the whole setup β€” no webhook URLs, no secrets to paste. You can change the repository selection any time from GitHub's App settings; PRFlow picks the change up automatically. You can also start from the [GitHub Marketplace listing](https://github.com/marketplace/prflow-for-slack) and install the App there first. GitHub then sends you to PRFlow to link the installation to your workspace β€” sign in (or sign up) and press **Connect GitHub** on the Connections page to finish. The end result is identical to starting from inside PRFlow. ## How it works ### Events PRFlow reacts to The GitHub App subscribes to pull request activity. Events for repositories you haven't mapped to a Slack channel are acknowledged and discarded. | Event | What PRFlow does | | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `pull_request` | `opened`, `reopened`, and `ready_for_review` post the PR message; `edited`, `synchronize`, `review_requested`, and `converted_to_draft` update it in place; `closed` marks the PR merged or closed. Other actions (labeled, assigned, …) are ignored. Draft PRs are skipped until `ready_for_review`, unless the rule's [**Draft PRs** switch](/channel-routing) is on. | | `pull_request_review` | A submitted review updates the message's review state β€” **approved** or **changes requested** β€” and posts a non-empty review summary as a threaded reply. Edited summaries update an existing reply. | | `issue_comment`, `pull_request_review_comment` | Conversation comments on a PR and inline review comments on the diff are posted as threaded replies under the PR message. Edited comments are updated in place; deleted comments are removed from the Slack thread. Comments on plain issues are ignored. | | `check_suite` / `check_run` | Update the CI status shown on the PR message, aggregated across check suites β€” GitHub Actions and any other check provider. Only the newest run of each check counts, so a re-run that passes flips the card to βœ… even if an earlier run failed. | | `installation`, `installation_repositories` | Track the App's lifecycle: installing, uninstalling, and changing the repository selection. Uninstalling the App from GitHub cleanly disconnects the integration. | ### Slack message model The Slack side is identical to the GitLab integration: a 1:1 mapping between a pull request and a Slack message, updated in place via `chat.update`, with merge-requirement rows that flip from πŸ”΄ to 🟒 as CI, approvals, and mergeability are satisfied. On GitHub the merge requirements are read from the repository's **branch rulesets**; repositories using classic branch protection (which the App's read-only permissions cannot inspect) get a conservative fallback that never overstates what is required. **Comment sync**: each PR comment becomes a threaded reply under the PR message, quoting the comment text (truncated to 2,000 characters) with the commenter's username. Comment edits on GitHub update the existing thread reply; comment deletions remove it. PRFlow retains the comment text along with the comment ID and the Slack message reference β€” see [data handling](/gitlab-integration#data-handling), which applies equally to GitHub. ## Permissions & data The App requests four **read-only** permissions: | Permission | Why PRFlow needs it | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pull requests: read | PR metadata for notifications, and review state when rendering the message. | | Checks: read | CI status from check suites and check runs. | | Issues: read | Conversation (non-code) comments on a pull request β€” GitHub delivers these as issue comments, and reading them requires this permission. Comments on plain issues are received and immediately discarded. | | Metadata: read | Repository names for the repository picker, and branch rulesets for the merge-requirement rows. | The Issues permission was added in August 2026 to carry conversation comments β€” there is no PR-only way to receive them from GitHub. If you installed the App before then, GitHub shows organization owners a one-time request to approve the new permission. Everything else keeps working while the request is pending, and conversation comments start syncing the moment it is accepted. PRFlow has no write access and no Contents permission, so it cannot read repository code or diffs, and it cannot modify anything on GitHub. Every webhook delivery is signature-verified (`X-Hub-Signature-256`) before it is processed. As with GitLab, only pull request, review, comment, and check metadata is handled β€” see the GitLab page's [data handling section](/gitlab-integration#data-handling) for the storage and Slack-delivery details, which apply equally here. ## Disconnecting Either side works: **Disconnect** on the PRFlow Connections page removes the integration and every repository tracked under it (it does not uninstall the App on GitHub's side), or uninstall the App from GitHub's settings and PRFlow disconnects automatically. ## Support Questions about the integration or a notification that didn't arrive? Email [hello@prflow.dev](mailto:hello@prflow.dev) and we'll get you sorted. # GitLab integration Source: https://docs.prflow.dev/gitlab-integration How PRFlow connects to GitLab, which webhook events and API calls it uses, what it sends to Slack, and exactly what merge request data it handles along the way. ## Overview PRFlow delivers GitLab merge request notifications to Slack. For each merge request it posts **one Slack message that updates in place** as the MR progresses β€” CI/CD pipeline status, approvals, and merge state all appear on the original message, and GitLab review comments are synced into a Slack thread under it. The integration has three parts: 1. **GitLab webhooks** β€” your GitLab group or projects send merge request, comment, and pipeline events to a per-workspace PRFlow endpoint in real time. 2. **Read-only GitLab API access** β€” PRFlow uses a credential with the `read_api` scope to list your projects during setup, to look up approval and discussion state when rendering notifications, and to run a polling fallback that catches anything a missed webhook would have dropped. 3. **Slack Web API** β€” PRFlow posts and updates messages in the channels you map, using a Slack bot token obtained via OAuth. ```text theme={null} GitLab ── webhooks (MR / note / pipeline events) ──▢ PRFlow ── chat.postMessage / ◀── read-only API calls (read_api) β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ chat.update ──▢ Slack ``` PRFlow works with **gitlab.com** (OAuth or personal access token) and **self-managed GitLab** instances reachable over HTTPS (personal access token). It never requests write access to GitLab and never reads repository code or diffs β€” only merge request, comment, and pipeline metadata. ## How it works ### Webhook events consumed PRFlow exposes a unique webhook endpoint per workspace (`https://app.prflow.dev/webhooks/gitlab/`). It processes three GitLab event types, identified by the `X-Gitlab-Event` header. Any other event type is acknowledged with HTTP 200 and discarded, so you can safely enable extra event toggles in GitLab without side effects. | GitLab event | What PRFlow does | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Merge Request Hook` | `open` / `reopen` post the MR message; `update`, `close`, and `merge` update it in place; `approval` / `approved` and `unapproval` / `unapproved` trigger an approval-state refresh (verified against the GitLab approvals API before the message shows "approved"). Draft MRs are skipped until they leave draft state, unless the rule's [**Draft PRs** switch](/channel-routing) is on. | | `Note Hook` | Comments on merge requests (`noteable_type: MergeRequest`) are posted as threaded replies under the MR message. Edited comments are updated in place; deleted comments are removed from the Slack thread. Notes on issues, commits, or snippets are ignored. | | `Pipeline Hook` | Updates the CI status shown on the MR message. Recognized statuses: `success`, `failed`, `running`, `pending`, `canceled`, `skipped`, `manual`. Pipeline updates stop once an MR is merged or closed. | Events for projects you have not added in PRFlow are acknowledged and ignored. This makes a single group-level webhook safe: GitLab can fan out events for every project in the group, and PRFlow only acts on the ones you mapped to a Slack channel. ### GitLab API calls All data calls are read-only `GET` requests under the `read_api` scope. The only `POST` PRFlow sends to GitLab is the standard OAuth token exchange and refresh on `/oauth/token`. PRFlow never modifies anything in your GitLab instance. | Endpoint | Purpose | | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `GET /user` | Identify the connected account during OAuth and token validation. | | `GET /groups?top_level_only=true` | Resolve the top-level GitLab group your workspace is scoped to during setup. | | `GET /groups/:id/projects` | List projects (including subgroups) when you add repositories. Only projects where the credential has at least Developer access are listed. | | `GET /projects/:id`, `GET /groups/:id` | Fetch project and group metadata (name, path, avatar) for the dashboard. | | `GET /projects/:id/merge_requests/:iid/approvals` | Check approvals remaining, so the message can show an accurate "needs approvals" indicator and only flips to "approved" when approval rules are actually satisfied. | | `GET /projects/:id/merge_requests/:iid/discussions` | Count unresolved discussions for the "has unresolved discussions" indicator. | | `GET /projects/:id/merge_requests`, `GET .../merge_requests/:iid/notes` | Background polling fallback: PRFlow periodically diffs merge request state via the API and emits the same message updates if a webhook delivery was missed. | | `GET /personal_access_tokens/self` | Validate a personal access token when you save one: scope check, expiry, and revocation status. | | `POST /oauth/token` | OAuth authorization-code exchange and automatic refresh-token rotation (gitlab.com OAuth connections). | ### Slack message model PRFlow keeps a **1:1 mapping between a merge request and a Slack message**. The first qualifying event posts the message to the channel you mapped; every later event updates that same message (Slack `chat.update`) instead of posting a new one. If a project is mapped to multiple channels, each channel gets at most one message per MR, and updates stay in the channel where the message was originally posted. The message shows: * a status emoji for the MR state (new / approved πŸ‘ / merged βœ… / closed ❌), * the MR number and title, linked to the MR in GitLab, * author display name and username, target branch, and project name, * inline CI status when pipeline events arrive (βœ… passed, ❌ failed, πŸ”„ running, 🚫 canceled, ⏭️ skipped, πŸ‘† manual), * actionable blockers from GitLab's `detailed_merge_status` β€” e.g. "Needs approvals", "Has unresolved discussions", "CI failed", "Needs rebase", "Has merge conflicts". **Comment sync**: each GitLab comment on the MR becomes a threaded reply under the MR message, quoting the comment text (truncated to 2,000 characters) with the commenter's username. Comment edits in GitLab update the existing thread reply; comment deletions remove it. If an MR has no parent message in Slack (for example, the project was connected after the MR was opened), comments are not posted. **Ordering and duplicates**: PRFlow timestamps each event and rejects stale, out-of-order updates at the database layer before touching Slack, and uses atomic claim records so concurrent webhook deliveries never produce duplicate messages. MRs created before a project was connected to PRFlow are not announced retroactively. ## Setup guide Setup takes a few minutes in the PRFlow web app at [app.prflow.dev](https://app.prflow.dev). You connect GitLab, point a webhook at PRFlow, connect Slack, and map projects to channels. Sign in at [app.prflow.dev](https://app.prflow.dev) and create or select a workspace. Each PRFlow workspace connects to one GitLab organization (top-level group) and one Slack workspace. From **Home** or **Settings β†’ Connections**, choose your instance type: * **gitlab.com** β€” connect via OAuth (PRFlow requests only the `read_api` scope; tokens auto-refresh), or save a personal access token instead. * **Self-managed GitLab** β€” enter your instance root URL (HTTPS required; PRFlow appends `/api/v4` automatically) and save a personal access token. OAuth is not available for self-managed instances. Personal access tokens must have the `read_api` scope and access to the GitLab group you want PRFlow to use. PRFlow validates the token on save β€” scope, expiry, and revocation β€” and recommends a dedicated service-account token over a personal one to reduce operational risk. Token health (expiring, expired, revoked) is surfaced on the dashboard afterwards. During connection, PRFlow resolves the **top-level GitLab group** your credential can access and binds the workspace to it. One GitLab organization maps to one PRFlow workspace β€” connecting the same GitLab group to a second workspace is rejected. PRFlow shows your workspace's unique webhook URL under **Settings β†’ Connections β†’ GitLab Webhook**. In GitLab, add it as either: * **Group webhook (recommended)** β€” add the URL once on your top-level group (**Group β†’ Settings β†’ Webhooks**). Covers all projects in the group. Requires the Owner role and GitLab Premium or Ultimate. * **Per-project webhooks** β€” add the same URL to each project (**Project β†’ Settings β†’ Webhooks**). Works on every GitLab tier, including Free. Requires Maintainer or Owner on each project. Enable the **Merge request events**, **Comment events**, and **Pipeline events** triggers, and keep **SSL verification** on. Optionally set a **Secret token** in GitLab and save the same value in PRFlow β€” PRFlow then verifies the `X-Gitlab-Token` header on every delivery. Authorize PRFlow's Slack app via OAuth. PRFlow requests four bot scopes: `chat:write`, `chat:write.public`, `channels:read`, and `groups:read` β€” enough to list channels and post/update messages, nothing more. Click **+ New rule** on the dashboard, pick the destination Slack channel (PRFlow fills in the rule name for you), and choose what routes there: a specific project, a wildcard pattern on the full GitLab path such as `acme/frontend/*`, or every repository as a catch-all. Rules are checked top to bottom and the first match wins β€” see [Notification rules](/channel-routing) for the details. From then on, every merge request in the matched projects posts itself to the rule's channel (drafts wait until they're marked ready unless the rule's **Draft PRs** switch is on). **GitLab compatibility:** gitlab.com on any tier (group webhooks need Premium or Ultimate; per-project webhooks work on Free), and self-managed GitLab instances reachable over HTTPS. Self-managed instances behind a firewall need outbound HTTPS to `app.prflow.dev` for webhooks; PRFlow's API polling additionally requires the instance to be reachable from the internet. ## Data handling When you connect PRFlow, **merge request metadata leaves your GitLab instance**: GitLab transmits it to PRFlow via webhooks and API responses, and PRFlow forwards a subset of it into the Slack workspace you connect. This section spells out exactly what moves and what is stored. See the [privacy policy](https://prflow.dev/privacy.html) for the full legal terms. ### Data received from GitLab From the three webhook event types and the API calls listed above, PRFlow receives: * Project metadata: ID, name, path, web URL. * Merge request metadata: IID, title, description, state, action, source/target branch, author, draft flag, detailed merge status, timestamps, web URL. * Comment (note) data: note ID, comment text, author username, action, URL. * Pipeline data: pipeline ID, status, ref, duration. * Approval and discussion summaries: approvals remaining, unresolved discussion count. * Connected-account info: GitLab user ID, username, display name, avatar URL; group names and paths. PRFlow never requests repository file contents, diffs, or commit contents. The `read_api` credential is used exclusively for the endpoints documented above. ### Data sent to Slack Notifications in your Slack channels contain: * MR number, title, and a link to the MR in GitLab, * author display name and username, target branch, project name, * CI pipeline status, approval/discussion indicators, and merge-status labels, * comment text excerpts (up to 2,000 characters) and commenter usernames in thread replies. ### What PRFlow stores * Connection records: your workspace, the connected GitLab account ID/username, the bound top-level group, and the instance URL for self-managed GitLab. * Credentials: OAuth access/refresh tokens, personal access tokens, and the optional webhook secret β€” all encrypted at rest with AES-256-GCM. * Repository and channel-mapping configuration for the projects you add. * Per-MR metadata needed to keep the Slack message current: title, state, branches, web URL, author username and display name, pipeline status, detailed merge status, approval/discussion counts, and timestamps. * Slack message references (channel ID, message timestamp) for each MR message and each synced comment. * Comment text, comment URL, and author username for each synced comment, alongside the note ID and Slack message reference. **Why comment text is retained.** PRFlow delivers Slack messages through a durable queue: a reply is recorded first and posted afterwards, then retried if Slack is unavailable, so the text has to survive between accepting the comment and delivering it. Keeping it also lets an edit in GitLab or GitHub update the existing reply, and a deletion remove it, without re-fetching from the source. Only comments PRFlow syncs to Slack are stored this way. ## Permissions & security ### GitLab scopes | Scope | Why PRFlow needs it | | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `read_api` | The single scope PRFlow requests β€” for OAuth and for personal access tokens alike. It covers listing groups and projects during setup, reading MR approval and discussion state, and the polling fallback. No write scope is ever requested; PRFlow cannot modify code, MRs, settings, or anything else in GitLab. | ### Slack scopes | Scope | Why PRFlow needs it | | ------------------- | ---------------------------------------------------------------------------------- | | `chat:write` | Post the MR message, update it in place, and manage threaded comment replies. | | `chat:write.public` | Post to public channels without requiring a manual bot invite to each one. | | `channels:read` | List public channels in the channel picker when you map a project. | | `groups:read` | List private channels the bot has been added to, so they appear in the picker too. | ### Webhook endpoint security * Each workspace gets a unique, unguessable webhook URL; deliveries are matched to exactly one workspace. * When you configure a secret token, PRFlow verifies the `X-Gitlab-Token` header on every delivery using a constant-time comparison and rejects mismatches with HTTP 401. The secret is stored encrypted. Leaving the secret blank (URL-only mode) is supported but the secret is recommended. * Events whose project host does not match the connected GitLab instance are rejected. * Events for projects you have not added are acknowledged and discarded without processing. * Webhook payloads are capped at 10 MB to prevent resource-exhaustion abuse. ### Credentials and transport * The gitlab.com OAuth flow uses the standard authorization-code grant with CSRF state validation (state values are stored as SHA-256 hashes and expire after 10 minutes). * GitLab and Slack tokens are encrypted at rest with AES-256-GCM and are never written to logs. * OAuth access tokens are refreshed automatically before expiry; 401/403 responses from GitLab flag the credential as unhealthy on your dashboard so you can rotate it. * All traffic β€” GitLab webhooks in, GitLab API calls out, Slack API calls out β€” runs over HTTPS. Self-managed instance URLs must be HTTPS. ## Support Questions about the integration, setup help, or a webhook that isn't firing? Email [hello@prflow.dev](mailto:hello@prflow.dev) and we'll get you sorted. # Welcome to PRFlow Source: https://docs.prflow.dev/index Merge request and pull request notifications in Slack, for GitLab and GitHub β€” one updating message per MR/PR with CI/CD status and threaded comment sync. PRFlow replaces the native GitLab and GitHub Slack integrations for code-review notifications. Instead of flooding channels with separate messages for every event, PRFlow posts a **single message per merge request or pull request** that updates in place as it progresses β€” CI/CD status, approvals, and merge all appear on the original message, and review comments go to a Slack thread. ## Key features * **[One updating Slack message](/mr-states) per merge request or pull request** β€” not 10+ separate messages, with a colored rail showing each MR/PR's state at a glance * **CI/CD status** displayed inline on the notification β€” GitLab pipelines and GitHub Actions * **[Review comments synced](/comment-sync)** to a Slack thread, with edits updated in place β€” on both GitLab and GitHub * Works with **gitlab.com, self-managed GitLab, and GitHub** * **Read-only access** β€” GitLab `read_api` scope, or the read-only PRFlow GitHub App; no write permissions * Posts to **your existing Slack channels** β€” no ephemeral channels Connect GitLab or GitHub and Slack and get your first notification in a few minutes. Free for teams of up to 3 users. All features included on every plan. ## How it works PRFlow is a multi-tenant SaaS: each workspace connects one source provider β€” a GitLab organization (top-level group) or GitHub via the PRFlow GitHub App, published on the GitHub Marketplace as [PRFlow for Slack](https://github.com/marketplace/prflow-for-slack) β€” and one Slack workspace. The source sends webhook events to PRFlow, PRFlow keeps one Slack message per merge request or pull request up to date, and comments flow into the message's thread. Questions? Reach us at [hello@prflow.dev](mailto:hello@prflow.dev). # MR card states Source: https://docs.prflow.dev/mr-states What the colored rail on PRFlow's Slack card means: blue, amber, red, green, purple, and grey, state by state. PRFlow posts one Slack card per merge request and keeps updating that same message as the MR moves through its lifecycle, while review comments join it as [threaded replies](/comment-sync). The colored rail on the card's left edge summarizes the state at a glance, so you can scan a channel without reading a single row. The same card and colors apply to GitHub pull requests. | Rail | State | What it means | | --------- | -------------- | ----------------------------------------------- | | πŸ”΅ Blue | Under review | Default state, nothing known to block the merge | | 🟠 Amber | In flux | Draft, pipeline running, or merge scheduled | | πŸ”΄ Red | Blocked | At least one required merge gate is failing | | 🟒 Green | Ready to merge | Every required gate is confirmed passing | | 🟣 Purple | Merged | The MR was merged, the card's final state | | ⚫ Grey | Closed | Closed without merging, comes back if reopened | ## Reading the requirement rows The card body shows one row per merge requirement that is actually enabled for the repository: required CI, approvals (with the required count), an up-to-date branch, resolved review threads, and merge conflicts. Each row flips independently as events arrive: * 🟒 the requirement is confirmed passing * πŸ”΄ the requirement is failing and blocks the merge * πŸ”„ in progress right now (a running pipeline) * βšͺ pending (a pipeline that exists but has not started) * ◻️ unknown, PRFlow has no signal yet; unknown is never treated as passing A draft PR's card shows a πŸ“ **Draft** row while the draft flag is set (drafts post when the rule's [**Draft PRs** switch](/channel-routing) is on). The rail color is derived from those rows plus the MR's lifecycle, as follows. ## Blue: under review PRFlow Slack card with a blue rail: CI pending, approvals and threads unknown, up to date, no conflicts The default state. Nothing is known to block the merge, but not every requirement is confirmed passing yet. A just-opened MR usually starts blue: the pipeline has not run and nobody has reviewed it. Rows showing ◻️ or βšͺ keep the card blue rather than green, because PRFlow never counts an unknown as a pass. ## Amber: in flux PRFlow Slack card with an amber rail: CI running, other requirements unchanged The state is changing right now. The rail turns amber while the MR is a draft, while a pipeline is actively running, or while a merge is scheduled to happen automatically (for example "merge when pipeline succeeds"). Amber resolves itself: once the pipeline finishes or the draft is marked ready, the card moves to blue, red, or green. ## Red: blocked PRFlow Slack card with a red rail: CI failed, approvals 1/2, two unresolved threads At least one required merge gate is failing. Failed or canceled CI, missing approvals, unresolved review threads, a branch that needs a rebase, and merge conflicts all turn the rail red, and the failing rows name exactly what is left to fix. Less common blockers (blocked by another MR, security policy violations, a missing Jira issue, and similar) also turn the rail red and appear as their own row. A failing gate that is not required, such as an optional pipeline, never turns the card red. ## Green: ready to merge PRFlow Slack card with a green rail: CI passed, approvals 2/2, all threads resolved, up to date, no conflicts Every required gate is confirmed passing: the MR is mergeable right now. Green is earned, never assumed. If any required gate is still unknown, the card stays blue instead of guessing. ## Purple: merged PRFlow Slack card with a purple rail: merged into main The MR was merged. The requirement rows collapse into a single line naming the target branch, so the card becomes a compact record of the MR in the channel. Merged is the card's only final state: a merged MR cannot be reopened, so the card never changes again. ## Grey: closed PRFlow Slack card with a grey rail: closed The MR was closed without being merged: the rows collapse into a single ❌ Closed line. Unlike merged, closed is not final. If the MR is reopened, the card comes back to life and the rail returns to whatever color the reopened MR's state calls for. *** Questions about a state you're seeing? Email [hello@prflow.dev](mailto:hello@prflow.dev). # Scheduled digest Source: https://docs.prflow.dev/pr-digest A recurring Slack post that lists a channel's open PRs, grouped by what each one is waiting on. ## What it posts On the schedule you pick, PRFlow posts a summary of every open PR routed to a Slack channel, from GitLab and GitHub alike. Each line links the PR and shows why it is still open, how old it is, and who wrote it. PRs are sorted into three groups: * πŸ”΄ **Needs attention**: failing CI, merge conflicts, changes requested, unresolved discussions, or a blocked merge * 🟑 **Waiting on review**: approvals missing or CI still running * 🟒 **Ready to merge**: approved and green PRs with no activity for longer than the stale threshold are flagged with 🚨. ## Turning it on The digest lives inside the [notification rule](/channel-routing) that posts to the channel. On the dashboard, open the rule's editor β€” **Edit** on an existing rule, or while creating one β€” and switch on **Scheduled digest**. The schedule, filter, and delivery settings unfold below the toggle. The Scheduled digest section of the rule editor: toggle on, with schedule, filters, and delivery settings expanded ## Settings * **Schedule**: the days and time of day the digest posts. The timezone defaults to your workspace setting and can be overridden per channel. * **Filters**: hide draft PRs (independent of the rule's [**Draft PRs** switch](/channel-routing)), hide bot-authored PRs (Dependabot, Renovate, and similar), and skip recently opened PRs, either anything opened today or anything younger than a number of hours or days. **Flag PRs as stale** sets how many days of inactivity earn the 🚨 marker. * **Delivery**: an empty digest is skipped by default. Turn on **Post even when there are no open PRs** to get a short all-clear message instead. Digest settings belong to the channel, so everything a rule routes there shares one digest. They also stay with the channel: deleting a rule and later creating a new one for the same channel brings the saved settings back. # Quickstart Source: https://docs.prflow.dev/quickstart Connect GitLab or GitHub and Slack, map repositories to channels, and get your first merge request notification in a few minutes. Setup takes a few minutes in the PRFlow web app at [app.prflow.dev](https://app.prflow.dev): you connect a source provider β€” GitLab or GitHub β€” connect Slack, and map repositories to Slack channels. ## Pick your provider A workspace connects one source provider. The Slack side is identical either way; only the provider connection differs: * **[GitLab](#set-up-with-gitlab)** connects via OAuth (gitlab.com) or a `read_api` personal access token, plus a webhook you point at PRFlow. Works with gitlab.com and self-managed instances. * **[GitHub](#set-up-with-github)** is a one-click install of the read-only [PRFlow GitHub App](/github-integration) β€” no webhook URLs, no tokens or secrets to paste. Works with github.com organizations and personal accounts. ## Set up with GitLab Sign in at [app.prflow.dev](https://app.prflow.dev) and create or select a workspace. Each PRFlow workspace connects to one GitLab organization (top-level group) and one Slack workspace. From **Home** or **Settings β†’ Connections**, choose your instance type: * **gitlab.com** β€” connect via OAuth (PRFlow requests only the `read_api` scope; tokens auto-refresh), or save a personal access token instead. * **Self-managed GitLab** β€” enter your instance root URL (HTTPS required; PRFlow appends `/api/v4` automatically) and save a personal access token. OAuth is not available for self-managed instances. Personal access tokens must have the `read_api` scope and access to the GitLab group you want PRFlow to use. PRFlow validates the token on save β€” scope, expiry, and revocation β€” and recommends a dedicated service-account token over a personal one to reduce operational risk. Token health (expiring, expired, revoked) is surfaced on the dashboard afterwards. During connection, PRFlow resolves the **top-level GitLab group** your credential can access and binds the workspace to it. One GitLab organization maps to one PRFlow workspace β€” connecting the same GitLab group to a second workspace is rejected. PRFlow shows your workspace's unique webhook URL under **Settings β†’ Connections β†’ GitLab Webhook**. In GitLab, add it as either: * **Group webhook (recommended)** β€” add the URL once on your top-level group (**Group β†’ Settings β†’ Webhooks**). Covers all projects in the group. Requires the Owner role and GitLab Premium or Ultimate. * **Per-project webhooks** β€” add the same URL to each project (**Project β†’ Settings β†’ Webhooks**). Works on every GitLab tier, including Free. Requires Maintainer or Owner on each project. Enable the **Merge request events**, **Comment events**, and **Pipeline events** triggers, and keep **SSL verification** on. Optionally set a **Secret token** in GitLab and save the same value in PRFlow β€” PRFlow then verifies the `X-Gitlab-Token` header on every delivery. Authorize PRFlow's Slack app via OAuth. PRFlow requests four bot scopes: `chat:write`, `chat:write.public`, `channels:read`, and `groups:read` β€” enough to list channels and post/update messages, nothing more. Click **+ New rule** on the dashboard, pick the destination Slack channel (PRFlow fills in the rule name for you), and choose what routes there: a specific project, a wildcard pattern on the full GitLab path such as `acme/frontend/*`, or every repository as a catch-all. Rules are checked top to bottom and the first match wins β€” see [Notification rules](/channel-routing) for the details. From then on, every merge request in the matched projects posts itself to the rule's channel (drafts wait until they're marked ready unless the rule's **Draft PRs** switch is on). **GitLab compatibility:** gitlab.com on any tier (group webhooks need Premium or Ultimate; per-project webhooks work on Free), and self-managed GitLab instances reachable over HTTPS. Self-managed instances behind a firewall need outbound HTTPS to `app.prflow.dev` for webhooks; PRFlow's API polling additionally requires the instance to be reachable from the internet. For the technical details behind these steps β€” webhook events consumed, API endpoints called, and how data is handled β€” see the [GitLab integration](/gitlab-integration) reference. ## Set up with GitHub 1. Sign in at [app.prflow.dev](https://app.prflow.dev), open **Settings β†’ Connections**, and select **GitHub** as the source provider. 2. Click **Install GitHub App**. GitHub asks which account or organization to install to and which repositories the App may see, and shows the read-only permissions it requests β€” the [full list with reasons](/github-integration#permissions-&-data) is on the GitHub integration page. 3. Back in PRFlow, [connect Slack](/slack-integration) and map repositories to Slack channels from the dashboard. That's the whole setup β€” no webhook URLs and no secrets to paste. The [GitHub integration](/github-integration#setup) reference covers the details, including starting from the GitHub Marketplace listing instead. Questions or trouble getting set up? Check the [FAQ](/faq) or email [hello@prflow.dev](mailto:hello@prflow.dev). # Slack integration Source: https://docs.prflow.dev/slack-integration How PRFlow connects to Slack, which channels it can post to, the four bot scopes it requests, what data reaches your workspace, and how disconnecting or uninstalling works. ## Overview PRFlow delivers GitLab merge request and GitHub pull request notifications to Slack. For each MR/PR it posts **one Slack message that updates in place** as the work progresses β€” CI status, approvals, and merge state all appear on the original card instead of new messages piling up β€” and review comments are synced into a Slack thread under it, so the channel stays readable. Slack is the **destination** side of PRFlow. Notifications originate from a source provider β€” [GitLab](/gitlab-integration) or [GitHub](/github-integration) β€” so a workspace needs a source connected before the Slack connection has anything to deliver. Each PRFlow workspace connects to one Slack workspace. ## Setup You connect Slack from inside PRFlow, via OAuth: 1. Sign in at [app.prflow.dev](https://app.prflow.dev) and connect a source provider first β€” [GitLab](/gitlab-integration) or [GitHub](/github-integration). 2. From **Home** or **Settings β†’ Connections**, click **Connect Slack** and authorize the app in the Slack consent screen. PRFlow requests the four bot scopes listed below β€” enough to list channels and post/update its own messages, nothing more. 3. Map repositories to Slack channels from the dashboard. From then on, every qualifying MR/PR posts its card to the mapped channel. Starting from Slack instead β€” an **Add to Slack** link or Slack's app directory β€” also works: the install lands on a guided sign-in page that explains the order of operations (connect a repository first, then add Slack from within PRFlow). No partial or dangling Slack connection is created along the way; you simply create an account or sign in and continue setup from the right starting point. ## How it works ### Which channels PRFlow can post to PRFlow channel picker listing public Slack channels and one private channel * **Public channels** β€” PRFlow can post to any public channel without a manual invite (that is what the `chat:write.public` scope is for), and every public channel appears in the channel picker. * **Private channels** β€” PRFlow can only see and post to a private channel after someone runs `/invite @PRFlow` in it. Once the bot is a member, the channel shows up in the channel picker like any other. PRFlow never reads channel messages: it requests no history scopes and no user scopes, so it cannot see what anyone writes in your channels or DMs. It only posts and updates its own messages. ### Posting model * **One card per MR per channel.** The first qualifying event posts the message; every later event updates that same message (Slack `chat.update`) instead of posting a new one. If a repository is mapped to multiple channels, each channel gets at most one message per MR, and updates stay in the channel where the message was originally posted. * **Comments as threaded replies.** Each review comment becomes a reply in the card's thread, quoting the comment text (truncated to 2,000 characters) with the commenter's username. Comment edits update the existing reply; comment deletions remove it. ## Permissions & security PRFlow requests exactly four **bot** scopes β€” no user scopes, no message history: | Scope | Why PRFlow needs it | | ------------------- | ---------------------------------------------------------------------------------- | | `chat:write` | Post the MR message, update it in place, and manage threaded comment replies. | | `chat:write.public` | Post to public channels without requiring a manual bot invite to each one. | | `channels:read` | List public channels in the channel picker when you map a project. | | `groups:read` | List private channels the bot has been added to, so they appear in the picker too. | The Slack bot token is encrypted at rest with AES-256-GCM and is never written to logs, and all Slack API traffic runs over HTTPS. ### Data sent to Slack Notifications in your Slack channels contain: * MR/PR number, title, and a link back to GitLab or GitHub, * author display name and username, target branch, and project name, * CI status, approval/review indicators, and merge-status labels, * comment text excerpts (up to 2,000 characters) and commenter usernames in thread replies. **Comment text is retained** for the comments PRFlow syncs, alongside the comment ID and the Slack message reference: delivery is durable, so a reply is recorded before it is posted and retried if Slack is unavailable, and an edit at the source updates the existing reply without re-fetching it. For the full picture of what PRFlow receives from the source provider and what it stores, see the GitLab page's [data handling section](/gitlab-integration#data-handling), which applies equally to GitHub. ## Disconnecting Either side works: * **From PRFlow** β€” the Slack card on **Settings β†’ Connections** has a **Disconnect** button (admin-only, with a confirmation step). Your repository and channel-mapping setup is kept, and notifications resume when you reconnect. Disconnecting also revokes the bot token at Slack, so it does not linger β€” unless another of your PRFlow organizations shares the same Slack workspace, in which case revocation is safely skipped and only your connection is removed. * **From Slack** β€” uninstalling the PRFlow app from the workspace (or revoking its tokens on Slack's side) disconnects **every** PRFlow organization linked to that workspace immediately, deleting the stored access tokens and stopping further posting attempts. If Slack ever rejects a notification because a connection has gone bad, PRFlow marks that connection unhealthy and stops trying to post through it instead of failing on every event; reconnecting from the Connections page restores it. ## Support Questions about the integration, a channel that isn't showing up in the picker, or a notification that didn't arrive? Email [hello@prflow.dev](mailto:hello@prflow.dev) and we'll get you sorted.