Event webhooks
Burrowbox can POST a signed JSON event to your server whenever a machine changes state or a job finishes, so you don't need to poll. Register up to 10 HTTPS endpoints; each one can receive all events or a chosen few.
These are outgoing notifications to you. To have an outside service trigger something inside a machine, see Scheduled jobs & webhooks.
#Event types
| Type | When |
|---|---|
machine.created |
A machine was created and booted |
machine.started |
A stopped machine was turned on |
machine.stopped |
A machine was turned off (state kept). detail.reason says why: requested, expired, balance-empty, platform (stopped by the host), or after schedule …/after webhook … (stopAfter) |
machine.error |
A machine failed to boot or crashed |
machine.expired |
A machine reached its expiresAt (followed by stopped or destroyed) |
machine.destroyed |
A machine was deleted |
machine.claimed |
A machine was taken from a warm pool |
run.succeeded |
A scheduled job or webhook run finished successfully |
run.failed |
A scheduled job or webhook run failed |
#Payload
POST /burrowbox/events HTTP/1.1
Content-Type: application/json
Burrowbox-Event: machine.stopped
Burrowbox-Delivery: dlv_5c1e0a9b2d4f6e81
Burrowbox-Signature: t=1790797200,v1=5f0c…e9
{
"id": "evt_91ab34cd56ef7890",
"type": "machine.stopped",
"createdAt": "2026-09-30T12:00:00.000Z",
"data": {
"machine": { "id": "2f6aeedcd3", "name": "acme", "status": "stopped", "size": "tiny", "externalId": "cus_8f2a", "labels": { "plan": "pro" } },
"detail": { "reason": "expired" }
}
}
run.* events carry data.run (id, source — schedule, webhook or manual (run now), sourceId, status, and the first 2,000 characters of output) and data.machine (id, name, externalId, labels).
Answer with any 2xx within 10 seconds. Anything else (or a timeout) is retried with backoff: 30 s, 1, 2, 4, 8, 16, 32 and 60 minutes — 8 attempts in total. Events can arrive more than once and out of order: use id to deduplicate and createdAt to order.
#Verify the signature
Each endpoint has a signing secret (whsec_…), shown once when you create it. The Burrowbox-Signature header is t=<unix seconds>,v1=<hex HMAC-SHA256 of "t.body" with the secret>. Compute it over the raw request body and reject old timestamps.
// Node.js (Express): app.post("/burrowbox/events", express.raw({ type: "application/json" }), handler)
import crypto from "node:crypto";
function verify(rawBody, header, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const ok = parts.v1 && parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
return ok && Math.abs(Date.now() / 1000 - Number(parts.t)) <= toleranceSeconds;
}
# Python
import hmac, hashlib, time
def verify(raw_body: bytes, header: str, secret: str, tolerance=300) -> bool:
parts = dict(p.split("=", 1) for p in header.split(","))
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", "")) and abs(time.time() - int(parts["t"])) <= tolerance
#Endpoints
| Method | Path | |
|---|---|---|
GET |
/api/event-webhooks |
List your endpoints |
POST |
/api/event-webhooks |
{"url": "https://…", "events": ["machine.stopped", "machine.error"], "description": "prod"} — events defaults to ["*"]. Returns secret once |
PATCH |
/api/event-webhooks/{id} |
Change url, events, description, or pause with {"enabled": false} |
POST |
/api/event-webhooks/{id}/rotate-secret |
New signing secret (returned once); the old one stops working |
POST |
/api/event-webhooks/{id}/test |
Send a test event now and return {ok, status, error} |
DELETE |
/api/event-webhooks/{id} |
Remove it |
GET |
/api/event-webhooks/deliveries?endpoint={id}&limit=50 |
Recent deliveries with status (pending, delivered, failed), attempts and the last response |
curl -X POST https://burrowbox.dev/api/event-webhooks \
-H "Authorization: Bearer $BURROWBOX_KEY" -H "Content-Type: application/json" \
-d '{"url": "https://api.example.com/burrowbox/events", "events": ["*"]}'
You can also manage them in the dashboard under Account → Event webhooks, or from an agent with the platform MCP tools event_webhooks_list, event_webhook_create, event_webhook_test and event_webhook_delete.