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.
Manage endpoints
Section titled “Manage endpoints”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 an endpoint
Section titled “Create an 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" }'const endpoint = await mf.webhookEndpoints.create({ url: "https://hooks.example.com/managed-dev", events: ["site.deployed", "deployment.failed", "malware.detected"], description: "Deploy + security alerts",});
// Shown once — store it now.console.log(endpoint.signing_secret);mf webhooks create \ --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.
{ "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..."}Event types & subscriptions
Section titled “Event types & subscriptions”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.
The event envelope
Section titled “The event envelope”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.
{ "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.
Delivery log & replay
Section titled “Delivery log & replay”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. |
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).
Best practices
Section titled “Best practices”-
Respond
2xxfast. managed.dev treats any2xxas 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. -
Process asynchronously. Enqueue the event and handle it on a worker. A slow handler causes timeouts, which trigger retries, which look like duplicate events.
-
Verify every payload. Reject any request whose
Forge-Signaturedoesn’t check out before you trust a single field. See verifying signatures. -
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. -
Expect new fields. Payloads are additive within the API version. Ignore unknown fields rather than failing on them.