Operate
Webhooks
Receive real-time HTTP notifications when browser sessions, sandboxes, and machines change state.
Banata can send HTTP POST requests to your server whenever important events occur — a browser session becomes ready, a sandbox fails, a machine is suspended, and more. Webhooks let you build reactive workflows without polling.
How It Works
- You register an HTTPS endpoint in the dashboard or via the API.
- When an event fires, Banata enqueues a delivery for every matching webhook.
- Each delivery is a signed JSON POST with retry logic.
- Your server verifies the signature and processes the payload.
Creating a Webhook
From the Dashboard
- Sign in to the Banata dashboard.
- Navigate to Webhooks in the sidebar.
- Click Create Webhook.
- Enter your HTTPS endpoint URL.
- Select which event types to subscribe to (or leave all selected).
- Copy the signing secret — it is only shown once.
From the API
curl -X POST "https://api.boxes.banata.dev/v1/webhooks" \
-H "Authorization: Bearer br_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/banata",
"description": "Production handler",
"event_types": ["session.ready", "session.ended", "sandbox.failed"]
}'The response includes the signing_secret. Store it securely — you will need it to verify signatures.
Event Types
Browser Session Events
| Event | Fired when |
|---|---|
session.created | A browser session is created |
session.assigned | The session is assigned to a machine |
session.ready | The browser is running and the CDP endpoint is available |
session.ending | Session termination has been initiated |
session.ended | The session has fully terminated |
session.failed | The session could not be started or crashed |
Sandbox Events
| Event | Fired when |
|---|---|
sandbox.created | A sandbox is created |
sandbox.assigned | The sandbox is assigned to a machine |
sandbox.ready | The sandbox runtime is up and accepting commands |
sandbox.ending | Sandbox termination has been initiated |
sandbox.ended | The sandbox has fully terminated |
sandbox.failed | The sandbox could not be started or crashed |
sandbox.paused | The sandbox has been paused |
sandbox.resumed | A paused sandbox has been resumed |
Sandbox Extension Events
| Event | Fired when |
|---|---|
sandbox.opencode.updated | The opencode runtime state changed |
sandbox.browser_preview.updated | The browser preview state changed |
sandbox.handoff.requested | A human handoff was requested |
sandbox.handoff.accepted | A human handoff was accepted |
sandbox.handoff.completed | A human handoff was completed |
sandbox.artifacts.updated | Sandbox artifacts were updated |
Payload Format
Every webhook delivery is a JSON POST with the following structure:
{
"event_type": "session.ready",
"event_id": "evt_abc123",
"org_id": "org_xyz",
"timestamp": 1710000000,
"data": {
"resource": {
"type": "browser_session",
"id": "sess_abc123"
}
}
}Headers
Each delivery includes the following headers:
| Header | Description |
|---|---|
X-Banata-Event | The event type (e.g. session.ready) |
X-Banata-Event-Id | Unique event ID for deduplication |
X-Banata-Delivery-Attempt | Attempt number (1-based) |
X-Banata-Timestamp | Unix timestamp of the delivery |
X-Banata-Signature | HMAC-SHA256 signature for verification |
Verifying Signatures
Every delivery is signed with your webhook's signing secret using HMAC-SHA256. Always verify the signature before processing the payload.
The X-Banata-Signature header has the format:
t={timestamp},v1={signature}To verify:
- Extract the timestamp (
t) and signature (v1) from the header. - Construct the signed payload:
{timestamp}.{request_body}. - Compute the HMAC-SHA256 of the signed payload using your signing secret.
- Compare your computed signature with
v1.
Example (Node.js)
import crypto from "crypto";
function verifyWebhookSignature(
payload: string,
signatureHeader: string,
secret: string,
): boolean {
const parts = Object.fromEntries(
signatureHeader.split(",").map((p) => p.split("=", 2) as [string, string])
);
const timestamp = parts.t;
const signature = parts.v1;
if (!timestamp || !signature) return false;
const signedPayload = `${timestamp}.${payload}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedPayload)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature, "hex"),
Buffer.from(expected, "hex"),
);
}Example (Python)
import hmac, hashlib
def verify_webhook_signature(payload: str, signature_header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
timestamp = parts.get("t", "")
signature = parts.get("v1", "")
signed_payload = f"{timestamp}.{payload}"
expected = hmac.new(
secret.encode(), signed_payload.encode(), hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)Retry Policy
If your endpoint does not return a 2xx status code, Banata retries the delivery with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 5 seconds |
| 3 | 30 seconds |
| 4 | 2 minutes |
| 5 | 10 minutes |
After 5 failed attempts, the delivery is marked as permanently failed. You can view failed deliveries and their error messages in the dashboard under Webhooks → Recent Deliveries.
Testing Webhooks
You can send a test delivery from the dashboard:
- Go to Webhooks.
- Click the ⋯ menu on any webhook endpoint.
- Select Send Test.
This sends a test payload with event_type: "session.created" and a webhook_test resource type so your handler can distinguish test deliveries from real ones.
You can also send a test via the API:
curl -X POST "https://api.boxes.banata.dev/v1/webhooks/test" \
-H "Authorization: Bearer br_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{ "id": "WEBHOOK_ID" }'Best Practices
Respond quickly
Return a 200 response as fast as possible. Do heavy processing asynchronously (e.g. push to a queue). Banata waits up to 30 seconds for a response before marking the delivery as failed.
Idempotency
Use the X-Banata-Event-Id header to deduplicate events. The same event may be delivered more than once if your server responds slowly or if a retry is in progress when a success is recorded.
HTTPS only
Webhook URLs must use HTTPS. Banata rejects plain HTTP endpoints.
Monitor delivery health
Check the Webhooks page in the dashboard regularly. If a webhook is consistently failing, Banata shows its status as Failing so you can investigate.
Managing Webhooks via the API
List webhooks
curl "https://api.boxes.banata.dev/v1/webhooks" \
-H "Authorization: Bearer br_live_YOUR_KEY"Delete a webhook
curl -X DELETE "https://api.boxes.banata.dev/v1/webhooks?id=WEBHOOK_ID" \
-H "Authorization: Bearer br_live_YOUR_KEY"List deliveries
curl "https://api.boxes.banata.dev/v1/webhooks/deliveries?limit=20" \
-H "Authorization: Bearer br_live_YOUR_KEY"Next Steps
- API Keys — Create an API key with
org:managepermissions to manage webhooks via the API - Browser Sessions — Learn about session lifecycle events
- Sandboxes — Learn about sandbox lifecycle events
- API Reference — Full API endpoint documentation