Connections
Webhooks and the API
Send signed events to other systems with webhooks, check their signatures, and use the API with a personal access token.
Webhooks
A webhook sends a signed POST to an address of yours when something happens in a repository. Add one in the repository's Settings → Webhooks (repository admins):
- Add a webhook and give its payload URL.
- Give a secret, or leave it empty to have one made (shown once).
- Choose Everything, or Let me select individual events.
| Event | Sent when |
|---|---|
Pushes (push) | A branch or tag moves: a Git push, a commit from the browser, a submit, or an update from upstream |
Changes (change) | A change is created, gets a new revision, is merged or abandoned |
Reviews (review) | Someone approves, asks for changes, or withdraws their vote |
Comments (comment) | Someone comments on a change |
Releases (release) | A release is published |
Checks (status) | CI reports a check |
A ping is sent when you add a webhook, and whenever you choose Send a ping to test it.
Deliveries
Each webhook keeps a delivery log, with the payload, the response and its state. A delivery that fails (an error, or no reply) is retried, eight times over about 40 minutes, before it shows as Gave up. Choose Send again to retry one by hand.
Webhooks to private network addresses are refused unless your admin sets INTEGRATIONS_ALLOW_PRIVATE=true.
Checking the signature
Every delivery has these headers:
X-Fettlecode-Event: the event, such aschange.X-Fettlecode-Delivery: an id for the delivery, the same on retries, so you can ignore repeats.X-Fettlecode-Signature-256:sha256=and the HMAC-SHA256 of the body, with the webhook's secret as the key. The same value is inX-Hub-Signature-256, so code written for GitHub webhooks works unchanged.
In Node.js:
import { createHmac, timingSafeEqual } from 'node:crypto';
function signedByFettlecode(rawBody, header, secret) {
const expected = `sha256=${createHmac('sha256', secret).update(rawBody).digest('hex')}`;
return header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));
}
Check the raw body, exactly as it arrived, before parsing it.
The API
Fettlecode's web app uses an HTTP API at /api, which scripts can use too, with a personal access token:
curl https://code.example.com/api/repos/wren/bookings/changes \
-H "Authorization: Bearer $FETTLECODE_TOKEN"
A token acts as you, with your access. The commit status API, for reporting CI results, is described in CI and checks. The API isn't versioned yet, so it may change between releases; the webhook payloads and the status API are the stable parts.
For AI tools, use the MCP server instead.