#Webhooks

Receive real-time event notifications from sandboxes via webhooks. Webhooks allow you to react to process events, sandbox lifecycle changes, and file modifications.

Webhook Configuration#

Configure webhooks when claiming a sandbox:

FieldTypeDescription
urlstringWebhook endpoint URL (required)
secretstringShared secret for payload signing (optional)
watch_dirstringDirectory to monitor for file events (optional)
ℹ

Webhooks are configured per-sandbox at creation time. Each sandbox can have its own webhook configuration.

ℹ

secret is optional. For quick debugging, you can leave it empty and use services like webhook.site to inspect incoming events directly.


Event Types#

Sandbox Events#

EventDescription
sandbox.readySandbox has been initialized and is ready
sandbox.pausedSandbox has been paused
sandbox.resumedSandbox has been resumed
sandbox.killedSandbox has been killed

Process Events#

EventDescription
process.startedA new process/context has started
process.exitedA process has exited (includes exit code)
process.crashedA process has crashed unexpectedly

File Events#

EventDescription
file.modifiedA file event occurred (create, write, remove, rename, or chmod)
ℹ

File events require setting watch_dir in the webhook configuration. The specific operation type is included in the payload's event_type field.

Agent Events#

EventDescription
agent.eventCustom event published by the sandbox via the webhook publish API

Configure Webhook at Sandbox Creation#

Set up webhooks when claiming a sandbox.

go
webhookURL := os.Getenv("SANDBOX0_WEBHOOK_URL") webhookSecret := os.Getenv("SANDBOX0_WEBHOOK_SECRET") sandbox, err := client.ClaimSandbox( ctx, "default", sandbox0.WithSandboxHardTTL(300), sandbox0.WithSandboxWebhook(webhookURL, webhookSecret), sandbox0.WithSandboxWebhookWatchDir("/tmp/webhook-demo"), ) if err != nil { log.Fatal(err) } fmt.Printf("Sandbox ID: %s\n", sandbox.ID)

Signature Verification#

When a secret is configured, webhook payloads are signed using HMAC-SHA256.

ℹ

If secret is not configured, Sandbox0 will not send the X-Sandbox0-Signature header.

X-Sandbox0-Signature: <hex-encoded-signature>

The signature is the hex-encoded HMAC-SHA256 of the request body.

Verification Example#

go
import ( "io" "net/http" "os" sandbox0 "github.com/sandbox0-ai/sdk-go" ) func webhookHandler(w http.ResponseWriter, r *http.Request) { // Verify against raw request bytes, not a re-serialized JSON object. payload, err := io.ReadAll(r.Body) if err != nil { http.Error(w, "read body failed", http.StatusBadRequest) return } signature := r.Header.Get("X-Sandbox0-Signature") secret := os.Getenv("SANDBOX0_WEBHOOK_SECRET") if !sandbox0.VerifyWebhookSignature(secret, payload, signature) { http.Error(w, "invalid signature", http.StatusUnauthorized) return } // Process webhook... w.WriteHeader(http.StatusOK) }

Triggering Webhook Events#

Sandbox Events#

go
// sandbox.ready is triggered automatically when the sandbox initializes // Trigger sandbox.paused event _, err = client.PauseSandbox(ctx, sandbox.ID) if err != nil { log.Fatal(err) } log.Println("Sandbox paused - webhook triggered") // Trigger sandbox.resumed event _, err = client.ResumeSandbox(ctx, sandbox.ID) if err != nil { log.Fatal(err) } log.Println("Sandbox resumed - webhook triggered")

Process Events#

go
// Trigger process.started and process.exited events _, err = sandbox.Run(ctx, "bash", `echo "hello world"`) if err != nil { log.Fatal(err) } log.Println("Process events triggered") // Trigger process.exited with non-zero exit code _, err = sandbox.Cmd(ctx, `/bin/sh -c "exit 2"`) if err != nil { log.Printf("Command failed (expected): %v", err) } log.Println("Process exited with error code")

File Events#

go
// Ensure watch directory exists _, err = sandbox.Mkdir(ctx, "/tmp/webhook-demo", true) if err != nil { log.Fatal(err) } // Trigger file.modified events (with payload.event_type: "create", "write") _, err = sandbox.WriteFile(ctx, "/tmp/webhook-demo/file.txt", []byte("hello")) if err != nil { log.Fatal(err) } log.Println("File created/written - webhook triggered") // Trigger file.modified event (with payload.event_type: "rename") _, err = sandbox.MoveFile(ctx, "/tmp/webhook-demo/file.txt", "/tmp/webhook-demo/file-renamed.txt") if err != nil { log.Fatal(err) } log.Println("File renamed - webhook triggered") // Trigger file.modified event (with payload.event_type: "chmod") _, err = sandbox.Cmd(ctx, `/bin/sh -c "chmod 600 /tmp/webhook-demo/file-renamed.txt"`) if err != nil { log.Fatal(err) } log.Println("File chmodded - webhook triggered") // Trigger file.modified event (with payload.event_type: "remove") _, err = sandbox.DeleteFile(ctx, "/tmp/webhook-demo/file-renamed.txt") if err != nil { log.Fatal(err) } log.Println("File removed - webhook triggered")

Agent Events#

Publish custom events from within the sandbox using the webhook publish API:

go
// Publish a custom agent event from within the sandbox _, err = sandbox.Run(ctx, "bash", `curl -X POST http://localhost:49983/webhook/publish \ -H "Content-Type: application/json" \ -d '{"payload": {"message": "Task completed", "progress": 100}}'`) if err != nil { log.Fatal(err) } log.Println("Agent event published")

Next Steps#

Port Exposure

Expose sandbox ports publicly

Network Policy

Control network access

Files

File operations and watching