Skip to content

Webhooks

SandBase supports webhooks for receiving real-time notifications about sandbox lifecycle events. Webhooks are E2B-compatible and deliver HTTP POST requests to your configured endpoint whenever a sandbox state changes.

Webhook Endpoint

SandBase receives E2B webhook callbacks at:

POST https://api.sandbase.ai/api/webhooks/e2b

This endpoint is used by the E2B platform to notify SandBase of sandbox state changes. For your own applications, you can register custom webhook URLs to receive these events forwarded from SandBase.

Registering Webhooks

Via API

Register a webhook endpoint to receive sandbox lifecycle events:

POST /webhooks

Request Body

FieldTypeRequiredDescription
namestringNoHuman-readable webhook name
urlstringYesHTTPS URL to receive webhook events
eventsstring[]YesEvent types to subscribe to
enabledbooleanNoWhether the webhook is active (default: true)
signatureSecretstringNoSecret for signature verification

Example Request

bash
curl -X POST https://api.sandbase.ai/webhooks \
  -H "Authorization: Bearer sk-sb-your-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Sandbox Monitor",
    "url": "https://your-app.com/webhooks/sandbox",
    "events": [
      "sandbox.lifecycle.created",
      "sandbox.lifecycle.killed",
      "sandbox.lifecycle.paused",
      "sandbox.lifecycle.resumed"
    ],
    "enabled": true,
    "signatureSecret": "whsec_your-secret-key"
  }'
python
import requests

response = requests.post(
    "https://api.sandbase.ai/webhooks",
    headers={"Authorization": "Bearer sk-sb-your-key"},
    json={
        "name": "Production Sandbox Monitor",
        "url": "https://your-app.com/webhooks/sandbox",
        "events": [
            "sandbox.lifecycle.created",
            "sandbox.lifecycle.killed",
            "sandbox.lifecycle.paused",
            "sandbox.lifecycle.resumed"
        ],
        "enabled": True,
        "signatureSecret": "whsec_your-secret-key"
    }
)

webhook = response.json()["data"]
print(f"Webhook ID: {webhook['id']}")

Via Dashboard

You can also manage webhooks through the SandBase Dashboard under Settings → Webhooks.

Event Types

Event TypeDescription
sandbox.lifecycle.createdA new sandbox has been created and is running
sandbox.lifecycle.killedA sandbox has been destroyed (manually or by timeout)
sandbox.lifecycle.pausedA sandbox has been paused
sandbox.lifecycle.resumedA paused sandbox has been resumed
sandbox.lifecycle.updatedSandbox metadata or configuration was updated

Payload Format

All webhook events are delivered as HTTP POST requests with a JSON body:

json
{
  "id": "evt_01H9X2K3M4N5P6Q7R8S9T0",
  "version": "1.0",
  "type": "sandbox.lifecycle.created",
  "timestamp": "2026-07-01T10:00:00.000Z",
  "event_category": "sandbox",
  "event_label": "lifecycle",
  "sandbox_id": "sbx_a1b2c3d4e5f6",
  "sandbox_execution_id": "exec_x1y2z3",
  "sandbox_template_id": "code_interpreter",
  "sandbox_build_id": "build_m1n2o3",
  "sandbox_team_id": "team_p4q5r6",
  "event_data": {
    "status": "running",
    "timeout": 300
  }
}

Payload Fields

FieldTypeDescription
idstringUnique event identifier
versionstringPayload schema version
typestringEvent type (see table above)
timestampstringISO 8601 timestamp of the event
event_categorystringEvent category (e.g., "sandbox")
event_labelstringEvent label (e.g., "lifecycle")
sandbox_idstringID of the affected sandbox
sandbox_execution_idstringExecution ID (E2B internal)
sandbox_template_idstringTemplate used to create the sandbox
sandbox_build_idstringBuild ID of the template
sandbox_team_idstringTeam/organization ID
event_dataobjectAdditional event-specific data

Event Data by Type

sandbox.lifecycle.created

json
{
  "event_data": {
    "status": "running",
    "timeout": 300,
    "template_id": "code_interpreter"
  }
}

sandbox.lifecycle.killed

