Webhooks
Generated from openapi.json 0.12.0-draft (draft)
Hetscale sends node.created, node.terminating and group.release_changed to a node group's deployment webhook (the webhook adapter), and timeline, alert and ping to an organisation's webhook integration (Integrations). Every delivery is signed and carries replay protection; deliveries are deduplicated by delivery_id, and a retry repeats the same delivery_id and body, so a receiver that stores seen delivery ids can process a duplicate safely. Respond with any 2xx status; failed deliveries are retried with backoff.
timeline
A timeline event of one of the organisation's groups
Sent to an integration webhook that takes every event (Pro, the Integrations page): one delivery per event of the organisation's groups' timelines, the moment the write it describes commits. event and the X-Hetscale-Event header carry the event's name, such as node.routed, decision.scale_out or group.settings_changed; the timeline's own figures are not sent. Deliveries are not ordered: order them by occurred_at, and deduplicate them by delivery_id.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | string | The timeline event's name. |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. A retry repeats it. |
| sent_at* | string (date-time) | When the delivery was queued; every retry repeats it. |
| occurred_at* | string (date-time) | When the event happened. |
| organization* | DeliveryOrganization | The organisation an integration's delivery comes from. |
| group* | DeliveryGroup | The group a delivery is about. |
| node* | object | |
| actor* | string | Who acted: system, policy, user, api_token and the like. |
| text* | string | null | The reason the timeline shows, in English, or null. |
| url* | string (uri) | The group's timeline on the panel. |
* required
alert
An alert of the organisation
Sent to an integration webhook that takes alerts (Pro, the Integrations page): every alert the organisation's members are mailed, one delivery each, the moment it is raised, never batched. event is alert; alert.kind names it and alert.level is critical, warning or notice. Use it for an on-call tool, a bot or a status page.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | "alert" | |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. A retry repeats it. |
| sent_at* | string (date-time) | When the delivery was queued; every retry repeats it. |
| raised_at* | string (date-time) | When the alert was raised. |
| alert* | object | |
| organization* | DeliveryOrganization | The organisation an integration's delivery comes from. |
| group* | object | |
| node* | object | |
| text* | string | The alert's line from the alert mail, in English. |
| url* | string | null (uri) | The group's timeline on the panel, or null. |
* required
ping
The Integrations page's test
Sent when a member sends a test from the Integrations page. event is ping. It goes even to a suspended integration, and a success resumes it.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | "ping" | |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. A retry repeats it. |
| sent_at* | string (date-time) | When the delivery was queued; every retry repeats it. |
| organization* | DeliveryOrganization | The organisation an integration's delivery comes from. |
| group* | null | |
| node* | null | |
| text* | string | |
| url* | null |
* required
node.created
A new node is booted and reachable
Sent to a webhook group's endpoint once per node, when its server has booted and is reachable (after a boot grace of 30 seconds), so the customer's pipeline deploys the current release to it. The node joins the load balancer once the pipeline reports it ready and takes traffic when its health check then passes; both must happen before deadline_at, or the node is destroyed and an alert is raised, and three consecutive deploy failures pause the group. group.generation is the generation the node was created for: send it back, with release.id, in the node's ready and failed reports. Deliveries are deduplicated by delivery_id, never by node id; a delivery is retried for about 38 minutes and then given up.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | "node.created" | The event name. |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. |
| sent_at* | string (date-time) | When the delivery was sent. |
| group* | Group | |
| node* | Node | |
| release* | Release | |
| deadline_at* | string (date-time) | When the node's readiness deadline expires; the node is then destroyed and an alert is raised. |
* required
{
"event": "node.created",
"delivery_id": "01J8X4V9K2N7Q3R5S6T7V8W9XA",
"sent_at": "2026-09-15T13:02:11Z",
"group": {
"id": "grp_01J8X4V9K2N7Q3R5S6T7V8W9XB",
"name": "api-prod",
"generation": 42
},
"node": {
"id": "node_01J8X4V9K2N7Q3R5S6T7V8W9XC",
"server_id": 12345678,
"name": "hetscale-api-prod-01J8X4V9K2N7Q3R5S6T7V8W9XC",
"class": "managed",
"phase": "DEPLOYING",
"ipv4": "203.0.113.10",
"ipv6": "2001:db8::10",
"private_ip": "10.0.1.10",
"location": "nbg1",
"server_type": "cx23",
"release": {
"id": "rel_01J8X4V9K2N7Q3R5S6T7V8W9XD",
"ref": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"build_id": "4711",
"source": "api",
"created_at": "2026-09-15T12:40:00Z"
},
"build_id": null,
"phase_entered_at": "2026-09-15T13:02:10Z"
},
"release": {
"id": "rel_01J8X4V9K2N7Q3R5S6T7V8W9XD",
"ref": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"build_id": "4711",
"source": "api",
"created_at": "2026-09-15T12:40:00Z"
},
"deadline_at": "2026-09-15T13:17:11Z"
}node.terminating
A node is leaving the group
Sent to a webhook group's endpoint once per node, when the node enters DRAINING, before it is removed from the load balancer. After removal the load balancer sends no new connections to the node and keeps existing ones alive for the group's drain window (default 60 seconds, at most 300 seconds); the node is then shut down and deleted. Deliveries are deduplicated by delivery_id, never by node id.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | "node.terminating" | The event name. |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. |
| sent_at* | string (date-time) | When the delivery was sent. |
| group* | Group | |
| node* | object | The node that is leaving, identified by the same values as in the inventory API. |
| reason* | string | null | Why the node is leaving, as Hetscale recorded it: for example scale_in, rehearsal, user_destroy, heal or manual. |
* required
{
"event": "node.terminating",
"delivery_id": "01J8X4V9K2N7Q3R5S6T7V8W9XE",
"sent_at": "2026-09-15T14:55:20Z",
"group": {
"id": "grp_01J8X4V9K2N7Q3R5S6T7V8W9XB",
"name": "api-prod",
"generation": 42
},
"node": {
"id": "node_01J8X4V9K2N7Q3R5S6T7V8W9XC",
"server_id": 12345678,
"name": "hetscale-api-prod-01J8X4V9K2N7Q3R5S6T7V8W9XC"
},
"reason": "scale-in: CPU below 35% for 10 minutes"
}group.release_changed
The group's current release changed
Sent to a webhook group's endpoint when its current release changes: through the releases endpoint, or when a member makes an earlier release current again on the group's Deployment page. Every node created from now on names the new release in node.created; nodes already running keep the release they were deployed with until a member's rollout replaces them. The group carries its new generation.
Headers
| Header | Type | Description |
|---|---|---|
| X-Hetscale-Signature* | string | One v1=<hex> value per secret that signs now, comma-separated: the HMAC-SHA256 of <timestamp>.<body> with the endpoint's signing secret, where <timestamp> is X-Hetscale-Timestamp and <body> the raw request body. After a rotation the previous secret signs alongside the new one for 24 hours. Accept the delivery when one value matches, before you parse the body. |
| X-Hetscale-Timestamp* | string | Unix time of this attempt, in seconds. Every retry is signed again with its own timestamp while the body stays the same; refuse a delivery whose timestamp is more than five minutes old. |
| X-Hetscale-Delivery* | string | The delivery id, equal to delivery_id in the body. A retry repeats it with the same body, so a receiver that stores the ids it has seen can process a duplicate safely. |
| X-Hetscale-Event* | string | The event, equal to event in the body. |
* required
Payload
| Field | Type | Description |
|---|---|---|
| event* | "group.release_changed" | The event name. |
| delivery_id* | string | Identifier of this delivery, a ULID; the same value is sent in the delivery header. |
| sent_at* | string (date-time) | When the delivery was sent. |
| group* | Group | |
| previous_release* | Release | |
| release* | Release |
* required
{
"event": "group.release_changed",
"delivery_id": "01J8X4V9K2N7Q3R5S6T7V8W9XF",
"sent_at": "2026-09-15T12:40:01Z",
"group": {
"id": "grp_01J8X4V9K2N7Q3R5S6T7V8W9XB",
"name": "api-prod",
"generation": 42
},
"previous_release": {
"id": "rel_01J8X4V9K2N7Q3R5S6T7V8W9XG",
"ref": "sha256:2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
"build_id": "4710",
"source": "api",
"created_at": "2026-09-14T09:15:00Z"
},
"release": {
"id": "rel_01J8X4V9K2N7Q3R5S6T7V8W9XD",
"ref": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"build_id": "4711",
"source": "api",
"created_at": "2026-09-15T12:40:00Z"
}
}