When something happens, machines get a webhook — people get a notification.
GAEvery meaningful change in a repo becomes an event. Subscribe an endpoint and it arrives as a signed webhook you can verify and act on; the people who care get a personal notification in the dashboard. One thing happens; the right systems and the right humans both find out.
What fires
These are the events you can subscribe to. Each carries a small JSON body describing what changed and which repo it belongs to.
| Event | Fires when |
|---|---|
push | Commits are pushed to a branch. |
pull_request | A pull request is opened or merged. |
issue | An issue is opened. |
release | A release is published. |
deployment | A deployment changes state. |
check | A commit status / check is reported. |
ping | You send a test from the dashboard or CLI. |
push can kick off your CI; a published release can trigger a rollout; a deployment can update your status page. TillForge doesn’t run your pipeline — it tells your pipeline exactly when to run.Point an endpoint at the events
Add a webhook for an org (all repos) or a single repo, choose your events, and set a shared secret — the secret is what lets you prove a delivery is genuinely from TillForge. Send yourself a ping to confirm the endpoint is wired before anything real depends on it.
# Subscribe an endpoint to the events you care about. A shared secret
# lets you verify every delivery came from us and wasn't tampered with.
tilldev forge webhook add \
--url https://ci.example.com/hooks/tillforge \
--events push,pull_request,release,deployment \
--repo acme-api \
--secret "$(openssl rand -hex 32)"
tilldev forge webhook ls
tilldev forge webhook ping <id> # send a test 'ping' event now
tilldev forge webhook deliveries <id> # the recent delivery log
tilldev forge webhook rm <id>Verify every delivery
Two headers ride on every delivery: X-TillForge-Event names the event, and X-TillForge-Signature carries an HMAC-SHA256 of the raw body keyed by your secret, as sha256=<hex>. Recompute it over the exact bytes you received and compare in constant time. Reject anything that doesn’t match — that’s how you know a request is really from us and untampered.
import crypto from 'node:crypto'
// Verify a delivery before you trust it. The signature is an HMAC-SHA256
// of the raw request body, keyed by your webhook secret.
export function isValid(rawBody: string, header: string, secret: string) {
const expected = 'sha256=' +
crypto.createHmac('sha256', secret).update(rawBody).digest('hex')
const a = Buffer.from(header)
const b = Buffer.from(expected)
// constant-time compare — never a plain ===
return a.length === b.length && crypto.timingSafeEqual(a, b)
}
// req: X-TillForge-Event tells you which event; X-TillForge-Signature is the sig.
// isValid(rawBody, req.headers['x-tillforge-signature'], YOUR_SECRET)===.Delivery you can inspect
Deliveries are best-effort and recorded. Every attempt logs its response and timing, viewable with forge webhook deliveries or on the Webhooks screen — so a receiver that was down is visible, not silent. Delivery is time-boxed, and a receiver that answers slowly never holds up the push, merge, or release that triggered it.
Notifications — for people, not machines
The human side of the same events. When your work gets attention, a notification lands in the dashboard’s Notifications screen, deep-linked to the thread and tracked as unread until you open it.
| You get one when | On |
|---|---|
| Review — someone reviews it | Your pull request |
| Comment — someone replies | Your pull request or issue |
| Merge — it lands | Your pull request |
Notifications are personal — you only ever see your own, and you can only mark your own read. They’re a lightweight inbox for what needs your eyes; the durable, org-wide record of who did what lives in the audit log.
Next: Deployments and Releases & builds for the events worth wiring up, or the CLI reference. Back to the TillForge overview.