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 again

URLs 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

EventWhen
pushA commit landed that changes paths this slice includes, whichever slice authored it.
tag.createdA tag (release) was created. Re-creating the same tag is not an event.
changeset.createdA changeset was opened in the slice.
changeset.updatedA new patchset was uploaded.
changeset.approvedA changeset was approved.
changeset.submittedA changeset landed; the commit and its changed paths are included.
changeset.abandonedA changeset was abandoned.
check_run.completedA check finished, with its name and status.
*Every event above.
pingSent 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-ID to 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"