Skip to main content
Webhooks allow you to receive notifications of changes to your organization’s repositories in real time. A webhook target represents an https:// URL to which Mesa will send these notifications, in the form of HTTP POST requests. Each target is scoped to your organization, can subscribe to multiple event types, and can optionally be restricted to a subset of repositories.

Create a webhook target

Store target.secret in your secret manager. The SDK and REST API only return it once, here on create. The dashboard always shows it on the target’s detail page, and you can also rotate to mint a new one.

Supported events

Subscribe to any combination of the following on a target: If events is omitted on create, it defaults to ["push"].
Managing webhook targets requires the admin scope. Repo-scoped tokens can only manage targets whose repo_ids is a subset of the repos the token has access to. They cannot create org-wide targets.

List webhook targets

Paginated. limit is at most 100 and defaults to 100. Pass the previous response’s next_cursor to fetch the next page; has_more indicates whether another page is available.

Update a webhook target

PATCH uses full-replace semantics on each field you include. Omitted fields are left unchanged.
  • events: providing an array replaces the current list of subscribed events in full. There is no incremental add/remove.
  • repo_ids: providing an array replaces the current repo filter. Pass null to clear the filter and make the target org-wide. Empty arrays are rejected.
The secret is not returned by update. See Rotate the signing secret below.

Delete a webhook target

Payload shape

Every delivery shares the same envelope:

push event data

updates[].action is created, updated, or deleted. before is null when the action is created; after is null when the action is deleted.

repo.* event data

before and after are only included on repo.updated. repo.created and repo.deleted carry just repo.

bookmark.* event data

from is only included on bookmark.moved. bookmark.created, bookmark.deleted, and bookmark.merged carry just bookmark.

change.* event data

previous_current_commit_oid is only included on change.evolved. change.created carries just change.

sync.* event data

status reflects the run’s state at the moment the event fires: queued on sync.queued, in_progress on sync.in_progress, and completed or failed on the corresponding terminal event. refs is the ref-selection policy for the run. error is populated on sync.failed. stats is populated once the worker finishes ingestion or reconciliation.

Webhook headers

  • Content-Type: application/json
  • User-Agent: Mesa-Webhook/1.0
  • X-Mesa-Event: <event_type>
  • X-Mesa-Delivery: <delivery_id>
  • X-Mesa-Signature: t=<unix>,sha256=<hex>

Verify signatures

The signature is computed as:
In your receiver, also reject deliveries whose t is more than a few minutes old (5 minutes is a common choice). This stops an attacker from replaying a captured request later. The signature stays valid until you rotate the secret. Time alone doesn’t invalidate it.

Rotate the signing secret

To rotate, open the webhook target’s detail page in the dashboard (Webhooks → click the target) and click Regenerate in the Signing secret section. Confirm the dialog.
Rotation takes effect immediately:
  • Mesa starts signing every subsequent delivery with the new secret.
  • Deliveries will fail signature verification at your receiver until you update it with the new value.
To minimize the failure window, copy the new secret and update your receiver as soon as you rotate. Rotation requires the admin scope, same as create, update, and delete. See the note at the top of this page.

Delivery behavior

Mesa fires webhooks asynchronously, with a 10-second timeout per request. Receivers should respond fast and defer long-running work to a background queue. Anything longer than 10s is treated as a failure. There is no automatic retry. A failed delivery is logged but not redelivered. If you need at-least-once semantics, queue the work yourself the moment you receive the request. The X-Mesa-Delivery header is unique per delivery. Use it as an idempotency key on the receiver.

Local development

Expose your local webhook listener using a tunnel like Tailscale or Cloudflare Tunnel, then use the public URL when creating the webhook.