Capability
Webhooks
Subscribe to sandbox lifecycle events via HTTP POST callbacks. The control plane delivers push notifications whenever a sandbox is created or destroyed, complete with automatic retry backoff, delivery auditing, and HMAC-SHA256 signatures.
1. Manage webhook subscriptions
Register an HTTPS callback URL and the event types you want to receive. The creation response returns a signing secret exactly once — make sure to store it, as it cannot be retrieved again later.
# Register a webhook subscription
hook = client.create_webhook(
url="https://api.example.com/webhooks/boxkite",
event_types=["sandbox.created", "sandbox.destroyed"],
description="slack-notifier"
)
secret = hook["secret"] # Shown only once!
# List subscriptions
subscriptions = client.list_webhooks()
# Delete a subscription
client.delete_webhook(hook["id"])2. Payload schema
Every webhook delivery is sent as an HTTP POST with a JSON body:
{
"event_id": "evt_1234567890abcdef",
"event": "sandbox.created",
"created_at": "2026-07-13T02:00:00Z",
"account_id": "acc_0987654321fedcba",
"data": {
"session_id": "sb_session_abc123",
"label": "demo-sandbox",
"pod_name": "sandbox-abc123-standalone",
"size": "small"
}
}event_id is the same across every retry of a delivery — de-duplicate on it, not on any per-attempt id. data's shape depends on event: sandbox.destroyed carries session_id, label, duration_seconds, and reason instead of pod_name/size.
3. Delivery monitoring
View recent delivery attempts, status, response codes, and failure details for any webhook subscription:
deliveries = client.list_webhook_deliveries(subscription_id="wh_...")4. Verify signatures
Each request contains a header X-Boxkite-Webhook-Signature of the form:t=timestamp,v1=signature. Compute hmac_sha256(secret, timestamp + "." + body) and verify it matches the signature to guarantee authenticity:
import hmac
import hashlib
def verify_webhook(body_bytes: bytes, signature_header: str, secret: str) -> bool:
parts = dict(pair.split('=') for pair in signature_header.split(','))
timestamp = parts.get('t')
signature = parts.get('v1')
if not timestamp or not signature:
return False
expected_msg = f"{timestamp}.".encode('utf-8') + body_bytes
computed = hmac.new(
secret.encode('utf-8'),
expected_msg,
hashlib.sha256
).hexdigest()
return hmac.compare_digest(computed, signature)At-least-once delivery