Create Webhook
curl --request POST \
--url https://app.opencomputer.dev/api/webhooks \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "<string>",
"eventTypes": [
"<string>"
],
"sandboxId": "<string>",
"secret": "<string>",
"name": "<string>",
"enabled": true
}
'import requests
url = "https://app.opencomputer.dev/api/webhooks"
payload = {
"url": "<string>",
"eventTypes": ["<string>"],
"sandboxId": "<string>",
"secret": "<string>",
"name": "<string>",
"enabled": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: '<string>',
eventTypes: ['<string>'],
sandboxId: '<string>',
secret: '<string>',
name: '<string>',
enabled: true
})
};
fetch('https://app.opencomputer.dev/api/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.opencomputer.dev/api/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => '<string>',
'eventTypes' => [
'<string>'
],
'sandboxId' => '<string>',
'secret' => '<string>',
'name' => '<string>',
'enabled' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.opencomputer.dev/api/webhooks"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.opencomputer.dev/api/webhooks")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.opencomputer.dev/api/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "whk_3f9a2c",
"name": "prod",
"url": "https://app.example.com/oc-webhook",
"eventTypes": ["sandbox.stopped"],
"sandboxId": null,
"enabled": true,
"hasSecret": true,
"secret": "whsec_Hk9…",
"createdAt": "2026-06-24T12:00:00Z",
"updatedAt": "2026-06-24T12:00:00Z"
}
Webhooks
Create Webhook
POST
/
api
/
webhooks
Create Webhook
curl --request POST \
--url https://app.opencomputer.dev/api/webhooks \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"url": "<string>",
"eventTypes": [
"<string>"
],
"sandboxId": "<string>",
"secret": "<string>",
"name": "<string>",
"enabled": true
}
'import requests
url = "https://app.opencomputer.dev/api/webhooks"
payload = {
"url": "<string>",
"eventTypes": ["<string>"],
"sandboxId": "<string>",
"secret": "<string>",
"name": "<string>",
"enabled": True
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: '<string>',
eventTypes: ['<string>'],
sandboxId: '<string>',
secret: '<string>',
name: '<string>',
enabled: true
})
};
fetch('https://app.opencomputer.dev/api/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://app.opencomputer.dev/api/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => '<string>',
'eventTypes' => [
'<string>'
],
'sandboxId' => '<string>',
'secret' => '<string>',
'name' => '<string>',
'enabled' => true
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://app.opencomputer.dev/api/webhooks"
payload := strings.NewReader("{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://app.opencomputer.dev/api/webhooks")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.opencomputer.dev/api/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"<string>\",\n \"eventTypes\": [\n \"<string>\"\n ],\n \"sandboxId\": \"<string>\",\n \"secret\": \"<string>\",\n \"name\": \"<string>\",\n \"enabled\": true\n}"
response = http.request(request)
puts response.read_body{
"id": "whk_3f9a2c",
"name": "prod",
"url": "https://app.example.com/oc-webhook",
"eventTypes": ["sandbox.stopped"],
"sandboxId": null,
"enabled": true,
"hasSecret": true,
"secret": "whsec_Hk9…",
"createdAt": "2026-06-24T12:00:00Z",
"updatedAt": "2026-06-24T12:00:00Z"
}
Register a webhook destination for sandbox lifecycle events. See Webhooks.
Without an
string
required
HTTPS endpoint to deliver to. (SSRF protection is applied by the delivery provider at send time, not at registration.)
string[]
Event-type allow-list — exact (
sandbox.stopped) or prefix (sandbox.*). Default: all event types. Types outside the sandbox taxonomy are rejected with 400.string
Scope to a single sandbox. Omit to receive events for all of the org’s sandboxes.
string
Signing secret. Omit and one is generated (
whsec_…). The secret is returned in the create response and is re-fetchable any time via GET /api/webhooks/{id}/secret.string
Optional display name for the destination.
boolean
Whether the destination is active. Default
true. false pauses delivery.string
Optional. A retried create with the same key and same body returns the same destination (
200) instead of a duplicate. Reusing the key with a different body is a 409 conflict.Idempotency-Key, each call creates a new destination and returns 201 — there is no get-or-create by name. With one, a retried call returns the same destination with 200.
Validation (all 400): url must be HTTPS; each eventTypes entry must be a known sandbox event type or a prefix.* wildcard.
{
"id": "whk_3f9a2c",
"name": "prod",
"url": "https://app.example.com/oc-webhook",
"eventTypes": ["sandbox.stopped"],
"sandboxId": null,
"enabled": true,
"hasSecret": true,
"secret": "whsec_Hk9…",
"createdAt": "2026-06-24T12:00:00Z",
"updatedAt": "2026-06-24T12:00:00Z"
}
The
secret is returned here and stays re-fetchable via GET /api/webhooks/{id}/secret — store it to verify deliveries.⌘I