Integrations

Webhooks

Webhooks are in beta. Event coverage and payload schemas may evolve; the supported events and delivery limits are described below.

Outbound webhooks notify receivers of supported issue, pull-request, catalog, CI and Git changes. Subscribe to individual names below or *. Other syntactically valid subscription names remain accepted for configuration compatibility but produce no deliveries. Provider issue/PR imports do not emit local lifecycle events. Supported native Git settlement paths are listed below. Issue deletion, transfer and comment moderation events are not available. No historical backfill is provided. Administrators can queue signed pings and bounded manual redelivery as described below.

Supported catalog, repository and CI events

EventCommitted change
label.created, label.updated, label.deletedLocal label lifecycle
milestone.created, milestone.updated, milestone.deletedLocal milestone lifecycle, including state changes
repo.default_branch_changedNative-confirmed default-branch metadata settlement
ci.run_updatedAccepted authenticated Buildkite run observation

Catalog events include the resulting public snapshot; deletion retains the last snapshot. Updates also include previous values from the owning transaction. These events describe the catalog object rather than emitting an event for every assigned issue or PR. Default-branch events include the old/new branch and resulting configuration generation. CI events include the run/check identities, version, commit, ref, state, context, external URL and execution timestamps. They omit a human actor because a provider observation does not establish one. Duplicate or stale provider observations and replayed default-branch requests emit nothing.

Supported Git activity events

EventSuccessful native publication
git.pushPublic branch/tag ref changes from a local push, branch operation or merge
git.ref_createdA batch of public branches/tags created by that operation
git.ref_deletedA batch of public branches/tags deleted by that operation, including committed merge cleanup

Git activity includes HTTP and SSH pushes, browser branch creation, deletion and restoration, committed local merges and committed source-branch cleanup. It also includes settled mirror fetches, GitHub native synchronization/imports and restack ref writes. Fork copying and direct upstream synchronization remain excluded until their native effect owners retain durable publication evidence. Capture uses the exact successful native receipt and the database publication transaction; a request being accepted or a native result being uncertain emits nothing. Recovery reaches the same publication owner, and replay adds no event. A committed merge can emit Git activity even if its PR diverged before publication. Unrelated native observations do not emit Git activity. Provider issue/PR metadata imports remain outside local resource lifecycle events.

The git snapshot identifies operationId, source (push, merge, merge_cleanup, mirror, github_sync or restack) and native authorityOrdinal. Each refUpdates entry contains the full public ref name, exact nullable beforeOid/afterOid, and created/deleted flags. forced is present only when the native receipt establishes that fact; absence does not mean a fast-forward. Creation has a null previous OID; deletion has a null resulting OID. Commit lists and file changes are not enumerated.

Each event includes up to 100 matching public ref updates. refUpdatesCount counts all matching public updates for that event; refUpdatesTruncated explicitly indicates an incomplete list. A mixed durable publication emits at most three events, regardless of ref count. A native GitHub batch can settle several per-ref jobs; each job publishes its own owned ref changes and has its own operation ID. git.ref_created and git.ref_deleted are batch events, not one delivery per ref. Internal scratch, retained-deletion and cross-fork pin refs are omitted before counting; an operation touching only internal refs emits nothing. Subscribers to * can receive both the push summary and its creation or deletion batches. Deduplicate individual webhook events by their signed event ID; use operationId to correlate those distinct events.

Supported pull-request events

EventSuccessful local operation
pull.created, pull.updatedCreate or edit a pull request
pull.synchronizedAn authoritative observation changes the PR head
pull.closed, pull.reopenedClose or reopen a pull request
pull.drafted, pull.readyMark as draft or ready
pull.locked, pull.unlockedLock or unlock
pull.reviewers_updated, pull.assignees_updatedReplace reviewers or assignees
pull.labels_updated, pull.milestone_updatedReplace labels or assign/clear milestone
pull.linked, pull.unlinkedUpsert or remove an issue link
pull.comment_created, pull.comment_updated, pull.comment_deletedCreate, edit or delete a published comment
pull.comment_hidden, pull.comment_unhiddenHide or unhide a published comment
pull.comment_resolved, pull.comment_unresolvedResolve or unresolve a published thread
pull.reaction_added, pull.reaction_removedAdd or remove a PR or published-comment reaction
pull.mergedPublish a completed local native merge, including a landed stack member
pull.review_submitted, pull.review_dismissedSubmit or dismiss a published review

