Skip to main content

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.
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/<workspace-id>). 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. 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.

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. You connect GitLab, point a webhook at PRFlow, connect Slack, and map projects to channels.
1

Sign in and pick a workspace

Sign in at app.prflow.dev and create or select a workspace. Each PRFlow workspace connects to one GitLab organization (top-level group) and one Slack workspace.
2

Connect GitLab

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.
3

Add the webhook in GitLab

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.
4

Connect Slack

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.
5

Create a notification rule

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 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 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

Slack scopes

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 and we’ll get you sorted.