Webhooks
Inbound: keeping mirrors fresh
Point your origin’s push webhook at Weft:
POST https://api.weft.sh/webhooks/github (GitHub App deliveries)
POST https://api.weft.sh/webhooks/generic (anything else)
Deliveries must carry X-Hub-Signature-256: sha256=<hmac> computed over the
raw body with your webhook secret; unsigned or mis-signed deliveries are
rejected with 401. A verified push event fans out background syncs to
every mirror of that origin, and the receipt-to-servable lag is recorded as
the freshness metric.
Generic-provider payloads identify the origin by URL:
{ "full_name": "https://git.example.com/acme/widget.git" }
Outbound: push events from your repos
Subscribe a URL to a repo:
curl -X POST https://api.weft.sh/v1/orgs/acme/repos/session-8412/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "url": "https://app.example.com/hooks/stratum" }'
The response includes the delivery secret — shown once. Every delivery is
signed the same way (X-Weft-Signature-256: sha256=<hmac over the body>);
verify before trusting.
You can do the same from the repository’s Settings → Push webhooks panel, which is also where the CI intake secret lives — the two are the two halves of one job. See CI integration for the whole loop.
The events
| Event | Fires when |
|---|---|
push |
Anything moves a ref: a git push over HTTPS or SSH, or POST …/commits |
change.landed |
A change lands, including one that landed by inclusion when a stack’s top landed |
change.ejected |
The lander refused a change and took it out of the queue |
Every delivery is { event, repo_id, payload }:
{
"event": "push",
"repo_id": "01jm…",
"payload": { "via": "api", "commit": "3f2a…", "branch": "main" }
}
The push payload depends on how the push arrived, and the common case
is the thin one. A git push carries { "via": "git" } and an SSH push
carries { "via": "ssh" } — no branch, no commit. Only POST …/commits
carries { "via": "api", "commit": …, "branch": … }, as above. So a
receiver that reads payload.branch works against the API path and
silently never fires for real pushes; treat a push as “something moved,
go and look” and fetch to find out what.
change.landed carries { change, commit, branch, patchset } (or
included_in in place of patchset when it landed by inclusion), and
change.ejected carries { change, verdict }.
Failed deliveries retry three times with backoff; delivery outcomes are
recorded and visible to support. List subscriptions with GET …/webhooks,
remove them with DELETE …/webhooks/{id}.