n8n GitHub Integration: Automate PR and Issue Workflows

Connect n8n to GitHub to automate PR reviews, issue triage, and Slack notifications. Step-by-step setup with real workflow JSON. Saves 2+ hours/week.

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.

  1. Go to GitHub → Settings → Developer settings → OAuth Apps → New OAuth App
  2. 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
  3. Copy Client ID and Client Secret

In n8n:

  1. Open Credentials → New → GitHub OAuth2 API
  2. Paste Client ID and Client Secret
  3. 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 }} equals ready_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 }} equals opened

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 label
  • 404 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:

  1. In GitHub webhook settings, set a Secret (a random string, 32+ chars)
  2. In n8n, open your GitHub Trigger node → Options → Header Auth
  3. 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 action type, 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