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 failuresEvent types
| Category | Events |
|---|---|
| Server | server.created, server.updated, server.destroyed |
| Server State | server.running, server.stopped, server.failed |
| Backup | backup.created, backup.completed, backup.failed |
| Restore | backup.restore_started, backup.restore_completed |
| Incident | incident.opened, incident.acknowledged, incident.resolved |
| Alert | alert.triggered, alert.resolved |
| Data | data.created, data.updated, data.destroyed |
| Job | job.queued, job.running, job.completed, job.failed |
| Usage | usage.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_atto reorder. - Idempotency: use
X-Puchify-Deliveryto 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_1Via 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 refusedA 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.