Webhook endpoints
Where we send events. Subscribe to the state changes you care about; every delivery is signed.
The webhook_endpoint object
Every object carries an id, timestamps, and an audit_trail. Money is integer cents. Expand any node in the explorer to see how it links to the rest of the record.
- idstringUnique, stable identifier. Prefixed by object type.
- objectstringString naming the object type.
- urlstringHTTPS URL that receives POSTs.
- statusenumenabled · disabled.
- eventsarrayEvent types, or ["*"].
- descriptionstringYour label.
- secretstringSigning secret. Shown in full once, on create.
- api_versionstringPayload version pinned at create.
- created_attimestampISO 8601, UTC.
- updated_attimestampISO 8601, UTC. Changes on every write.
- audit_trailstringPath to the append-only history of who changed what, and when.
webhook_endpoint · sample data
object{11}
- id"we_7Hq2"id
- object"webhook_endpoint"string
- url"https://erp.example.com/hooks/os"string
- status"enabled"string
eventsarray[3]
- 0"change_order.approved"string
- 1"pay_app.submitted"string
- 2"invoice.coded"string
- description"ERP sync"string
- secret"whsec_••••••••3f9a"string
- api_version"2026-10-preview"string
- created_at
- updated_at
- audit_trail"/v1/webhooks/we_7Hq2/audit"string
POST
/v1/webhooksCreate an endpoint
Returns the full secret once.
Idempotent. Send an Idempotency-Key; retries within 24 hours return the original result and never write twice.
Parameters
- Idempotency-KeystringheaderrequiredAny unique string. Replays return the first result instead of writing twice.
- urlstringbodyrequiredHTTPS URL.
- eventsarraybodyrequiredEvent types.
Returns
The webhook_endpoint object. Errors use the standard error shape.
Request
curl -X POST https://api.os.construction/v1/webhooks \
-H "Authorization: Bearer $OS_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"url": "https://erp.example.com/hooks/os",
"events": [
"change_order.approved",
"pay_app.submitted",
"invoice.coded"
],
"description": "ERP sync"
}'const res = await fetch("https://api.os.construction/v1/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
"url": "https://erp.example.com/hooks/os",
"events": [
"change_order.approved",
"pay_app.submitted",
"invoice.coded"
],
"description": "ERP sync"
}),
});
const obj = await res.json();import os, uuid, requests
res = requests.post(
"https://api.os.construction/v1/webhooks",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"url": "https://erp.example.com/hooks/os",
"events": [
"change_order.approved",
"pay_app.submitted",
"invoice.coded"
],
"description": "ERP sync"
},
)
res.raise_for_status()
data = res.json()Response · 201
{
"id": "we_7Hq2",
"object": "webhook_endpoint",
"url": "https://erp.example.com/hooks/os",
"status": "enabled",
"events": [
"change_order.approved",
"pay_app.submitted",
"invoice.coded"
],
"description": "ERP sync",
"secret": "whsec_5b1e0c7d9a2f4e3f9a",
"api_version": "2026-10-preview",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z",
"audit_trail": "/v1/webhooks/we_7Hq2/audit"
}GET
/v1/webhooksList endpoints
All endpoints on the account.
Parameters
- limitintegerqueryPage size, 1 to 100. Default 25.
- cursorstringqueryCursor from a previous page’s next_cursor.
Returns
A paginated list of webhook_endpoint objects. Errors use the standard error shape.
Request
curl https://api.os.construction/v1/webhooks \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/webhooks", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
},
});
const page = await res.json();import os, uuid, requests
res = requests.get(
"https://api.os.construction/v1/webhooks",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json()Response · 200
{
"object": "list",
"url": "/v1/webhooks",
"has_more": false,
"next_cursor": null,
"data": [
{
"id": "we_7Hq2",
"object": "webhook_endpoint",
"url": "https://erp.example.com/hooks/os",
"status": "enabled",
"events": [
"change_order.approved",
"pay_app.submitted",
"invoice.coded"
],
"description": "ERP sync",
"secret": "whsec_••••••••3f9a",
"api_version": "2026-10-preview",
"created_at": "2026-10-01T12:00:00Z",
"updated_at": "2026-10-01T12:00:00Z",
"audit_trail": "/v1/webhooks/we_7Hq2/audit"
}
]
}DELETE
/v1/webhooks/{id}Delete an endpoint
Stops deliveries immediately.
Parameters
- idstringpathrequiredThe webhook endpoint ID, e.g. we_7Hq2.
Returns
A deletion confirmation. Errors use the standard error shape.
Request
curl -X DELETE https://api.os.construction/v1/webhooks/we_7Hq2 \
-H "Authorization: Bearer $OS_API_KEY"const res = await fetch("https://api.os.construction/v1/webhooks/we_7Hq2", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.OS_API_KEY}`,
},
});
const obj = await res.json();import os, uuid, requests
res = requests.delete(
"https://api.os.construction/v1/webhooks/we_7Hq2",
headers={
"Authorization": f"Bearer {os.environ['OS_API_KEY']}",
},
)
res.raise_for_status()
data = res.json()Response · 200
{
"id": "we_7Hq2",
"object": "webhook_endpoint",
"deleted": true
}