Webhooks
DocuGardener receives GitHub webhook events at POST /webhooks/github. This page explains event handling, security, and how to test locally.
Handled Events
| Event | Action | What happens |
|---|---|---|
| pull_request | opened | Enqueues a new PR analysis job. Quota checked first. |
| pull_request | synchronize | New commits pushed to an existing PR. Re-enqueues analysis. |
| pull_request | reopened | Treated the same as opened. |
| pull_request | closed / merged | Ignored — no analysis runs on close. |
All other event types (push, issues, etc.) are accepted with a 200 OK response and silently ignored. This is intentional — GitHub retries non-2xx responses, so always returning 200 prevents noise in the GitHub App delivery log.
Payload Structure
DocuGardener reads the following fields from the GitHub webhook payload:
{
"action": "opened",
"number": 42,
"pull_request": {
"number": 42,
"title": "feat: rename API endpoint",
"head": {
"sha": "abc123", // used for cloning + check run
"ref": "feat/rename-api"
},
"base": {
"ref": "main",
"repo": {
"full_name": "acme/backend",
"private": false
}
}
},
"installation": {
"id": 12345678 // maps to tenant via GitHub App installation
},
"repository": {
"full_name": "acme/backend"
}
}Signature Verification
Every request from GitHub includes an X-Hub-Signature-256 header containing an HMAC-SHA256 digest of the raw request body, keyed with your GITHUB_WEBHOOK_SECRET. DocuGardener verifies this before processing. Requests with missing or invalid signatures return 403.
Rate Limiting
A per-installation token-bucket rate limiter allows 20 webhook requests per minute (burst: 20). Requests over the limit return HTTP 429 with a Retry-After header. GitHub will retry the delivery automatically.
GitHub Check Run Lifecycle
After receiving a webhook, DocuGardener immediately creates a check run in queued state. As the analysis progresses, the check run transitions:
queued→ job accepted, waiting for workerin_progress→ worker started analysiscompleted / success— drift score below thresholdcompleted / failure— drift score at or above thresholdcompleted / neutral— analysis error or quota exceeded
The check run update always runs in a finally block, so it resolves even if the analysis fails mid-way.
Local Testing
GitHub cannot reach localhost. For local development, use a proxy to forward webhooks:
# Install smee client globally npm install -g smee-client # Create a new channel at https://smee.io/new then run: smee --url https://smee.io/YOUR_CHANNEL --target http://localhost:8000/webhooks/github
Set your GitHub App's webhook URL to the smee.io channel URL. In production, set it directly to https://your-domain.com/webhooks/github.
You can replay past deliveries from the Recent Deliveries tab in your GitHub App settings without needing to open a real pull request.