Outbound webhooks let a workspace subscribe an HTTP endpoint to platform events. When a subscribed event happens, Miget POSTs a signed JSON payload to your endpoint - so your CI, chat bot, or automation tool can react to a deploy without polling GET /api/v1/apps/{uuid}/deployments. Webhooks are workspace-level. Manage them under Workspace → Settings → Integrations, or over the API at /api/v1/webhooks. Both require the workspace:integrations permission (workspace admins).

Events

Subscribe to one or both. There is no separate build event: on Miget the build and the deployment are one lifecycle, so a build finishing is the deploy ending. Static site deployments fire the same events, with data.app_id set to the site’s UUID. A zip or SFTP deploy carries no commit, so the commit fields come back null.

Payload

Scoping to Specific Apps

By default a webhook receives events from every app in the workspace, including apps created later. Narrow it by selecting apps in the dashboard, or by passing app_uuids over the API; an empty list widens it back to the whole workspace. The apps must belong to the same workspace. Either way the payload carries data.app_id, so a consumer can filter on its own as well.

Verifying a Delivery

Deliveries follow the Standard Webhooks specification, so any Standard Webhooks library verifies them as-is. Three headers accompany every POST: The signature is HMAC-SHA256(key, "{webhook-id}.{webhook-timestamp}.{body}"), base64-encoded, where key is the base64-decoded part of the secret after the whsec_ prefix.
Sign against the raw request body, before any JSON parsing and re-serialization.
The signing secret is shown exactly once, when the webhook is created. It is not readable afterwards and there is no rotation endpoint - store it straight away. To replace it, delete the webhook and create a new one.

Testing an Endpoint

Use Send test in the dashboard, or POST /api/v1/webhooks/{uuid}/test, to POST a signed event to the URL immediately and get the result back. It is signed exactly like a real delivery, so it exercises your signature verification too, and it works on a webhook that is still disabled. The test event carries "type": "ping" with an empty data object. ping is deliberately not one of the subscribable event types, so a consumer that rejects unknown types will fail the test even though the endpoint is otherwise fine - accept ping explicitly, or ignore types you do not recognise. The API call responds 200 whether or not your endpoint accepted the event; read status and response_code on the returned delivery.

Retries

A non-2xx response is retried with exponential backoff over roughly 24 hours - 9 attempts in total, spaced 30s, 2m, 8m, 30m, 1h, 3h, 8h, and 12h apart with jitter. Make your endpoint idempotent and deduplicate on the event id: it is stable across automatic retries and across manual replays. A failing endpoint is never disabled automatically.

Delivery History

Every event records one delivery entry, updated in place across retries. The webhook card lists the most recent ones, and the full history opens in a dialog; the same data is available at GET /api/v1/webhooks/{uuid}/deliveries. Each entry carries:
  • status - pending, delivered, or failed
  • attempts - how many times it has been tried
  • response_code and message - what your endpoint returned (the transport error when nothing came back)
  • payload - the exact JSON body that was POSTed, so you can compare it with what you received
The last 50 events per webhook are kept. Retry a delivery from the dialog or with POST /api/v1/webhooks/{uuid}/deliveries/{delivery_uuid}/retry - it replays the stored payload under the original event id.
Diagnosing a webhook that looks silent: a failed entry with a response_code means the delivery reached you and your endpoint rejected it, while no entries at all means no subscribed event has fired yet.

Destination Requirements

The endpoint must be publicly reachable. A URL pointing into private address space - loopback, RFC1918, link-local (including 169.254.169.254), CGNAT, or the localhost, .local, and .internal hostnames - is rejected. The hostname is re-resolved before every delivery, so a name that later starts resolving to a private address stops being delivered to rather than being retried. Developing locally? Expose your endpoint with a tunnel (see ngrok) and register the public URL.

API Reference

  • GET /api/v1/webhooks - list webhooks
  • POST /api/v1/webhooks - create a webhook (the only response containing secret)
  • GET /api/v1/webhooks/{uuid} - get a webhook
  • PUT /api/v1/webhooks/{uuid} - update a webhook (send only the fields you want changed)
  • DELETE /api/v1/webhooks/{uuid} - delete a webhook
  • POST /api/v1/webhooks/{uuid}/test - send a test event and get the result back immediately
  • GET /api/v1/webhooks/{uuid}/deliveries - delivery history, newest first
  • POST /api/v1/webhooks/{uuid}/deliveries/{delivery_uuid}/retry - replay a delivery
Create fields: name (unique per workspace), url (http or https), event_filter (array of event types), plus optional app_uuids and enabled (default true).

Next Steps

Logs

Search and filter application logs

Metrics API

Query metrics programmatically