Burrowbox Docs

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.