Receive events when runs complete, approvals are requested, or sources sync.
| Method | Path | Purpose |
|---|---|---|
GET | /webhooks | List endpoints |
POST | /webhooks | Create (url, events[], optional description) |
PATCH | /webhooks/{id} | Update |
DELETE | /webhooks/{id} | Delete |
POST | /webhooks/{id}/test | Send a test event |
| Event | When |
|---|---|
run.started | A run starts |
run.approval_requested | A policy paused a run |
run.completed | Status became succeeded, handed_off, failed or cancelled |
query.flagged | A user down-rated an answer |
source.synced | A source finished syncing |
source.failed | A source sync failed |
agent.promoted | A version was promoted |
workspace.emergency_stop | Emergency stop engaged or released |
{
"id": "evt_01j9x7g7h8",
"type": "run.completed",
"created_at": "2026-09-19T08:24:40Z",
"data": { "run": { "id": "run_01j9x6e5f6", "status": "succeeded", "…": "…" } }
}
Each delivery includes X-BMC-Signature: t=<unix>,v1=<hmac_sha256>. Compute HMAC_SHA256(secret, t + "." + raw_body) and compare with v1; reject if t is older than 5 minutes.
import hmac, hashlib, time
def verify(secret: str, header: str, body: bytes) -> bool:
parts = dict(p.split("=") for p in header.split(","))
if time.time() - int(parts["t"]) > 300:
return False
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
Deliveries time out after 10 seconds and are retried with exponential backoff for 24 hours (up to 12 attempts). Respond 2xx quickly and process asynchronously. Failed deliveries are visible under Settings → Webhooks → (endpoint) → Deliveries.