Puchify

Webhooks

React to resource events in real time with HMAC-signed webhooks.

Webhooks let you react to Puchify events in real time — trigger a deploy when a server is created, send a Slack notification when a backup completes, run a GitHub Action when an incident fires.

Architecture

Resource Event ──► Event Bus ──► Webhook Engine ──► POST https://your-app.com/hooks

                                       ├── Retry (backoff × 15)
                                       ├── Delivery history
                                       └── Auto-disable after 50 failures

Event types

CategoryEvents
Serverserver.created, server.updated, server.destroyed
Server Stateserver.running, server.stopped, server.failed
Backupbackup.created, backup.completed, backup.failed
Restorebackup.restore_started, backup.restore_completed
Incidentincident.opened, incident.acknowledged, incident.resolved
Alertalert.triggered, alert.resolved
Datadata.created, data.updated, data.destroyed
Jobjob.queued, job.running, job.completed, job.failed
Usageusage.threshold_reached, usage.overage

Payload format

{
  "id": "evt_abc123",
  "type": "server.created",
  "created_at": "2026-05-28T10:30:00Z",
  "data": {
    "id": "svr_xyz789",
    "name": "web-prod-01",
    "status": "creating",
    "plan": "shared-2",
    "region": "us-east"
  },
  "team_id": "team_456"
}

Delivery

POST https://your-app.com/webhooks/puchify
Content-Type: application/json
X-Puchify-Event: server.created
X-Puchify-Delivery: del_001
X-Puchify-Signature: t=1680000000,v1=abc123def456...
  • Retries: exponential backoff (1s, 5s, 25s, 125s, 625s), then every ~10 min for 24 hours. Max 15 attempts.
  • Timeout: 10 seconds per delivery
  • Ordering: best-effort. Use created_at to reorder.
  • Idempotency: use X-Puchify-Delivery to deduplicate.

Signature verification

The X-Puchify-Signature header contains a timestamp and HMAC-SHA256 hash:

t=1680000000,v1=abc123...

TypeScript

import { verifyWebhookSignature } from "@puchify/sdk"

function handleWebhook(req: Request, secret: string) {
  const signature = req.headers.get("X-Puchify-Signature")
  const payload = await req.text()

  if (!verifyWebhookSignature(payload, signature ?? "", secret)) {
    return new Response("Invalid signature", { status: 401 })
  }

  // Process the webhook
  const event = JSON.parse(payload)
  console.log(`Received ${event.type}`)
}

Python

from puchify import verify_webhook_signature

if not verify_webhook_signature(payload, signature_header, secret):
    raise Exception("Invalid signature")

Management

Via API

curl -X POST https://api.puchify.com/api/v1/webhooks \
  -H "Authorization: Bearer $PUCHIFY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://myapp.com/hooks","events":["server.created"]}'

curl https://api.puchify.com/api/v1/webhooks/wh_1/deliveries \
  -H "Authorization: Bearer $PUCHIFY_API_KEY"

Via CLI

puchify webhooks create \
  --url https://myapp.com/hooks \
  --events server.created,server.destroyed

puchify webhooks deliveries wh_1
puchify webhooks retry wh_1 --delivery del_1

Via Terraform

resource "puchify_webhook" "deploy" {
  url    = "https://ci.myapp.com/hooks/puchify"
  events = ["server.created"]
  active = true
}

Event filtering

Reduce noise by filtering events:

{
  "url": "https://slack.myapp.com/hooks",
  "events": ["server.*"],
  "filter": {
    "server.region": "us-east",
    "server.plan": ["shared-2"]
  }
}

Delivery history

Failed deliveries appear in the web app and CLI:

Delivery ID  Status  Attempt  Next Retry     Error
del_001      failed  3/15     2026-05-28T... Connection refused

A webhook is automatically disabled after 50 consecutive failures. The team owner receives an email notification.

Rate limits

Each webhook subscription has a configurable rate limit (default 100 deliveries/min). If exceeded, events are queued and delivered once capacity frees up.

On this page