Pull payloads include the immutable ID, number and title, description, resulting state/draft/version, author identity, source repository and source/target branches, and the observed head OID when available. The envelope identifies the performing actor.id; it can differ from the resource author. Edit events include changes with the submitted fields. Assignment events include the resulting actor IDs; label and milestone replacements include their IDs and names/titles. Link events identify issueId and linkType; the same transaction emits the issue event. Replayed creation requests and rejected lifecycle transitions emit nothing. Accepted edits and assignment replacements notify even when values are equal.

Comment events retain commentId and a published comment snapshot including body, author, version, thread/review identity, inline location, and moderation state. Deletion retains the comment's pre-deletion content. Review events retain identity, author actor ID, verdict, reviewed commit OID, published body and version; dismissal includes its reason. Submission includes inlineCommentCount, inlineCommentIds and bounded inlineComments with public body/location snapshots. The snapshot batch is bounded to 64 KiB of serialized JSON; inlineCommentsTruncated marks omitted content, and individual body truncation remains explicit. Submission emits one summary for the review and its published draft inline comments, without separate comment-created events for those comments. Private drafts, review request replays and duplicate reaction additions emit nothing. Reaction events include subject identity, emoji, and the reaction/actor identity when available. Merge events include merge.strategy, merge.sourceOid, merge.targetOidBefore and merge.resultOid. They notify only after exact native committed proof and successful PR/operation publication; each landed stack member emits individually. An accepted merge request, uncertain native result, failed/rolled-back publication, a diverged PR and completed-operation replay emit nothing. If ownership changes before publication, recipients use the repository's current locked organization. Provider merge projections remain unavailable. pull.synchronized includes synchronization.beforeOid, afterOid, source and the observed authority ordinal watermark. It describes an observed head change, not proof of the reader causing that Git write; it omits the actor. Cross-fork destination heads require the existing authorized native pin/refresh workflow: a source push does not eagerly notify a PR-only destination subscription. Base-only and unchanged observations emit no synchronization event.

Supported issue events

EventSuccessful local operation
issue.createdCreate an issue
issue.closedClose an open issue as completed or canceled
issue.updatedUpdate issue metadata
issue.reopenedReopen a closed issue
issue.locked, issue.unlockedLock or unlock an issue
issue.pinned, issue.unpinnedPin or unpin an issue
issue.assignees_updatedReplace assignees
issue.labels_updatedReplace labels
issue.milestone_updatedAssign or clear a milestone
issue.linked, issue.unlinkedUpsert or remove an issue's pull-request link
issue.comment_created, issue.comment_updated, issue.comment_deletedCreate, edit or delete an issue comment
issue.reaction_added, issue.reaction_removedAdd or remove a reaction on an issue or issue comment

Events describe committed mutations, rather than a before/after difference. Metadata, assignment, link-upsert, comment-edit, lock, unlock and unpin operations that advance a version still emit when the requested value equals the old value. An already-pinned issue and an already-present reaction return without inserting an event. Rejected writes, missing-link removals and rolled-back transactions produce none. Closing again does not emit issue.closed; reopening has its own event.

Issue snapshots contain ID, number, title, description, resulting state/version, priority, due date, milestone ID, lock/pin state and closure/lock reasons. Edit events include submitted changes; assignment replacements include the resulting assignee or label IDs, with label names and milestone identity/title where applicable. Links retain the PR identity and link kind. Comment events include commentId and the immutable body, author, version and timestamps, including content removed by deletion. Reactions retain their subject, emoji and identity.

