API reference
Generated from openapi.json 0.12.0-draft (draft)
Public API of the Hetscale control plane: the groups and their projects, the node reports, the release pointer, the inventory API, the deploy lock, the in-place deploy report, the rollout and the Prometheus endpoint, plus the signed deliveries Hetscale sends to the customer's endpoints: the webhook adapter's deployment events and, on Pro, the integrations' timeline events, alerts and tests. Operations marked implemented are served under /v1 on api.hetscale.com; the others are planned and may still change. Eight operations are stable and marked x-hetscale-stability: stable: the ones a deploy pipeline calls to hold and release a group's deploy lock, create a release, list a group's nodes, report an in-place deploy, start a rollout, and report that the deploy to a new node succeeded or failed. Within /v1, what a pipeline sends them and reads from their answers is not removed, renamed or narrowed: the method, path and ability of each, the request fields and their bounds, the success statuses, the answer fields a pipeline uses (the lock's end; the release's id, version, ref and build id and the group's generation; each node's id, class, phase, IPv4 address, private address and release; the rollout's id and its count of older servers; the reported node's phase), the node phases, Retry-After, and the error body and its codes. Everything else in this document is a draft and may still change before it leaves draft.
Base URL: https://api.hetscale.com/v1. Every request and response body is JSON. The specification is the source of these pages; nothing here is written by hand. The file itself is openapi.json (OpenAPI 3.1.0, version 0.12.0-draft). It is a draft, except the operations marked stable, so the rest may still change. Import it into an API client or a code generator.
Authentication
Authorization: Bearer <token> on every request. An organisation's API token from the panel's API tokens page, sent as Authorization: Bearer <id>|<secret>. A token acts for the member who created it and stops working when that member leaves the organisation. It may do only what its abilities allow: nodes:report, releases:write, groups:lock, nodes:read, rollouts:write, metrics:read, groups:read and groups:write. An ability counts only while the member's role holds it: reads for every member, the node reports, releases, the deploy lock and the rollout for an operator, a group's changes for an admin; an ability above the role is answered 403 forbidden.Tokens are never sent in a URL. A token is shown once, when you make it on the API tokens page, and expires after 30, 90 or 365 days, or never; 90 is the default. Each organisation may send 60 requests a minute. A request may carry an Idempotency-Key header: a retry by the same token with the same key and the same request within 24 hours gets the first answer again.
Endpoints
Groups · Projects · Nodes · Releases · Metrics · Webhooks
Errors
Errors return the Error schema:400 when an Idempotency-Key is malformed, 401 when the token is missing or invalid, 403 when it lacks the ability the operation needs or when the role of the member who made the token no longer holds that ability (Team's roles),404 when the group or node is not in your organisation, 409 when a report changes nothing, a request of the same token with the same key is still being handled, the group is not Live or paused for a release or the deploy lock (not Live for a rollout), a rollout already runs or has nothing to replace, or an in-place report names a server that is not a serving managed server of a webhook group, 422 when the body fails validation, names a release the group does not have, or reuses an Idempotency-Key with a different request or from another token, 429 when the rate limit is exceeded, and502 or 503 when Hetzner fails while a release is read back from your project; a 503 may be retried.
Schemas
PolicyRule
| Field | Type | Description |
|---|---|---|
| metric* | cpu_percent · lb_requests_per_second · lb_open_connections | What the rule reads: the servers' CPU in percent, or the load balancer's requests per second or open connections per server. |
| threshold* | number | The value the metric must stay above (scale-out) or below (scale-in) for the whole window. |
| window_seconds* | integer | How long the metric must hold, in seconds. |
* required
GroupSettings
What a member changes on a group: the panel's Settings page, the SSH keys of new servers and the decision mode. It is the body `PATCH /groups/{groupId}` takes, whole or in part, and its digest is the group's `ETag`. The panel's Export settings downloads this block alone. A field it does not list, at any depth, is refused with `validation_failed` under the field's path, and so is a whole group document sent back instead of this block. `minimum` and `maximum` give a number's range, `x-hetscale-initial` the value a new group starts with.
| Field | Type | Description |
|---|---|---|
| min | integer | The fewest servers the group keeps. Bounded by the group: at least the servers that cannot leave, at most max. |
| max | integer | The most servers the group runs: one under the load balancer's target limit once its targets that are not servers of the group are taken off, so a replacement has a target beside the server it replaces, and within the room the private network new servers join has left, one place kept free for a replacement there too. |
| policy | object | The scaling policy. A group keeps the values of the preset it took until a member takes the preset's values of today. A named preset sets every other field, which is accepted only when it matches the values the group keeps or the preset's values today; a body that matches only today's takes all of today's values, not just the fields it sends; naming the group's own preset alone keeps its values; target writes them from its target (one metric and the value to hold, on Pro and Team; plan_required otherwise), from the values the group keeps or from today's Balanced, and they too are accepted only when they match; custom takes each field, and a field left out keeps its value. Scale-in must stay stricter than scale-out. |
| drain_window_seconds | integer | How long a leaving server keeps its connections after it leaves the load balancer: 10 to 300 seconds.Default 60Range 10–300 |
| readiness_deadline_seconds | integer | How long a new server may take to pass the load balancer's health check: 300 to 3,600 seconds.Default 900Range 300–3,600 |
| keep_failed_nodes_seconds | integer | How long a failed server is kept for a look before it is deleted: 0 to 7,200 seconds.Default 0Range 0–7,200 |
| euro_ceiling | integer | null | The monthly euros the group's added servers may cost at list prices, 1 to 1,000,000; null for none.Default nullRange 1–1,000,000 |
| release_age_warning_days | integer | null | The age in days, 1 to 365, at which the current release warns; null for never.Default 30Range 1–365 |
| fallback | object | What happens when the group's type is out of stock. |
| hold_scale_in_while_sold_out | boolean | Keep the servers a scale-in would remove while the central stock poll, read within the last 3 minutes, flags every type and location the group's next create would try as out of stock; at most 24 hours per stock-out. Off by default.Default false |
| placement | spread · none · null | spread puts new servers on different physical hosts; null keeps it. |
| ssh_key_ids | array of integer | The Hetzner ids of the public keys of the group's project that new servers get, among those the latest read of the project found. |
| decision_mode | automatic · propose | automatic acts on the policy's decisions, propose waits for a member to approve each one on the panel. |
GroupSummary
| Field | Type | Description |
|---|---|---|
| id* | string | Group identifier, a ULID prefixed with grp_. |
| name* | string | |
| state* | observe · rehearsing · live · paused | |
| project* | object | null | |
| min* | integer | |
| desired* | integer | |
| max* | integer | |
| archived_at* | string | null (date-time) | When the group went to the Archive, or null. |
* required
GroupDocument
A group as one document, also its JSON export: the panel's Export JSON downloads the same. A webhook secret, the template's user data and any token never appear.
| Field | Type | Description |
|---|
Project
| Field | Type | Description |
|---|---|---|
| id* | string | Project identifier, a ULID prefixed with prj_. |
| name* | string | |
| status* | active · revoked · deleted | Whether Hetscale can still reach the project with its token. |
| token_level* | read · read_write | |
| created_at* | string (date-time) |
* required
CreateGroup
| Field | Type | Description |
|---|---|---|
| project* | string | The project's prj_ id, from GET /projects. |
| load_balancer_id* | integer | The Hetzner id of the load balancer to observe. |
| seed_server_id* | integer | The Hetzner id of one of your servers behind it, the seed new servers are made from. |
| name* | string | |
| server_prefix | string | null | The middle of the group's server names; by default one the name suggests. |
| answers* | object | The panel's case questions, assessed against the project's latest read. |
* required
DeliveryOrganization
The organisation an integration's delivery comes from.
| Field | Type | Description |
|---|---|---|
| id* | string | Organisation identifier, a ULID prefixed with org_. |
| name* | string |
* required
DeliveryGroup
The group a delivery is about.
| Field | Type | Description |
|---|---|---|
| id* | string | Group identifier, a ULID prefixed with grp_. |
| name* | string |
* required
DeliveryNode
The server a delivery is about.
| Field | Type | Description |
|---|---|---|
| id* | string | Node identifier, a ULID prefixed with node_. |
| name* | string | null | The server's name in Hetzner Cloud. |
| server_id* | integer | null | Id of the server in the customer's Hetzner Cloud project. |
* required
Group
| Field | Type | Description |
|---|---|---|
| id* | string | Group identifier, a ULID prefixed with grp_. |
| name* | string | Name of the group as shown in the panel. |
| generation* | integer | Counter incremented on every template or release change; a scaling decision made against an older generation is discarded. In node.created, the generation the node was created for. |
* required
Node
| Field | Type | Description |
|---|---|---|
| id* | string | Node identifier, a ULID prefixed with node_. |
| server_id* | integer | null | Id of the server in the customer's Hetzner Cloud project, or null before it is created. |
| name* | string | null | Server name in Hetzner Cloud: hetscale-<group prefix>-<number> for a server Hetscale created (for example hetscale-web-7), hetscale-<group prefix>-0 for a promoted seed, the customer's own name for an adopted server. |
| class* | adopted · managed | adopted is the seed server the customer already ran, which is never scaled in or replaced; managed nodes were created by Hetscale and have the full lifecycle. |
| phase* | REQUESTED · PROVISIONING · BOOTING · DEPLOYING · HEALTH_CHECKING · ACTIVE · DRAINING · TERMINATING · TERMINATED · PROVISION_FAILED · DEPLOY_FAILED · HEALTH_FAILED · DRAIN_TIMEOUT · TERMINATION_FAILED | Current phase in the node lifecycle; only ACTIVE nodes receive traffic. |
| ipv4* | string | null | Public IPv4 address, or null when the server has none. |
| ipv6* | string | null | Public IPv6 address, or null when the server has none. |
| private_ip* | string | null | Address in the group's private network, or null when the server is not attached to one. |
| location* | string | null | Hetzner Cloud location the server runs in, such as nbg1. |
| server_type* | string | null | Hetzner Cloud server type the server was created with, such as cx23. |
| release* | object | The release the node runs, or null for a server that names none, such as a promoted seed. |
| build_id* | string | null | The build id of the release the node runs, or null when that release has none. |
| phase_entered_at* | string (date-time) | When the node entered its current phase. |
| older* | boolean | True when the node runs another release than the group's current one, none, a template version that differs in what changes a running server, or no placement group while the group spreads its servers across hosts. A rollout replaces it, except while the load balancer no longer lists it as a target. |
* required
Release
| Field | Type | Description |
|---|---|---|
| id* | string | Release identifier, a ULID prefixed with rel_. |
| version | integer | The release's number within its group, v1 the first. |
| ref* | string | The artefact the release boots from: a Hetzner snapshot id, an image digest or a Coolify deployment reference. |
| build_id* | string | null | The customer's own build identifier for the release, or null when none was given. |
| source* | snapshot · image · coolify · api | How the release was created: a snapshot of the seed, an image digest, a Coolify deployment or the releases endpoint. |
| created_at* | string (date-time) | When the release was recorded. |
* required
Error
| Field | Type | Description |
|---|---|---|
| message* | string | Human-readable description of the error. |
| code* | string | Stable machine-readable reason: unauthenticated, forbidden, not_found, method_not_allowed, validation_failed, rate_limited, idempotency_key_invalid, idempotency_key_reused, idempotency_key_in_flight, provider_unavailable, key_service_unavailable, secrets_unavailable, provider_error, plan_required, precondition_failed, or the reason an operation changed nothing, such as stale_generation or image_not_found. |
| errors | object | Validation messages keyed by field name; present on 422 responses only. |
| node | NodeState |
* required
NodeReport
| Field | Type | Description |
|---|---|---|
| release* | string | The release the pipeline deployed, as node.created sent it. |
| generation* | integer | The group generation the node was created for, as node.created sent it.Range ≥ 1 |
* required
NodeState
| Field | Type | Description |
|---|---|---|
| id* | string | Node identifier, a ULID prefixed with node_. |
| phase* | string | Current phase in the node lifecycle, as in Node. |
| generation* | integer | The group generation the node was created for. |
| release* | string | null | Identifier of the release the node boots from, prefixed with rel_. |
| ready_reported_at* | string | null (date-time) | When the first ready report was taken, or null. |
| failed_reported_at* | string | null (date-time) | When the first failure report was taken, or null. |
* required