Integrations

Webhooks

Webhooks are in beta: event coverage and payload fields may still change. A webhook sends a signed HTTPS request to your server when something happens in a repository or organization, such as a push, a new issue or a merged pull request.

Add a webhook

Repository admins open Settings → Webhooks in the repository; organization owners and admins open Webhooks in the organization's settings.

  1. Choose Add webhook.
  2. Enter your receiver's HTTPS URL and select events. All events subscribes to every supported event. To test a new receiver first, select only webhook.ping.
  3. Save, then copy the signing secret into your receiver. It is shown only once.
  4. Choose Manage, then Send signed ping, and check Delivery history to confirm your receiver accepted it.
  5. Edit the webhook to add the events you need.

A repository or organization can have up to ten webhooks.

Through the API, use a personal access token. Apps and clients you connected with OAuth, such as MCP clients and apps acting as you, can list and delete webhooks but cannot create, edit or re-key one: a webhook keeps sending new data after you disconnect them.

Webhooks of installed apps

An app installed in your organization can receive events for the repositories you selected for it, if its manifest asks for them and you approved the installation. These webhooks belong to the installation: they do not appear under Webhooks, do not count toward the limit of ten, and you cannot edit, test or delete them there. Pausing the app stops its deliveries, resuming starts them again for new events, removing a repository from the app stops deliveries for that repository, and uninstalling the app removes its webhook. Deliveries still waiting when you do any of this are not sent, even if you resume the app or add the repository back. A delivery already being sent finishes and is recorded with the receiver's answer, but is not retried.

If you publish an app, see App webhooks for what your receiver gets.

From the terminal, pass an organization slug or owner/repo as the target:

sh
1cafe webhook create acme/project --url https://hooks.example.com/gitcafe \
2 --event issue.created --event pull.merged
3cafe webhook ping acme/project <webhook-id> --request-id first-ping --expected-version <version>

cafe webhook capabilities lists the supported events, and cafe webhook readiness checks whether a webhook is ready to send.

Events

EventsSent when
git.pushBranches or tags change by push, merge or sync
git.ref_created, git.ref_deletedBranches or tags are created or deleted
repo.default_branch_changedThe default branch changes
issue.created, issue.updated, issue.closed, issue.reopenedAn issue is created, edited, closed or reopened
issue.locked, issue.unlocked, issue.pinned, issue.unpinnedAn issue is locked, unlocked, pinned or unpinned
issue.assignees_updated, issue.labels_updated, issue.milestone_updatedAn issue's assignees, labels or milestone change
issue.linked, issue.unlinkedA pull request is linked to or unlinked from it
issue.comment_created, issue.comment_updated, issue.comment_deletedAn issue comment changes
issue.reaction_added, issue.reaction_removedA reaction is added or removed
pull.created, pull.updated, pull.closed, pull.reopenedA pull request is opened, edited, closed or reopened
pull.synchronizedA pull request's source branch gets new commits
pull.mergedA pull request merges in GitCafe
pull.drafted, pull.ready, pull.locked, pull.unlockedDraft or lock state changes
pull.reviewers_updated, pull.assignees_updatedReviewers or assignees change
pull.labels_updated, pull.milestone_updatedLabels or milestone change
pull.linked, pull.unlinkedAn issue is linked or unlinked
pull.review_submitted, pull.review_dismissedA review is submitted or dismissed
pull.comment_created, pull.comment_updated, pull.comment_deletedA pull request comment changes
pull.comment_hidden, pull.comment_unhiddenA comment is hidden or shown
pull.comment_resolved, pull.comment_unresolvedA review thread is resolved or reopened
pull.reaction_added, pull.reaction_removedA reaction is added or removed
label.created, label.updated, label.deletedA repository label changes
milestone.created, milestone.updated, milestone.deletedA milestone changes
ci.run_updatedA CI run's state changes
actions.workflow_jobAn Actions job on your organization's pool changes
webhook.pingYou send a test ping

Events describe changes made in GitCafe. Issues and pull requests imported from GitHub do not send issue.* or pull.* events, and past activity is not sent when you add a webhook.

Actions job events

actions.workflow_job tells an autoscaler when to start and stop runners. It is sent for a job that runs on one of your organization's pools, with an action of queued, in_progress (a runner took the job) or completed:

  • queued is sent once the job has a pool. A job that matches no pool yet sends it later, when a pool is bound.
  • in_progress and completed follow. A queued job that is cancelled or times out before a runner takes it sends completed with no runner.
  • Jobs on the instance's shared pool send nothing.

Each action is sent once per job, in order. workflowJob holds the job, run, pool and runner IDs, the run attempt, the job name, its labels, its status (the same as action), its conclusion once completed, and when it was queued, started and completed. It never contains scripts, environment variables, secrets, tokens, clone URLs or logs, and has no actor. The shape follows GitHub's workflow_job event but is not compatible with it. A receiver that missed deliveries reconciles with the pool's provider demand read.