Snapshots describe the resource at capture time, not its current state or a complete before/after diff. Treat supplied replacement lists as complete values, including empty lists and explicit null when clearing a milestone. The performer's actor.id is distinct from authorship. New captures include enriched fields; previously queued version 1 payloads can omit them and retain their exact original bytes on retry or manual redelivery. Consumers must tolerate those older payloads. Bodies include up to 65,536 characters. Larger imported PR descriptions, comments or reviews retain the first 65,536 characters and set descriptionTruncated or bodyTruncated; ordinary local content is complete. Webhook administrators should treat delivered content as repository data, including deleted or moderated published comments. Credentials and private review drafts are excluded.

Delivery requires configured encryption and an operator-approved outbound transport. The default trusted_origins transport supports only exact operator-trusted HTTPS origins in WEBHOOK_ALLOWED_ORIGINS, a comma-separated list such as https://hooks.example.com,https://events.example.org. Origins must use port 443 and contain no path, query, credentials, wildcard, IP literal or local hostname. An empty list disables HTTP delivery execution in this mode. Queued rows remain pending within their age budget; scheduled expiry still runs while sending is disabled. Customers can save URLs outside the list, but those destinations cannot be sent by this transport. Operators must control the trusted domains and their DNS destinations; this restriction does not establish public-IP connection enforcement or DNS-rebinding protection. Instances may explicitly configure WEBHOOK_TRANSPORT_MODE=public_https to use an HTTP/1.1 sender over existing native TLS sockets instead of ordinary fetch. This mode permits HTTPS domain receivers on port 443 when the origin list is empty; a nonempty list further restricts it during rollout. It requires a qualified runtime enforcing the public-only socket destination policy; private/mixed DNS answers are rejected before connection. Redirects are not followed, response headers are bounded and connections close after each attempt. Public mode is opt-in and requires qualification on your instance. An active endpoint alone does not prove successful delivery. This page describes the implementation contract; deployed availability and transport qualification require evidence from your instance's release.

This API is separate from the incoming Buildkite integration.

Access and endpoint paths

Organization administrators use /api/orgs/:slug/webhooks. Repository administrators use /api/repos/:owner/:repo/webhooks. Credentials also need the corresponding organization or repository webhooks/read or webhooks/write scope. An endpoint ID belongs to the organization or repository where it was created.

Each repository or organization can have up to ten endpoints, including inactive ones. Creating another at the limit returns 400 without saving an endpoint or key. Delete an endpoint to free a slot. Existing configurations remain editable even if the owner already exceeds this limit.

The following paths are relative to the chosen base path:

OperationRequestResponse
ListGET /?limit=50&after=<next>{ items, next }
ReadGET /:idEndpoint metadata
CreatePOST / with url, events, optional active and secret201 { endpoint, secret }
UpdatePATCH /:id with expectedVersion and changed url, events, or activeUpdated metadata
DeleteDELETE /:id with expectedVersion{ id }
Rotate secretPOST /:id/rotate-secret with expectedVersion{ endpoint, secret, previousExpiresAt }
DeliveriesGET /:id/deliveries?limit=50&after=<next>{ items, next }
PingPOST /:id/ping with requestId, expectedVersion202 { requestId, deliveryId, eventId, eventCreatedAt }
RedeliverPOST /:id/deliveries/:deliveryId/redeliver with requestId, expectedVersion, eventCreatedAtSame durable receipt
AttemptsGET /:id/deliveries/:deliveryId/attempts?eventCreatedAt=<ISO timestamp>{ items, next }

Browser setup and inspection

