#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