boxkite

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.

webhooks.py
# 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:

payload.json
{
  "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.py
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:

verify.py
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

The control plane guarantees at-least-once delivery. If your receiver returns anything other than a 2xx status code, boxkite will retry the delivery using exponential backoff over a 24-hour period. Make sure your webhook receiver is idempotent.