Repository administrators can open /<owner>/<repo>/settings/webhooks; organization owners and admins can open /orgs/<handle>/settings/webhooks. Choose Webhooks in Settings. The page requires settings access and scoped webhook access, even when other repository metadata is publicly readable. If adding is disabled because signing secrets are not configured, ask the instance administrator to complete that prerequisite first. Sending may remain disabled while you save endpoint configuration.

  1. Choose Add webhook, enter the HTTPS receiver URL and select events. All events subscribes to the supported catalog. For an initial receiver check, select only webhook.ping. Save the configuration.
  2. Copy the shown-once signing secret into your receiver, then dismiss it. Creation and Rotate signing secret show the key locally; reloading or leaving the page loses it. If you lose a key, rotate it and update the receiver.
  3. Choose Manage for the receiver. Resolve any visible configuration prerequisites; Ready to send means configuration is ready, not that the receiver has accepted a delivery. Expand Configuration checks for details. Choose Edit webhook to change configuration; More actions contains secret rotation and deletion.
  4. Choose Send signed ping, then inspect Delivery history. A queued receipt confirms durable admission. Refresh deliveries observes progress and View attempts shows retained HTTP outcomes. Pages load more history on request rather than continuously polling.
  5. For an eligible terminal original, choose Request redelivery. Recovery keeps the original event and body; the limits below still apply. Copies cannot be redelivered through the page.

If a ping or redelivery response is uncertain, choose Retry same request to recover its receipt. This retains the original request identity and expected version. Avoid starting another operation to work around an uncertain result. If endpoint creation is uncertain, refresh the webhook list and check for the saved endpoint before creating another. Creation has no equivalent request-ID replay guarantee.

Edit webhook retains the version originally opened for editing. A background refresh does not silently replace that draft. If another administrator changed the endpoint, cancel and reopen the editor to review the current configuration before saving again. Delete webhook requires confirmation and stops pending deliveries; it cannot retract a request already accepted by the receiver.

Save and update configuration

Provide an HTTPS URL without embedded credentials or a fragment, and a nonempty list of distinct event names. Saving validates configuration and does not contact the receiver. Delivery also depends on destination policy and current source repository/organization identity and liveness. Endpoints are persistent organization/repository administration settings; they can receive private repository events. A visibility change or the creator losing their role does not remove an endpoint. Administrators disable or delete it to stop new sends.

Read the latest endpoint version and send it as expectedVersion when updating, deleting or rotating a secret. A stale version returns 409. Read the endpoint again, review the current values, then retry your intended change.

Signing secrets

Creation and rotation return the signing secret once. Save it securely before closing the response; subsequent reads never return it. Supply a secret of 16–128 characters or omit it to generate one.

Rotation replaces the current secret. The immediately preceding encrypted secret becomes eligible for cleanup after 24 hours; the next rotation removes it sooner. The response includes previousExpiresAt. Scheduled cleanup is bounded, so physical removal may lag that time. Every attempt uses the endpoint's current signing key. Rotation preserves the immutable event body and delivery identity, while the timestamp, signature and key-version header change on a retry. Install the new key at your receiver before removing the old key.

If your instance cannot securely store secrets, creation and rotation fail without saving the requested change. Ask the instance operator to configure secret storage. Reading, updating and deleting existing endpoint configuration remain available.

Receive and verify an event

The request is an HTTPS POST with Content-Type: application/json. Version 1 includes an event ID, a supported type, its capture timestamp, repository and organization IDs, and a resource snapshot. New issue captures include description text; comment and review captures include published content. Optional fields may be absent in older retained events. Closure events include issue.state as completed or canceled. The exact serialized UTF-8 body remains unchanged across attempts for that delivery.

Read X-GitCafe-Timestamp as Unix seconds and X-GitCafe-Key-Version as the signing-key generation. Compute HMAC-SHA256 with that key over the timestamp string, a literal ., and the unchanged request body bytes. Compare X-GitCafe-Signature with sha256=<lowercase hexadecimal digest> in constant time. Reject timestamps outside your receiver's replay window. Verify before parsing or accepting the event.

X-GitCafe-Event matches the body's type. Deduplicate by the signed body's event id within your receiver subscription. X-GitCafe-Delivery identifies delivery history and remains stable across retries; headers other than the timestamp are not covered by the signature. Store acceptance durably before returning a 2xx response. A receiver may accept a request whose response is lost, so retries can duplicate accepted requests. No delivery ordering is guaranteed.

Delivery failures and inspection

