Guides

Auto-Create GitHub Issues from Webhook Payloads

Build an n8n workflow that receives webhook payloads and automatically creates formatted GitHub issues with labels and assignments.

GuideIntermediate5 min read

Nodes used: Webhook, Edit Fields (Set), IF, GitHub


What you'll build

A workflow that receives incoming webhook payloads (for example, from a monitoring tool or internal service), filters them by severity, and automatically creates labeled GitHub issues for events that need attention. The result is an automated triage pipeline that turns alerts into trackable issues without manual intervention.

Why this is useful

Engineering teams deal with alerts from many sources: uptime monitors, CI pipelines, error trackers, and custom services. Manually creating GitHub issues from these alerts is tedious and error-prone. This workflow standardizes the process and ensures nothing falls through the cracks.

Prerequisites

  • An n8n instance (Cloud or self-hosted, v2.x)
  • A GitHub account with access to the target repository
  • A GitHub credential configured in n8n (OAuth2 or Personal Access Token)
  • A tool capable of sending HTTP requests (curl, Postman, or another service)

Steps

1. Create a new workflow

Open n8n, click Add workflow, and name it "Webhook to GitHub Issues."

2. Add the Webhook trigger node

Click Add first step and select Webhook. Configure:

  • HTTP Method: POST
  • Path: Set a descriptive path such as alert-intake.
  • Response Mode: Leave as When last node finishes so the caller gets a confirmation response.

After saving, n8n generates two webhook URLs:

n8n Cloud

Your production URL follows the pattern:

https://<your-instance>.app.n8n.cloud/webhook/alert-intake

The test URL uses /webhook-test/ and is active only while the canvas is open.

Self-hosted

Your production URL follows the pattern:

https://<your-domain>/webhook/alert-intake

If you are running n8n behind a reverse proxy, make sure the WEBHOOK_URL environment variable is set to your externally reachable base URL. Otherwise, the generated URLs will reference localhost and external services will not be able to reach them.

Info: Test vs. production URLs

Use the test URL while building the workflow. Switch to the production URL only after you activate the workflow.

3. Add the Edit Fields node to shape the data

Click + on the Webhook output and add an Edit Fields (Set) node. Define the following fields by pulling values from the incoming payload using expressions:

Field Name Expression
title {{ $json.body.title }}
body {{ $json.body.description }}
severity {{ $json.body.severity }}
source {{ $json.body.source }}

Set Keep Only Set to true so downstream nodes receive a clean, predictable object.

4. Add the IF node to filter by severity

Add an IF node after Edit Fields. Configure a condition:

  • Value 1: {{ $json.severity }}
  • Operation: is equal to
  • Value 2: critical

This routes only critical alerts to GitHub. Non-critical payloads exit through the false branch, which you can leave unconnected or attach to a logging node later.

Tip: Multiple severity levels

If you need to handle more than two levels, replace the IF node with a Switch node and define separate outputs for critical, warning, and info.

5. Add the GitHub node

Connect the true output of the IF node to a new GitHub node. Configure:

  • Credential: Select your GitHub credential.
  • Resource: Issue
  • Operation: Create
  • Repository Owner: Your GitHub username or organization.
  • Repository Name: The target repository.
  • Title: {{ $json.title }}
  • Body: Use the expression editor to build a Markdown body:
**Source:** {{ $json.source }}
**Severity:** {{ $json.severity }}

{{ $json.body }}
  • Labels: Add a label such as bug or alert. You can also set this dynamically from the payload.

6. Test with a curl command

With the workflow canvas open (so the test URL is active), send a test payload:

curl -X POST \
  '<your-test-webhook-url>' \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "Database connection timeout",
    "description": "Connection pool exhausted on db-primary. Average query latency exceeded 5s.",
    "severity": "critical",
    "source": "monitoring-agent"
  }'

Check the n8n canvas to see data flow through each node, then verify the issue was created in your GitHub repository.

7. Activate the workflow

Toggle the Active switch. From this point on, the production webhook URL is live and ready to receive payloads from your monitoring tools or services.


Test it

Send payloads with different severity values (critical, warning, info) and confirm that only critical alerts produce GitHub issues. Open the Executions log in n8n to trace each run and verify the IF node routed data correctly.


Take it further

  • Add assignees dynamically. Map a team field from the payload to specific GitHub usernames using a Switch or Code node.
  • Deduplicate issues. Before creating a new issue, use the GitHub node's Get All operation to search for existing open issues with the same title and skip creation if one exists.
  • Acknowledge the caller. Change the Webhook's response mode to Immediately and configure a custom response body so the calling service receives a structured JSON confirmation.
  • Attach to multiple repos. Use a Switch node to route issues to different repositories based on the source field in the payload.

Securing the Webhook

The workflow above uses an open webhook with no authentication. This is fine for testing, but a production webhook exposed to the internet should always require authentication to prevent unauthorized payloads.

Add Header Auth

  1. Open the Webhook node and set Authentication to Header Auth.
  2. Create a new Header Auth credential with a custom header name (e.g., X-Webhook-Secret) and a strong random value.
  3. Update the calling service to include the header in every request:
curl -X POST \
  '<your-production-webhook-url>' \
  -H 'Content-Type: application/json' \
  -H 'X-Webhook-Secret: your-secret-value-here' \
  -d '{ "title": "Test", "severity": "critical", "source": "monitor" }'

Requests missing the header or with an incorrect value receive a 403 Forbidden response.

Warning: Why this matters

An unauthenticated production webhook can be discovered by bots scanning common paths. Without auth, anyone can trigger your workflow and create spurious GitHub issues. Header Auth adds a shared secret that only your authorized callers know.


Troubleshooting

Issue: Webhook returns 404 Not Found. The workflow is not active, or the calling service is using the test URL instead of the production URL. Toggle the workflow to Active and verify the external service is configured with the production URL (/webhook/alert-intake, not /webhook-test/alert-intake). On self-hosted instances, also confirm the WEBHOOK_URL environment variable is set to your public-facing base URL.

Issue: GitHub node fails with "Resource not found" or "Validation Failed". Check that the repository owner and repository name are correct and that your GitHub credential has the repo scope. If you are using labels, verify the label already exists on the repository -- the Create Issue operation does not create labels automatically.

Issue: IF node sends all items to the False branch even though severity is "critical". String comparisons are case-sensitive. If the payload sends "Critical" or "CRITICAL", the condition equals "critical" will not match. Use an expression with .toLowerCase() in the Edit Fields node to normalize the severity value before the IF node: {{ $json.body.severity.toLowerCase() }}.

Want this running in your stack?

I build production n8n and Cloudflare automation for teams — the same engineering behind HarperFlow. Fixed-price, escrow-protected, US-based.