Gitslice docs
Webhooks
A webhook POSTs a JSON event to your HTTPS endpoint when something happens in a slice, so a build, a deploy or a chat bot can react without polling.
Add a webhook
Webhooks belong to a slice, and only the slice's owners and admins manage them. In the web app, open the slice's Settings and use the Webhooks panel. From the CLI:
# the secret is read from stdin so it stays out of your shell history
printf %s "$SECRET" | gs webhook create --slice acme/payment \
--url https://example.com/gitslice --event tag.created --event push --secret-stdin
gs webhook ping <webhook-id> # send a ping now
gs webhook deliveries <webhook-id> # what was sent and how it went
gs webhook redeliver <delivery-id> # send one againURLs must be https:// and reach a public address. A slice can have up to 20 webhooks. The secret is stored encrypted and never shown again. Query values in a URL (often a token) are shown as ... in lists.
Events
| Event | When |
|---|---|
| push | A commit landed that changes paths this slice includes, whichever slice authored it. |
| tag.created | A tag (release) was created. Re-creating the same tag is not an event. |
| changeset.created | A changeset was opened in the slice. |
| changeset.updated | A new patchset was uploaded. |
| changeset.approved | A changeset was approved. |
| changeset.submitted | A changeset landed; the commit and its changed paths are included. |
| changeset.abandoned | A changeset was abandoned. |
| check_run.completed | A check finished, with its name and status. |
| * | Every event above. |
| ping | Sent when you ask (Ping, or gs webhook ping), even to an inactive webhook. |
Payload
Every body has the same envelope, plus a section for the event: tag, changeset, commit or check_run.
{
"id": "evt_…",
"event": "tag.created",
"created_at": "2026-10-08T16:00:00Z",
"slice": {
"account": "acme", "name": "payment", "full_name": "acme/payment",
"url": "https://gitslice.io/slices/acme/payment"
},
"sender": { "username": "ann" },
"tag": { "name": "v1.2.0", "commit_id": "sha256:…", "message": "Release v1.2.0" }
}A changeset section has its id, handle, title, status, author, target ref, patchset and a link. A push to another slice describes only the commit and the changed paths inside that slice.
Headers and signatures
X-Gitslice-Event- The event name, such as tag.created.
X-Gitslice-Event-ID- The event id. Retries and redeliveries keep it, so drop duplicates by it.
X-Gitslice-Delivery- This delivery's id.
X-Gitslice-Hook-ID- The webhook's id.
X-Gitslice-Signature-256- sha256= and the hex HMAC-SHA256 of the raw body, keyed by the secret (only when one is set).
To check a delivery, compute the HMAC of the raw request body with your secret and compare it to the header in constant time:
// Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(secret, rawBody, header) {
const want = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
return header?.length === want.length && timingSafeEqual(Buffer.from(header), Buffer.from(want));
}Delivery and retries
- Success: any 2xx answer within 10 seconds. Redirects are not followed.
- Retries: a failed delivery is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 12 hours, then marked failed. Redeliver it any time from the delivery log.
- At least once, any order: the same event can arrive twice and events can arrive out of order, so use
X-Gitslice-Event-IDto drop duplicates. - History: deliveries, with their request and response bodies, are kept for 30 days.
Example: release on tag
Gitslice builds its own gs releases this way: the gitslice/gitslice slice sends tag.created to a Cloud Build webhook trigger, which reads the tag name from $(body.tag.name) and builds it. Creating a tag is all it takes:
gs tag create v0.5.0 --slice gitslice/gitslice -m "Release v0.5.0"
Gitslice