Webhooks
Webhooks send HTTP POST requests to your server when events happen in your repositories. Every payload is signed with HMAC-SHA256 so you can verify authenticity.
Setting Up a Webhook
Create a webhook from your repository settings or via the API:
Create via API
curl -X POST https://api.gitforge.dev/api/repos/REPO_ID/webhooks \
-H "Authorization: Bearer gf_YOUR_PAT" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks/gitforge",
"secret": "your_webhook_secret",
"events": ["push", "pull_request"],
"active": true
}'The secret is used to generate HMAC signatures. Store it securely and never commit it to version control.
Event Types
EventDescriptionKey Fields
pushA push to a branch or tag.
pull_requestA pull request is opened, updated, closed, or merged.
pull_request_reviewA review is submitted on a pull request.
branch_createA branch is created.
branch_deleteA branch is deleted.
tag_createA tag is created.
repositoryA repository is created, deleted, or updated.
memberA collaborator is added or removed.
Use "events": ["*"] to subscribe to all events.
Payload Format
All payloads are JSON. The event type is included in the X-GitForge-Event header.
Example push payload
{
"event": "push",
"ref": "refs/heads/main",
"before": "abc1234...",
"after": "def5678...",
"commits": [
{
"id": "def5678...",
"message": "Add new feature",
"author": {
"name": "Jane Doe",
"email": "[email protected]"
},
"timestamp": "2026-03-28T12:00:00Z",
"added": ["src/feature.ts"],
"modified": [],
"removed": []
}
],
"pusher": {
"name": "Jane Doe",
"email": "[email protected]"
},
"repository": {
"id": "repo-uuid",
"name": "my-project",
"full_name": "my-org/my-project"
}
}Headers
X-GitForge-EventEvent type (e.g. push, pull_request)
X-GitForge-DeliveryUnique delivery UUID
X-GitForge-Signature-256HMAC-SHA256 signature of the body
Content-Typeapplication/json
Verifying Signatures
Every webhook request includes an X-GitForge-Signature-256 header containing a hex-encoded HMAC-SHA256 digest of the request body, prefixed with sha256=.
Node.js verification
import crypto from "node:crypto";
function verifyWebhook(body: string, signature: string, secret: string): boolean {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(body, "utf-8")
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
// In your handler:
const sig = req.headers["x-gitforge-signature-256"];
if (!verifyWebhook(rawBody, sig, process.env.WEBHOOK_SECRET!)) {
return res.status(401).send("Invalid signature");
}Python verification
import hmac
import hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode("utf-8"),
body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(signature, expected)
# In your handler:
sig = request.headers.get("X-GitForge-Signature-256", "")
if not verify_webhook(request.data, sig, os.environ["WEBHOOK_SECRET"]):
abort(401, "Invalid signature")Go verification
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
)
func VerifyWebhook(body []byte, signature, secret string) bool {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(body)
expected := "sha256=" + hex.EncodeToString(mac.Sum(nil))
return hmac.Equal([]byte(signature), []byte(expected))
}SDK Webhook Helpers
All official SDKs include webhook verification helpers:
TypeScript SDK
import { GitForge } from "@gitforge/sdk";
const gf = new GitForge({ webhookSecret: "your_secret" });
// Returns typed event object or throws on invalid signature
const event = gf.webhooks.verify(rawBody, headers);
console.log(event.type); // "push"
console.log(event.payload); // typed payloadPython SDK
from gitforge import GitForge
gf = GitForge(webhook_secret="your_secret")
event = gf.webhooks.verify(request.data, request.headers)
print(event.type) # "push"
print(event.payload) # typed payload- Webhooks are delivered within seconds of the event.
- Your server must respond with a 2xx status within 10 seconds.
- Failed deliveries are retried up to 3 times with exponential backoff (10s, 60s, 300s).
- Webhooks that fail consistently for 7 days are automatically disabled.
- Delivery history is available in your repository settings for the last 30 days.