Any 2xx response acknowledges acceptance. Network failures, timeouts, 408, 425, 429 and 5xx responses are retryable; redirects and other statuses terminate the delivery. Attempts stop after eight attempts or 24 hours from event capture. Backoff is bounded and honors Retry-After up to one hour. Each attempt has a 10-second timeout. Receiver response bodies are cancelled without reading or retaining them. Missing sender encryption configuration retries within the same budget rather than reporting success. With trusted-origin execution disabled by an empty allowed-origin list, no attempt is made. Scheduled maintenance expires at most twenty aged queued or expired-lease running deliveries per invocation, preserving live leases and unknown attempt evidence. This is bounded cleanup rather than a guarantee that an entire backlog terminalizes exactly at 24 hours. Guarded history pruning follows expiry; see History maintenance below for retention limits.

Use the delivery list to inspect state, attempt count, retry time, HTTP status and redacted error category. For attempt inspection, pass the delivery's exact eventCreatedAt; it identifies the partition together with deliveryId. Attempt outcome started means no conclusive settlement is retained, including after a worker crash or expired lease. History lists omit payloads, signing headers, receiver bodies and secrets. Open View payload for a delivery to lazily fetch its original immutable JSON; this includes published content later deleted or moderated. Inspection requires current webhook read authority and a live source still belonging to its captured organization. Moved/deleted sources are unavailable. The response is private and not cached; signing headers and receiver bodies are never returned.

The API exposes GET .../deliveries/:deliveryId/payload?eventCreatedAt=.... For push-sourced Git events, GET .../deliveries/:deliveryId/ref-updates with the same timestamp returns retained public ref updates in indexed pages. Supply after from next and a limit of 1–100. Pages follow public ref name order; private refs and their positions are excluded. Creation/deletion filtering can produce an empty public page with a next continuation. Creation/deletion events return only their matching refs. Mirror, sync, merge and restack continuation is not supported by this endpoint. Delivery and attempt history use their own next cursors.

Fix receiver or instance configuration when a delivery is terminal or dead-lettered, then use the bounded recovery procedure below for retained events. Endpoint edits, disabling, deletion or lost source authority can cancel pending deliveries; these actions cannot retract a request already accepted by a receiver. Deleting an endpoint preserves durable delivery evidence but removes that endpoint's public history path.

For management and outgoing request schemas and examples, open /api/openapi on your instance. The JSON document is available at /api/openapi/json; outgoing events are modeled under its OpenAPI 3.1 webhooks section.

Inspect readiness and history with Cafe

Pass an organization slug or owner/repository as the webhook target. Inspect capabilities before selecting events, then check the configured endpoint:

sh
1cafe webhook capabilities acme/project
2cafe webhook readiness acme/project <webhook-id>
3cafe webhook deliveries acme/project <webhook-id> --state failed
4cafe webhook dead-letter acme/project <webhook-id>
5cafe webhook attempts acme/project <webhook-id> <delivery-id> --event-created-at <exact-eventCreatedAt>

GET /capabilities and GET /:id/readiness are relative to the organization or repository webhook base path. They require the same webhook read authority as endpoint management. Capabilities list supported event names and API version, plus encryption configuration and execution state (disabled, invalid or configured). Readiness additionally reports endpoint activation, destination admission, version support and unsupported subscription names. ready means these configuration checks permit an attempt with at least one supported subscription; it does not prove network reachability or receiver acceptance. These reads make no outbound request and do not expose operator origin lists or signing keys. Unsupported names remain stored for compatibility.

Delivery history accepts an optional state query filter. CLI --cursor carries the returned next; --limit bounds the page. Dead-letter inspection is a filtered history read, not a separate queue. Attempt inspection requires the exact eventCreatedAt from delivery history. Outcome started can represent an unknown result after a crash; no conclusive receiver acceptance is recorded. Ping and redelivery return durable admission receipts; use delivery history to inspect receiver outcomes.

Signed ping and manual redelivery

These operations require organization or repository administrator authority and webhooks/write scope. The endpoint must be active, its destination permitted, and instance encryption and sending configured. Read readiness and the current endpoint version first. You can configure an active endpoint with only the webhook.ping subscription while testing, then add business subscriptions after receiver acceptance. Ping does not require a matching subscription and only notifies the endpoint you select.