The request

Each delivery is an HTTPS POST with a JSON body. The body has an event id, a type, a timestamp, the repository (its ID, its organization's ID, and its owner and name when the event happened, as in /api/repos/<owner>/<name>), the actor who made the change, and a snapshot of the resource after the change. Edit events include the fields that changed. Text bodies longer than 65,536 characters are cut and flagged as truncated.

HeaderContains
X-GitCafe-EventThe event type, same as the body's type
X-GitCafe-DeliveryThe delivery ID, the same across retries
X-GitCafe-TimestampWhen this attempt was signed, in Unix seconds
X-GitCafe-Key-VersionWhich signing secret was used
X-GitCafe-Signaturesha256= followed by the hex HMAC

The full payload schemas are in the webhooks section of the OpenAPI description.

Verify the signature

Before you trust a request:

  1. Read X-GitCafe-Timestamp and reject it if it is too old for your liking.
  2. Compute HMAC-SHA256 with your signing secret over the timestamp, a literal ., and the raw request body bytes.
  3. Compare sha256=<lowercase hex digest> with X-GitCafe-Signature using a constant-time comparison.
js
1import { createHmac, timingSafeEqual } from "node:crypto";
2
3function verify(secret, timestamp, body, signature) {
4 const expected = `sha256=${createHmac("sha256", secret)
5 .update(`${timestamp}.`)
6 .update(body)
7 .digest("hex")}`;
8 return (
9 expected.length === signature.length &&
10 timingSafeEqual(Buffer.from(expected), Buffer.from(signature))
11 );
12}

Respond and handle retries

Return any 2xx status once you have stored the event. GitCafe retries on network errors, timeouts (10 seconds), 408, 425, 429 and 5xx, honouring Retry-After up to an hour, for up to eight attempts within 24 hours. Other statuses and redirects end the delivery.

Deliveries can arrive more than once and in any order. Deduplicate by the body's event id.

Inspect and redeliver

Under Manage, Delivery history shows each delivery's state, attempts and HTTP status; View attempts and View payload show the details. For a delivery that finally failed, fix your receiver, then choose Request redelivery to send the same event again. Each event can be redelivered twice, within 30 days.

sh
1cafe webhook deliveries acme/project <webhook-id> --state failed
2cafe webhook redeliver acme/project <webhook-id> <delivery-id> \
3 --request-id retry-1 --expected-version <version> --event-created-at <timestamp>

Delivery history is kept for 30 days.

Rotate the signing secret

Choose Rotate signing secret under More actions, or run cafe webhook rotate-secret <target> <webhook-id> --yes. The new secret is shown once. From then on every attempt, including retries of earlier events, is signed with the new secret, so update your receiver straight away. X-GitCafe-Key-Version tells you which secret signed a request.

App webhooks

An app that lists capabilities.webhooks (an HTTPS url and the events it needs) in its manifest receives those events for every repository each installation selected. Each event needs read access to its payload in the manifest's permissions.autonomous; registration refuses one that lacks it. Apps cannot subscribe to all events with * or to webhook.ping.

The signing secret belongs to the app registration: it is shown once when you register the app, and POST /api/orgs/<publisher>/apps/<app>/webhook-secret/rotate replaces it for every installation at once and shows the new one once. Requests are signed and verified as above, and X-GitCafe-Key-Version names the secret version. The body also contains installation: { "id": ... }; deduplicate by the installation and event id together.

An app receives what its bot could read: the body of a hidden or deleted pull request comment, and of a deleted issue comment, is null. Events your app's own bot causes are not sent to that installation.

Apps also have events about their own bot, sent only to the installation whose bot is targeted and never to other webhooks. Each names the bot as bot: { "id": ... }.

EventsSent whenNeeds read access to
issue.bot_assigned, issue.bot_unassignedSomeone assigns or unassigns the bot on an issueIssues
pull.bot_assigned, pull.bot_unassignedSomeone assigns or unassigns it on a pull requestPull requests
pull.bot_review_requestedSomeone requests the bot's reviewPull requests
issue.bot_mentioned, pull.bot_mentionedA new comment @mentions the botThe subject and comments

Mentions count in new comments, inline comments and replies, outside code, up to 20 handles a comment; edits, review summaries and descriptions send nothing. The bot must be mentionable (capabilities.bot.mentionable).

Stop a webhook

Disable it to pause deliveries, or delete it under More actions. Both stop pending deliveries. Changing a repository's visibility or removing the person who created a webhook does not remove it; webhooks can carry private repository data, so review them when access changes.

Read as MarkdownGitCafe documentation
Documentation