Webhooks API
Receive webhook deliveries for sandbox, template, and account-level events.
Authentication Required: All webhook management APIs require the X-API-Key header.
Webhook signing secrets are not returned by these APIs. Store the secret when you create the endpoint in the UI or wherever that provisioning flow exposes it.
Standard Response Format
Webhook management endpoints use the standard API envelope:
{
"success": true,
"data": {},
"timestamp": "2026-04-17T12:34:56Z"
}Delete returns a top-level message and omits data.
API Endpoints
Create Webhook
POST /v1/webhooks
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | Yes | http:// or https:// endpoint URL |
| event_types | array[string] | Yes | Event type subscriptions |
| description | string | No | Endpoint description |
| scope | string | No | user or account |
Response
{
"success": true,
"data": {
"endpoint_id": "whk-abc123def456789",
"url": "https://your-app.example/webhooks/scalebox",
"event_types": [
"sandbox.started",
"sandbox.terminated"
],
"is_active": true,
"description": "Production lifecycle webhook",
"timeout_seconds": 30,
"max_retries": 3,
"created_at": "2026-04-17T12:00:00Z",
"updated_at": "2026-04-17T12:00:00Z",
"user_id": "usr-xyz789abc123456"
},
"timestamp": "2026-04-17T12:00:00Z"
}For account-level webhooks, user_id is null.
List Webhooks
GET /v1/webhooks
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| page | integer | No | Page number, default 1 |
| limit | integer | No | Page size, default 50 (alias: page_size) |
| offset | integer | No | Offset, default 0 (alias: skip) |
Response
{
"success": true,
"data": {
"webhooks": [
{
"endpoint_id": "whk-abc123def456789",
"url": "https://your-app.example/webhooks/scalebox",
"event_types": [
"sandbox.started",
"sandbox.terminated"
],
"is_active": true,
"description": "Production lifecycle webhook",
"timeout_seconds": 30,
"max_retries": 3,
"total_deliveries": 150,
"successful_deliveries": 148,
"failed_deliveries": 2,
"last_delivery_at": "2026-04-17T11:55:00Z",
"last_success_at": "2026-04-17T11:55:00Z",
"last_failure_at": "2026-04-17T10:10:00Z",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-17T11:55:00Z",
"user_id": "usr-xyz789abc123456"
}
],
"pagination": {
"page": 1,
"limit": 50,
"total": 1,
"total_pages": 1
}
},
"timestamp": "2026-04-17T12:01:00Z"
}Root and admin viewers may also see account_id.
Get Webhook
GET /v1/webhooks/{endpoint_id}
Response
{
"success": true,
"data": {
"endpoint_id": "whk-abc123def456789",
"url": "https://your-app.example/webhooks/scalebox",
"event_types": [
"sandbox.started",
"sandbox.terminated"
],
"is_active": true,
"description": "Production lifecycle webhook",
"timeout_seconds": 30,
"max_retries": 3,
"total_deliveries": 150,
"successful_deliveries": 148,
"failed_deliveries": 2,
"last_delivery_at": "2026-04-17T11:55:00Z",
"last_success_at": "2026-04-17T11:55:00Z",
"last_failure_at": "2026-04-17T10:10:00Z",
"created_at": "2026-04-10T08:00:00Z",
"updated_at": "2026-04-17T11:55:00Z",
"user_id": "usr-xyz789abc123456"
},
"timestamp": "2026-04-17T12:02:00Z"
}Update Webhook
PUT /v1/webhooks/{endpoint_id}
Request body
| Name | Type | Required | Description |
|---|---|---|---|
| url | string | No | New URL |
| event_types | array[string] | No | New event subscriptions |
| is_active | boolean | No | Enable or disable |
| description | string | No | New description |
| timeout_seconds | integer | No | 5 to 300 |
| max_retries | integer | No | 0 to 10 |
| scope | string | No | user or account |
Response
{
"success": true,
"data": {
"endpoint_id": "whk-abc123def456789",
"url": "https://your-app.example/webhooks/scalebox",
"event_types": [
"sandbox.started",
"sandbox.failed",
"sandbox.terminated"
],
"is_active": false,
"description": "Paused temporarily",
"timeout_seconds": 30,
"max_retries": 3,
"updated_at": "2026-04-17T12:03:00Z"
},
"timestamp": "2026-04-17T12:03:00Z"
}Delete Webhook
DELETE /v1/webhooks/{endpoint_id}
Response
{
"success": true,
"message": "Webhook endpoint deleted successfully",
"timestamp": "2026-04-17T12:04:00Z"
}Get Delivery History
GET /v1/webhooks/{endpoint_id}/deliveries
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Default 50, valid range 1-200 |
Response
{
"success": true,
"data": {
"deliveries": [
{
"delivery_id": "whd-xyz789abc123456",
"event_type": "sandbox.started",
"event_id": "evt-abc123def456789",
"status": "delivered",
"http_status_code": 200,
"attempt_number": 1,
"max_attempts": 3,
"next_retry_at": null,
"delivered_at": "2026-04-17T11:55:00Z",
"failed_at": null,
"error_message": null,
"created_at": "2026-04-17T11:55:00Z"
}
]
},
"timestamp": "2026-04-17T12:05:00Z"
}Event Types
Supported customer-facing event types include:
sandbox.createdsandbox.startedsandbox.stoppedsandbox.pausedsandbox.resumedsandbox.terminatedsandbox.failedsandbox.project_switchedtemplate.uploadedtemplate.deleted
Account-level event types such as payment.*, subscription.*, and budget.* require an account-level webhook and an account root user.
Delivery Payload
Webhook deliveries are separate from the management APIs above. A typical delivery body looks like this:
{
"id": "evt-abc123def456789",
"type": "sandbox.started",
"timestamp": "2026-04-17T11:55:00Z",
"data": {
"sandbox_id": "sbx-abc123def456789",
"status": "running"
}
}Common Errors
{
"success": false,
"error": "Invalid event type: sandbox.unknown",
"timestamp": "2026-04-17T12:10:00Z"
}Common webhook errors:
400: invalid URL, invalid scope, unsupported event type, or invalid timeout/retry values403: non-root user tried to create or update an account-scoped webhook404: endpoint not found in your scope500: persistence or delivery-history lookup failure