Queue a ping with a stable request ID and the observed endpoint version:

sh
1cafe webhook ping <org-or-owner/repo> <endpoint-id> \
2 --request-id receiver-check-1 --expected-version <version>

The signed webhook.ping body includes its event ID, version, time, endpoint ID and a strict owner: either { kind: "repo", repoId, orgId } or { kind: "org", orgId }. An organization needs no repository to test its endpoint. Ping uses the same signing, timeout, lease and retry behavior as business notifications. Each endpoint admits at most one new ping per minute and cannot admit another while a ping is queued/running.

A 202 response or CLI receipt means the intent was committed, not that the receiver accepted it. Use deliveryId and eventCreatedAt from the receipt to inspect deliveries and attempts. Retry a lost response with the same request ID and exact input to receive the same receipt. Reusing that ID for a different action, version or original delivery returns a conflict. Recovery keys expire at the underlying event's thirty-day history horizon; a retired request is not a promise of indefinite replay.

To redeliver, find the terminal original delivery (deliveryOrdinal: 0, shown as cycle 0 in CLI history) and its exact eventCreatedAt, then submit:

sh
1cafe webhook redeliver <org-or-owner/repo> <endpoint-id> <original-delivery-id> \
2 --request-id repair-1 --expected-version <version> \
3 --event-created-at <exact-history-timestamp>

Manual redelivery creates a new delivery ID and attempt history while retaining the original event ID, exact body bytes, event timestamp and captured URL. The original state and uncertain attempts remain intact. It signs with the current key; rotation does not regenerate the body. There are at most two additional cycles per event and endpoint, each with eight attempts and a 24-hour budget. Only original deliveries can enroll a cycle; redelivering a copy cannot bypass the cap. Another cycle cannot be admitted while that event has queued/running work. The original event must remain retained and be less than thirty days old.

The current endpoint must still admit that event, use the captured URL and own the live source. Restore a changed URL or subscription before retrying; a moved or retired source cannot be revived through redelivery. A succeeded original may be redelivered, but a receiver deduplicating the stable event ID can acknowledge it without processing it again. This operation cannot force repeated processing.

For a conflict, reread endpoint version, original delivery identity, source and history before deciding whether a new request ID is appropriate. For admission failure, check readiness, endpoint activation, pending work, the ping cooldown and cycle limit. Do not automatically generate new request IDs when retrying an uncertain API response.

History maintenance

The API minute schedule expires aged work and prunes eligible webhook history older than thirty days even when sending is disabled. Pending consumer work, queued/running deliveries and oversized legacy groups keep their evidence. Cleanup selects indexed due events, without enumerating repositories. Guarded groups are deferred for an hour before another check. Each pass examines at most 100 events and processes at most twenty groups with delivery history; each group is limited to sixty deliveries and 480 attempts. Cleanup is gradual; thirty days is an eligibility threshold, not an exact removal deadline. Removed history is no longer available in delivery/attempt inspection.

Operators can inspect API logs for [api] webhook history retirement. The summary reports selected candidates, retired events, guarded candidates and removed delivery/attempt rows; it contains no destination, payload or secret. The summary also counts expired recovery keys and encrypted retiring signing keys removed by bounded maintenance. If old history stays, check cron failures, pending consumers and nonterminal deliveries first; expiry must drain aged work before retirement can remove it. Legacy histories exceeding sixty deliveries or 480 attempts per event need separately reviewed maintenance. Do not detach partitions or remove pending evidence as a recovery shortcut. Row pruning permits vacuum reuse; it does not promise filesystem space recovery.

Before enabling a release, verify a controlled receiver through actual Worker transport and queue/cron execution: successful signed delivery, retry and lost response, lease recovery, key rotation, endpoint/source changes, and expiry and history pruning with sending disabled. Local tests do not establish deployed receiver acceptance or large-backlog scan cost.

Read as MarkdownGitCafe documentation
Documentation