Skip to content

Webhooks ​

Webhooks send a JSON POST when a subscribed event occurs. A webhook can cover the whole organization or one app. Configuration requires a GitHub dashboard session with the admin role; API tokens cannot manage webhooks.

All paths begin with /api/v1/orgs/:orgId/webhooks.

Method and suffixSuccessResult
GET /200{ "items": [webhook, ...] }
POST /201Created webhook and one-time secret
GET /:webhookId200One webhook
PATCH /:webhookId200Updated webhook
POST /:webhookId/test200Delivery test result
POST /:webhookId/revoke200Revoked webhook
DELETE /:webhookId204Empty body

List results are newest first and include revoked webhooks. A webhook has id, orgId, nullable appId, name, nullable description, url, preset, events, payloadTemplate, active, lastDeliveryAt, lastDeliveryStatus, createdAt, and revokedAt. Delivery status is success, failure, or null before the first attempt. An unknown ID in the organization returns 404 webhook_not_found.

Create a webhook ​

http
POST /api/v1/orgs/org_123/webhooks
Content-Type: application/json

{
  "name": "Release receiver",
  "url": "https://hooks.example.com/releases",
  "preset": "generic",
  "events": ["release.created", "release.promoted"],
  "appId": "app_456"
}

name is 1–80 characters. description is optional, nullable, and at most 500 characters. events must contain at least one event from the catalog below. appId is optional; omit it for every app in the organization. If supplied, it must belong to this organization (404 app_not_found). preset defaults to generic; the other choices are slack and discord. A newly created webhook is active. The response includes a secret value for a generic webhook and null for Slack or Discord. Save a generic secret when it is returned: later reads do not expose it.

EventWhen it is sent
release.createdThe first artifact creates a release
release.promotedA release is promoted to a channel
release.revokedA release is revoked
release.rolled_backA channel is rolled back to a previous release
play.status_changedA Google Play publication changes status
asc.status_changedAn App Store Connect publication changes status
feedback.createdA tester submits feedback (payload has the kind, a message excerpt, attachment count and a feedbackUrl dashboard link; no image data)

Adding another artifact to an existing release does not create another release.created event. Use the event name and IDs in the payload to look up current state when you need more detail.

Payload and signature ​

The generic preset, with no custom template, sends a body shaped like this:

json
{
  "event": "release.promoted",
  "data": {
    "releaseId": "rel_789",
    "appId": "app_456",
    "channelId": "chn_stable",
    "channelName": "stable",
    "rolloutPercent": 100
  },
  "timestamp": "2026-09-29T12:00:00.000Z"
}

The actual data object may include more display fields, such as the app name, version, release notes, and download URL. Its event-specific fields vary. Treat the JSON sample as an illustration of shape rather than a fixed exhaustive schema.

Generic deliveries include X-ReleaseDesk-Event and X-ReleaseDesk-Signature: sha256=<hex>. The hex value is HMAC-SHA256 over the raw request body bytes, using the secret returned when the webhook was created. Verify against the body before parsing or changing its whitespace. Compare the computed and received digest with a constant-time comparison. Slack and Discord presets use their URL as the credential and do not receive this ReleaseDesk HMAC header.

All deliveries have Content-Type: application/json. A custom payloadTemplate can replace the JSON body for any preset. It must be at most 4,000 characters and render to a JSON object with sample event data. Placeholders use double curly braces:

text
{{event}}  {{timestamp}}  {{data.releaseId}}  {{data.channelName}}  {{data.variant}}

String values are substituted with JSON-escaped text; missing fields become empty strings. Keep string placeholders inside JSON string literals. The built-in Slack and Discord presets use formatted message templates when no custom template is supplied.

Change, test, and stop a webhook ​

PATCH /:webhookId accepts any subset of name, description, url, events, payloadTemplate, appId, and active. Send appId: null to make a webhook organization-wide. Send payloadTemplate: null to return to the selected preset's default body. The preset and signing secret cannot be changed after creation; create a new webhook if either must change.

POST /:webhookId/test sends a synthetic release.promoted event, including test: true in its data, to the configured URL. It works even when active is false. The response is { "ok": true, "status": 200, "error": null } for a successful receiver; an HTTP failure gives ok: false with its status, and a network failure gives status: null with an error string. This request records the delivery result on the webhook. Check the receiving service as well as the API result before relying on it.

Ordinary event delivery is best effort. ReleaseDesk waits up to five seconds for the receiver and records the last attempt as a success or failure. A receiver outage does not roll back a release promotion or other initiating operation. There is no delivery history or retry endpoint in this API; lastDeliveryAt and lastDeliveryStatus show only the latest attempt.

Setting active: false pauses matching for future events. POST /:webhookId/revoke also sets active to false and records revokedAt; repeating it returns the existing revoked record. DELETE /:webhookId removes the configuration altogether. Use revoke when you want to retain its configuration and latest delivery status for review.