Agent skill

Forgejo Webhooks Skill

Receive and verify Forgejo webhooks. Use when setting up Forgejo or Codeberg webhook handlers, debugging X-Forgejo-Signature verification, or handling self-hosted Git events like push, pull_request, issues, issue_comment, create, delete, or release.

Install this skill

npx skills add hookdeck/webhook-skills --skill forgejo-webhooks


When to Use This Skill

  • How do I receive Forgejo (or Codeberg) webhooks?
  • How do I verify the X-Forgejo-Signature header?
  • Why is my Forgejo webhook signature verification failing?
  • How do I handle push, pull_request, issues, issue_comment, create, delete or release events from Forgejo?
  • Which header carries the Forgejo event name (X-Forgejo-Event-Type vs X-Forgejo-Event)?

Setup note

Set a secret on the Forgejo webhook. Without one, deliveries are unsigned and fail verification. Set the webhook's HTTP method to POST and its content type to application/json. Form-encoded and GET deliveries sign the JSON payload rather than the request body and fail verification.

Verification (core)

Forgejo signs the raw request body bytes with HMAC-SHA256 keyed on the webhook Secret (used as UTF-8 bytes, not decoded). X-Forgejo-Signature is the bare lowercase hex digest: no sha256= prefix, no timestamp, nothing else signed. Compare the whole header value as one digest. If no secret is set, the header is still sent but empty, so reject an empty value.

Node:

const crypto = require('crypto');

function verify(rawBody, signature, secret) { // rawBody: Buffer
  if (!secret || typeof signature !== 'string' || !/^[0-9a-fA-F]{64}$/.test(signature)) return false;
  const expected = crypto.createHmac('sha256', Buffer.from(secret, 'utf8')).update(rawBody).digest();
  return crypto.timingSafeEqual(Buffer.from(signature.toLowerCase(), 'hex'), expected);
}

Python:

import hashlib, hmac, re

def verify(raw_body: bytes, signature: str | None, secret: str | None) -> bool:
    if not secret or not signature or not re.fullmatch(r"[0-9a-fA-F]{64}", signature):
        return False
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).digest()
    return hmac.compare_digest(bytes.fromhex(signature), expected)

Return 401 for a missing, empty, malformed or mismatched signature. There is no signed timestamp, so there is no replay window to check; use X-Forgejo-Delivery for idempotency. Read the raw bytes for any content type (in Express: express.raw({ type: '*/*' })) and parse only after verifying.

For complete handlers with route wiring, event dispatch, and tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Common Event Types

Dispatch on the X-Forgejo-Event-Type header (the exact event type, e.g. issue_assign). X-Forgejo-Event is the coarser group (e.g. issues) and is empty for package, schedule and workflow_dispatch events.

Event typeDescription
pushCommits pushed to a branch or tag
pull_requestPull request opened, closed, reopened, edited
issuesIssue opened, closed, reopened, edited
issue_commentComment on an issue
pull_request_commentComment on a pull request (sent with X-Forgejo-Event: issue_comment)
createBranch or tag created
deleteBranch or tag deleted
releaseRelease published, updated, deleted

Forgejo documents 41 event types in all, including fork, issue_assign, issue_label, issue_milestone, pull_request_assign, pull_request_label and pull_request_milestone. The examples handle the common ones above and log the rest. Most bodies also carry an action.

Full reference: Forgejo webhooks

Important Headers

HeaderDescription
X-Forgejo-SignatureBare hex HMAC-SHA256 of the raw body (empty if no secret)
X-Forgejo-Event-TypeExact event type (push, issue_assign, ...)
X-Forgejo-EventCoarser event group (push, issues, ...)
X-Forgejo-DeliveryUnique delivery UUID
X-Hub-Signature-256Same digest as sha256=<hex> (also sent as X-Gitea-Signature, X-Gogs-Signature)

Environment Variables

FORGEJO_WEBHOOK_SECRET=your_webhook_secret   # The "Secret" field of the Forgejo webhook

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 forgejo --path /webhooks/forgejo

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 12, 2026

View on GitHub →