json
{
  "event_data": {
    "reason": "timeout",
    "duration_seconds": 300
  }
}

sandbox.lifecycle.paused

json
{
  "event_data": {
    "previous_status": "running"
  }
}

sandbox.lifecycle.resumed

json
{
  "event_data": {
    "previous_status": "paused",
    "new_timeout": 300
  }
}

Signature Verification

Webhook payloads are signed using SHA-256 to verify authenticity. The signature is included in the e2b-signature header.

Verification Algorithm

The signature is computed as:

signature = base64_encode(SHA256(secret + raw_body))

Trailing = padding characters are stripped from the base64 output.

Verification Example

python
import hashlib
import base64

def verify_webhook_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    """Verify an E2B webhook signature."""
    # Compute expected signature
    payload = secret.encode() + raw_body
    hash_bytes = hashlib.sha256(payload).digest()
    expected = base64.b64encode(hash_bytes).decode().rstrip("=")

    return expected == signature
javascript
import crypto from 'crypto';

function verifyWebhookSignature(rawBody, signature, secret) {
  // Compute expected signature
  const payload = secret + rawBody;
  const hash = crypto.createHash('sha256').update(payload).digest('base64');
  const expected = hash.replace(/=+$/, '');

  return expected === signature;
}

Using in a Request Handler

python
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_your-secret-key"

@app.route("/webhooks/sandbox", methods=["POST"])
def handle_webhook():
    raw_body = request.get_data()
    signature = request.headers.get("e2b-signature", "")

    if not verify_webhook_signature(raw_body, signature, WEBHOOK_SECRET):
        abort(401, "Invalid signature")

    payload = request.get_json()
    event_type = payload["type"]

    if event_type == "sandbox.lifecycle.killed":
        sandbox_id = payload["sandbox_id"]
        print(f"Sandbox {sandbox_id} was destroyed")
        # Handle cleanup...

    return {"received": True}, 200
javascript
import express from 'express';

const app = express();
const WEBHOOK_SECRET = 'whsec_your-secret-key';

app.post('/webhooks/sandbox', express.raw({ type: 'application/json' }), (req, res) => {
  const rawBody = req.body.toString();
  const signature = req.headers['e2b-signature'] || '';

  if (!verifyWebhookSignature(rawBody, signature, WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  const payload = JSON.parse(rawBody);

  if (payload.type === 'sandbox.lifecycle.killed') {
    console.log(`Sandbox ${payload.sandbox_id} was destroyed`);
    // Handle cleanup...
  }

  res.json({ received: true });
});

Delivery and Retry Policy

Delivery

  • Webhooks are delivered via HTTP POST with Content-Type: application/json
  • Your endpoint must respond with a 2xx status code within 30 seconds
  • Any non-2xx response or timeout is treated as a delivery failure

Retry Schedule

Failed deliveries are retried with exponential backoff:

AttemptDelay
1st retry1 minute
2nd retry5 minutes
3rd retry30 minutes
4th retry2 hours
5th retry12 hours

After 5 failed attempts, the event is marked as permanently failed. You can view failed deliveries in the Dashboard.

Idempotency

Webhook events may be delivered more than once. Use the id field to deduplicate:

python
processed_events = set()

def handle_webhook(payload):
    event_id = payload["id"]
    if event_id in processed_events:
        return  # Already processed

    processed_events.add(event_id)
    # Process the event...

Ordering

Events are delivered in approximate chronological order, but strict ordering is not guaranteed. Use the timestamp field to determine event sequence.

Managing Webhooks

List Webhooks

GET /webhooks

Returns all webhooks for the authenticated organization.

Delete Webhook

DELETE /webhooks/:id

Permanently removes a webhook registration.

Best Practices

  1. Always verify signatures — Never process webhook payloads without verifying the e2b-signature header.
  2. Respond quickly — Return a 200 response immediately, then process the event asynchronously.
  3. Handle duplicates — Use the event id for idempotent processing.
  4. Use HTTPS — Webhook URLs must use HTTPS for security.
  5. Monitor failures — Check the Dashboard for failed deliveries and fix endpoint issues promptly.