Skip to content

Webhooks

Preview Webhooks let managed.dev push events to you instead of you polling for them. You register an HTTPS endpoint, subscribe it to the event types you care about, and managed.dev sends a signed POST to that URL whenever one of those events happens — a deploy lands, a build fails, malware is found, a certificate renews. Every delivery is signed, logged, and replayable.

Webhooks pair naturally with async jobs: kick off a long-running mutation, get a 202 and a job back, then let a job.succeeded or job.failed webhook tell you when it finished — no polling loop required.

A webhook endpoint is a resource: a target URL, the set of event types it’s subscribed to, and a signing secret. Managing endpoints needs the webhooks:write scope; reads need webhooks:read.

Method + path Purpose
GET /v1/webhook-endpoints List your endpoints.
POST /v1/webhook-endpoints Create an endpoint and reveal its signing secret once.
GET /v1/webhook-endpoints/{id} Fetch a single endpoint.
PATCH /v1/webhook-endpoints/{id} Change the URL, subscribed events, or enabled state.
DELETE /v1/webhook-endpoints/{id} Remove an endpoint and stop deliveries.
Create a webhook endpoint
curl https://api.managed.dev/v1/webhook-endpoints \
-H "Authorization: Bearer mfk_live_..." \
-H "Forge-Version: 2026-06-23" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/managed-dev",
"events": ["site.deployed", "deployment.failed", "malware.detected"],
"description": "Deploy + security alerts"
}'

The response carries the new endpoint and, on create only, the signing_secret you’ll use to verify signatures. The secret is reveal-once — it’s never returned again.

201 Created
{
"data": {
"id": "whe_01J7...",
"url": "https://hooks.example.com/managed-dev",
"events": ["site.deployed", "deployment.failed", "malware.detected"],
"description": "Deploy + security alerts",
"enabled": true,
"signing_secret": "whsec_9aF2...", // shown once, store it now
"created_at": "2026-06-23T18:04:11.412Z"
},
"request_id": "req_01J7..."
}

An endpoint subscribes to a list of event types. managed.dev sends a delivery only for events on that list, so a deploy-bot endpoint never sees malware events and a security endpoint never sees routine deploys. Subscribe to the events you’ll act on; you can change the list any time with PATCH.

The catalog spans the platform — deploys, builds, jobs, security, certificates, and quota. See event types & payloads for every event and its payload, and the event-types reference for the canonical list.

Every webhook body shares one envelope. The type tells you which event fired and data carries the type-specific payload; id is the unique delivery id you dedupe on.

Common event envelope
{
"id": "evt_01J9...", // unique per delivery — dedupe on this
"type": "site.deployed",
"created_at": "2026-06-23T18:05:02.118Z",
"api_version": "2026-06-23",
"data": { /* type-specific payload */ },
"request_id": "req_01J9..."
}

The same envelope is queryable over the API. GET /v1/events lists recent events for your account so you can backfill or reconcile after downtime, independent of any endpoint.

Every attempt to reach your endpoint is recorded — the response status, timing, and how many retries it took. Read the log to debug a misbehaving consumer, and replay any delivery once your endpoint is healthy again.

Method + path Purpose
GET /v1/webhook-endpoints/{id}/deliveries List delivery attempts for an endpoint, newest first.
POST /v1/webhook-endpoints/{id}/deliveries/{delivery_id}/replay Re-send a past delivery to the endpoint.
Inspect recent deliveries
curl "https://api.managed.dev/v1/webhook-endpoints/whe_01J7.../deliveries?limit=20" \
-H "Authorization: Bearer mfk_live_..."

The delivery list is cursor-paginated like every collection. A replay sends the original, unmodified payload — including its original signature timestamp — so build your verifier to accept replays within your tolerance window (see verifying signatures).

The webhook endpoint detail page in the app.managed.dev dashboard: the endpoint URL and subscribed event types at the top, then a delivery log table with columns for timestamp, event type, response status, attempt count, and a per-row “Replay” button.
  1. Respond 2xx fast. managed.dev treats any 2xx as accepted and anything else (or a timeout) as failed and retriable. Acknowledge the delivery and return immediately — don’t do real work inside the request.

  2. Process asynchronously. Enqueue the event and handle it on a worker. A slow handler causes timeouts, which trigger retries, which look like duplicate events.

  3. Verify every payload. Reject any request whose Forge-Signature doesn’t check out before you trust a single field. See verifying signatures.

  4. Dedupe by delivery id. Retries and manual replays mean the same evt_… id can arrive more than once. Treat handling as idempotent — record processed ids and skip ones you’ve already seen.

  5. Expect new fields. Payloads are additive within the API version. Ignore unknown fields rather than failing on them.