Problem: GitHub Notifications Don't Drive Action
Your team misses PR reviews. Issues sit unlabeled for days. Stale branches pile up. GitHub's built-in notifications are noise — they don't route work to the right person or trigger follow-up actions.
n8n fixes this by turning GitHub webhook events into automated workflows: label issues on open, ping reviewers on Slack when a PR is ready, and close stale issues after 30 days of inactivity.
You'll learn:
- How to connect n8n to GitHub via webhook and OAuth
- Build a PR review notification workflow with Slack
- Build an issue auto-labeling workflow by keyword
- Handle webhook security (signature verification)
Time: 25 min | Difficulty: Intermediate
Why GitHub's Native Automation Falls Short
GitHub Actions is great for CI/CD. It's not great for cross-tool workflows.
What it can't do natively:
- Post a formatted Slack message when a PR passes all checks
- Create a Linear/Jira ticket when a bug issue is opened
- DM the PR author if review hasn't happened in 24 hours
- Aggregate daily PR stats into a dashboard
n8n handles all of this with its GitHub trigger node and 400+ integrations — no custom GitHub App required.
Prerequisites
- n8n running (self-hosted via Docker or n8n Cloud)
- GitHub account with repo admin access
- A Slack workspace (for the notification workflow)
If you're not self-hosted yet, the quickest path is:
docker run -it --rm \
--name n8n \
-p 5678:5678 \
-v ~/.n8n:/home/node/.n8n \
n8nio/n8n
n8n is then available at http://localhost:5678.
Solution
Step 1: Create a GitHub OAuth Credential in n8n
n8n needs OAuth access to read/write issues and PRs — not just receive webhooks.
- Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
- Set:
- Application name:
n8n Automation - Homepage URL: your n8n instance URL (e.g.,
https://n8n.yourdomain.com) - Authorization callback URL:
https://n8n.yourdomain.com/rest/oauth2-credential/callback
- Application name:
- Copy Client ID and Client Secret
In n8n:
- Open Credentials → New → GitHub OAuth2 API
- Paste Client ID and Client Secret
- Click Connect my account — authorize in the GitHub popup
Expected: Green "Connected" badge in the credential.
Step 2: Build the PR Review Notification Workflow
This workflow fires when a PR is marked "ready for review" and sends a Slack message to the team channel.
Create a new workflow in n8n, then add these nodes:
Node 1 — GitHub Trigger
- Node type: GitHub Trigger
- Credential: the OAuth credential from Step 1
- Repository:
your-org/your-repo - Events:
Pull Request
This creates a webhook on your repo automatically. Check it at:
GitHub repo → Settings → Webhooks
Node 2 — IF Node (filter for ready_for_review)
GitHub fires the PR event for many actions (opened, closed, labeled, etc). Filter to only what you want:
- Condition:
{{ $json.body.action }}equalsready_for_review
Without this filter, every PR event hits Slack. That's noise.
Node 3 — Slack Node
- Node type: Slack
- Operation: Send a message
- Channel:
#engineering(or your team channel) - Message:
:eyes: *PR Ready for Review*
*<{{ $('GitHub Trigger').item.json.body.pull_request.html_url }}|{{ $('GitHub Trigger').item.json.body.pull_request.title }}>*
Opened by: {{ $('GitHub Trigger').item.json.body.pull_request.user.login }}
Changed files: {{ $('GitHub Trigger').item.json.body.pull_request.changed_files }}
Expected output in Slack:
👀 PR Ready for Review
[Fix: memory leak in worker pool] (linked)
Opened by: sarah-dev
Changed files: 4
Save and activate the workflow. Open a test PR and mark it ready — Slack should respond within 2 seconds.
Step 3: Build the Issue Auto-Labeling Workflow
This workflow adds a bug, feature, or question label automatically based on keywords in the issue title.
Node 1 — GitHub Trigger
- Events:
Issues
One trigger node handles all issue events.
Node 2 — IF Node (filter for opened)
- Condition:
{{ $json.body.action }}equalsopened
Only label new issues, not edits or closures.
Node 3 — Code Node (classify by keyword)
Use a Code node to decide the label:
const title = $input.item.json.body.issue.title.toLowerCase();
let label = 'needs-triage'; // default
if (title.includes('bug') || title.includes('error') || title.includes('crash') || title.includes('broken')) {
label = 'bug';
} else if (title.includes('feature') || title.includes('add') || title.includes('support') || title.includes('request')) {
label = 'feature';
} else if (title.includes('how') || title.includes('question') || title.includes('help') || title.includes('docs')) {
label = 'question';
}
return [{
json: {
...$input.item.json,
computed_label: label
}
}];
Node 4 — GitHub Node (add label)
- Operation: Add Labels to Issue
- Repository Owner:
{{ $json.body.repository.owner.login }} - Repository Name:
{{ $json.body.repository.name }} - Issue Number:
{{ $json.body.issue.number }} - Labels:
{{ $json.computed_label }}
If it fails:
422 Unprocessable Entity→ The label doesn't exist on the repo yet. Create it first:GitHub repo → Issues → Labels → New label404 Not Found→ Check repo name expression — it should pull from the webhook payload, not be hardcoded
Step 4: Verify Webhook Signatures
By default, n8n accepts any POST to your webhook URL. Anyone who discovers the URL can trigger your workflow.
Enable signature verification:
- In GitHub webhook settings, set a Secret (a random string, 32+ chars)
- In n8n, open your GitHub Trigger node → Options → Header Auth
- Add a header check — or use a Code node immediately after the trigger:
const crypto = require('crypto');
const secret = 'your-webhook-secret'; // store this in n8n credentials, not hardcoded
const signature = $input.item.json.headers['x-hub-signature-256'];
const body = JSON.stringify($input.item.json.body);
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
if (signature !== expected) {
throw new Error('Invalid webhook signature — request rejected');
}
return $input.all();
This rejects forged requests before any downstream nodes run.
Tip: Store the secret in n8n's built-in credential store (Credentials → New → Generic Credential Type) and reference it as {{ $credentials.GitHubWebhookSecret.value }} rather than hardcoding it.
Verification
Test the PR workflow:
# Via GitHub API — mark a draft PR as ready
gh pr ready <PR_NUMBER> --repo your-org/your-repo
Check Slack — the message should appear within 3 seconds.
Test the issue workflow:
# Create a test issue with a bug keyword
gh issue create \
--title "Bug: login crashes on Safari 17" \
--body "Repro steps..." \
--repo your-org/your-repo
Check the issue — it should have the bug label within 5 seconds.
Monitor workflow runs:
In n8n, open Executions (left sidebar). Every webhook hit shows up here with full input/output for each node. This is your first debugging stop when something doesn't trigger.
Production Considerations
Rate limits: GitHub's REST API allows 5,000 requests/hour per authenticated user. For high-volume repos (100+ PRs/day), you won't hit this. For monorepos with heavy activity, monitor usage at GET /rate_limit.
Webhook reliability: n8n's webhook receiver goes down if your instance restarts. Use n8n's queue mode with a Redis backend for production deployments — this persists webhook events across restarts.
Duplicate labels: If a workflow runs twice (webhook retried by GitHub), the "add label" call is idempotent — GitHub silently ignores adding a label that's already there. No defensive coding needed.
Self-hosted vs Cloud: If you're on localhost, GitHub can't reach your webhook. Use ngrok for local dev:
ngrok http 5678
# Copy the https URL → use as your n8n base URL in GitHub OAuth App
What You Learned
- GitHub Trigger nodes create and manage webhooks automatically — no manual GitHub App setup
- Always add an IF node after GitHub triggers to filter by
actiontype, or you'll process every event variant - The Code node gives you full JavaScript for logic that n8n's built-in nodes can't express
- Webhook signature verification is one Code node — skip it in dev, add it before going to production
Next step: Add a third workflow that comments on PRs older than 3 days with no review activity — combine the GitHub trigger with an n8n Schedule Trigger and the GitHub "List Pull Requests" node.
Tested on n8n 1.85.0, GitHub REST API v3, Node.js 20 LTS