Zaroz
Zaroz Cloud Kernel Open API

List My Subscriptions

GET
/api/v2/subscriptions

Lists subscriptions across every organization the caller may see, paginated: the organizations they hold a membership in, plus their personal organization (design doc §44.2 -- an organization is a shared workspace, so members see each other's subscriptions).

List My Subscriptions › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20
product_id
​string | null · uuid · style: form

Narrows the list to the subscriptions of one product (V-0602). Exact match; the total and the pagination describe the narrowed set. Omitted returns every product. A single id, not a list: a caller needing several products makes one call each.

Narrows the list to the subscriptions of one product (V-0602). Exact match; the total and the pagination describe the narrowed set. Omitted returns every product. A single id, not a list: a caller needing several products makes one call each.

Default: null
project_id
​string | null · uuid · style: form

Narrows the list to one project (§34.1). Omitted returns every subscription the caller may see, including those not yet attributed to a project.

Narrows the list to one project (§34.1). Omitted returns every subscription the caller may see, including those not yet attributed to a project.

Default: null

List My Subscriptions › Responses

Subscriptions

​SubscriptionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Get Subscription

GET
/api/v2/subscriptions/{id}

Gets one subscription from any organization the caller may see: the organizations they hold a membership in, plus their personal organization (design doc §44.2).

Get Subscription › path Parameters

id
​string · uuid · required · style: simple

Get Subscription › Responses

The subscription

One subscription as seen by the organization that owns it, on the `/v2` console surface. `/v2` DTOs are always named `*Request`/`*Response` (design doc `§43.4`) -- there is no `SubscriptionView` here on purpose. Deliberately does not carry `order_id`: that field is this backfill's own bridge back to `/v1`'s model (see `Subscription`'s own doc comment), not part of `/v2`'s vocabulary. `/v2` reaches a subscription's resources through `GET .../subscriptions/{id}/resources`, never by asking a consumer to know about the order underneath.
anchor_at
​string · date-time · required
auto_renew
​boolean · required
cadence_months
​integer · int16 · min: -32768 · max: 32767 · required

The billing period this subscription renews on -- distinct from the pinned Price's own cadence_months (the period its rates are quoted per). See price.cadence_months and design doc §3.4, "cadence_months appears on both prices and subscriptions".

created_at
​string · date-time · required
id
​string · uuid · required
organization_id
​string · uuid · required
​object · required

The Price this subscription is pinned to -- what it renews at. Always the pinned Price's own stored rates, never a fresh selection: design doc §3.4.2 guard rail 1 reserves resolve_acquisition_price for a brand-new purchase, and a subscription's renewal reads its own price_id, never that resolver. The figures below are the Price's stored price_lines verbatim -- no discount, proration, or reseller multiplier is applied, so this can never show a number that does not exist in the pinned row.

product_definition_id
​string · uuid · required
product_id
​string · uuid · required
project_id
​string · uuid · required

The project this subscription belongs to (§34.1's Organization -> Project -> Subscription -> Resource).

Always present. The link is NOT NULL in the schema, so there is no "not yet attributed" case for a reader to handle.

quantity
​integer · int32 · required
status
​SubscriptionStatusResponse · enum · required
Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed
activated_at
​string | null · date-time
cancels_at
​string | null · date-time
​

The hardware profile this subscription's workload runs on, by name. null under the same conditions as region.

internal_address
​string | null

The bare, human-readable Kubernetes Service name for this subscription's internal ClusterIP endpoint (design doc docs/V2Plan/111-internal-naming-and-region.md §7.2) -- e.g. postgres, never the FQDN and never the s-{uuid} form (§7.3). null is the correct, common answer, not a disabled state: it means no provisioned ClusterIpService exists yet for this subscription, or its slug does not qualify as a DNS-1035 label (a leading digit -- Slug::parse allows one, Kubernetes does not). A null must not be read as "this address is unreachable": absence here says only that there is no name to offer at all. Reachability from elsewhere is a separate, pairwise question this field does not answer by itself (§7.1) -- compare region below against the region of whatever would be connecting to it (e.g. the subscription that owns the field this value would be pasted into); the two share a Kubernetes cluster only when their regions match. Kernel does not compute or return that verdict directly, since a single subscription's own read never holds both sides of the comparison.

parent_subscription_id
​string | null · uuid

ZC-0984: the bundle this subscription is a member of, or null for a bundle itself and for every ordinary, standalone subscription.

​

The region this subscription's workload is placed in. null before a hardware profile has resolved (never yet activated, or the profile row itself no longer resolves). Compare id against another subscription's region.id to decide whether internal_address above would resolve from there -- see that field's own doc comment.

trial_converted_at
​string | null · date-time

When billing converted the trial to a paid subscription. Null until conversion.

trial_ends_at
​string | null · date-time

The persisted trial deadline, or null for a subscription without a trial.

​

Whether this lapsed free trial can still be bought (ZC-0782). Null for anything that is not an unconverted free trial past its end -- an ordinary subscription, a running trial, or one already bought. available carries the deadline; payment_pending means a purchase invoice is open (resume paying it); closed means the three days are over and the trial is being deleted.

​

The tax on the next renewal, computed by kernel. Present only on the single-subscription read (getSubscription); null in listings, and when the renewal cannot be calculated (no tax code resolves, or the Price is gone).


Get Attack Summary

GET
/api/v2/me/attack-summary/{date}

What Warden filtered for the caller's own services on one UTC day: episode count, mitigated time, peak rate, blocked traffic, why it was blocked, a per-service breakdown and a timeline. A quiet day answers 200 with zeros. Scoped to service instances of organizations the caller owns, exactly as the daily attack digest email is. Never carries source addresses, topology or gate names.

Get Attack Summary › path Parameters

date
​string · required · style: simple

The UTC day, YYYY-MM-DD.

The UTC day, YYYY-MM-DD.

Get Attack Summary › Responses

The day's summary

What Warden filtered for the caller's services on one UTC day. Numbers are what Warden observed, not a claim of totality, and never carry source addresses, topology or gate names.
attacker_estimate
​integer · uint64 · min: 0 · required
attacks
​integer · uint32 · min: 0 · required
blocked_bytes
​integer · uint64 · min: 0 · required
blocked_packets
​integer · uint64 · min: 0 · required
date
​string · required

The UTC day, YYYY-MM-DD.

mitigated_seconds
​integer · uint64 · min: 0 · required
peak_bits_per_second
​integer · uint64 · min: 0 · required
​AttackSummaryReasonResponse[] · required
​AttackSummaryServiceResponse[] · required
​AttackSummaryTimelineEntryResponse[] · required
services_stayed_online
​boolean | null

Absent when continuity cannot be stated honestly from our own measurements.


Set Subscription Cancellation

PUT
/api/v2/subscriptions/{id}/cancellation

Decides whether this subscription stops at the end of the period it has already been paid for. cancel_at_period_end: true schedules the end and answers with the exact instant in cancels_at; false withdraws a scheduled end and puts the subscription back into service. Unless the staff-only effective_immediately is sent this is not an immediate cancellation, and it never moves money. The service keeps running until the scheduled moment, no invoice is raised or voided, and nothing is credited to the wallet. It also does not change auto_renew, which is returned so a client can see that for itself. Idempotent -- sending the decision already in force succeeds and answers changed: false, which a client must not render as a failure, and a repeated request never moves an existing cancels_at. Refused with a business-rule error for a subscription with no paid period to run out (pending, terminated, cancelled or failed), for one an administrator has cancelled, which the customer may not reinstate, and for a scheduled end whose moment has already passed. Answered 409 when the decision moved underneath the request, which is retryable and most often means a cancellation landed while this withdrawal was in flight. A suspended subscription cannot be cancelled at period end -- that would put a service stopped for non-payment, or an expired free trial, back into service until the period closed -- and a suspended free trial awaiting deletion cannot withdraw that deadline: both answer 409 with code subscription_suspended and change nothing. Settling what is owed lifts the suspension; staff can still end a suspended subscription with effective_immediately: true. Requires the role that may buy for this organization (Owner or Admin), not merely the authority to change what runs inside it; a restricted API token needs subscriptions.cancel on the organization (or globally), and organizations.purchase does not open it. Staff holding ManageProvisioning on an elevated session may also call it for any organization, which is how a customer's billing is stopped from the admin page; such a call is audited as staff. effective_immediately: true is the staff-only variant that ends the subscription now rather than at the close of the paid period -- 403 for anyone else, and 422 when it is sent with cancel_at_period_end: false. An immediate end cannot be withdrawn afterwards by anybody.

Set Subscription Cancellation › path Parameters

id
​string · uuid · required · style: simple

Set Subscription Cancellation › Request Body

The cancellation decision, sent whole. `/v1` had two routes for this, `POST /orders/{id}/cancel` and `POST /orders/{id}/uncancel`. There are no orders in V2, so neither survives -- but the shape would not have been kept anyway, for the reason `SetSubscriptionRenewalRequest` gives: a `PUT` carrying the value the caller wants is idempotent, so a client that retries a request it is unsure landed cannot flip the answer. Two verbs make "cancel twice" and "cancel then uncancel" the same pair of requests in a different order, which is exactly the ambiguity that costs somebody their service.
cancel_at_period_end
​boolean · required

true to stop this subscription at the end of the period already paid for, false to withdraw a scheduled end and keep it running.

effective_immediately
​boolean

Staff only (ZC-0747). true ends the subscription now instead of at the close of the paid period: cancels_at is written at the present instant, which the reaper takes on its next tick.

Defaults to false, so a customer's request means exactly what it meant before this field existed and a staff member who does not ask for it gets the same safe answer. Sending true without the staff system permission is 403; sending it alongside cancel_at_period_end: false is 422, because withdrawing an end and taking one now are contradictory instructions rather than a pair.

It is not withdrawable afterwards, by anyone. The moment has passed by the time any later request arrives, and a passed deadline is refused -- which is /v1's "cancelled by an administrator and cannot be reinstated", reached through the deadline rather than the status.

Default: false

Set Subscription Cancellation › Responses

The subscription's cancellation as it now stands

What the subscription's cancellation now says. Deliberately small, and deliberately not `SubscriptionResponse`, on the same reasoning `SubscriptionRenewalResponse` gives: this write changed one fact, and echoing the pinned Price and the region back would invite a client to believe those could have changed here.
auto_renew
​boolean · required

The renewal setting, untouched by this write and returned so a client can see that for itself. Scheduling an end does not turn renewal off: the billing engine skips every subscription that is not active, so the status alone already stops the next invoice, and leaving this alone is what makes withdrawing the end restore exactly the arrangement that was in force. Change it through PUT /subscriptions/{id}/renewal.

changed
​boolean · required

Whether this request is what changed the decision. false means it already said this -- a retry, or somebody else deciding the same thing first. Both are successes; a client must not render false as a failure. On a retry the existing cancels_at is returned unchanged: a repeated request never moves the customer's end date.

status
​string · enum · required

The subscription's commercial status, which this write does move -- unlike the renewal decision beside it. pending_termination after scheduling, active after withdrawing.

Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed
subscription_id
​string · uuid · required
cancels_at
​string | null · date-time

When this subscription stops. Non-null exactly when status is pending_termination: the two are written in one statement and cannot disagree. Ordinarily it is the close of the billing period the subscription is currently in, computed from its own anchor and cadence, so it is the same instant the renewal invoice would otherwise have been raised. For a staff cancellation sent with effective_immediately it is the present instant instead. Until this moment passes the decision can be withdrawn by sending cancel_at_period_end: false; after it, it cannot.


List Database Tables

GET
/api/v2/databases/{database_id}/tables

Lists every table in one database, addressed by the database's own id. The same route serves a shared database and a dedicated one: this surface does not model how the database was provisioned, only which database it is.

Each entry carries its columns, its primary key and the engine's own size estimates. row_estimate, data_length and index_length are the ENGINE'S estimates, not counts: they are what information_schema reports and can be stale or absent, so render them as approximations and never as a row count. keyset_cursor_column names the column row paging will seek on, and is null for a table that has no usable one, which is what makes listDatabaseTableRows fall back to offset paging there.

truncated is true when the database has more tables than one listing describes. There is no cursor: the engine answers the whole schema in one query, and paging it inside kernel would report a different thing than the database knows.

404 for an unknown database id, for a database belonging to somebody else, and for a database the caller deleted, which are deliberately the same answer.

List Database Tables › path Parameters

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

List Database Tables › Responses

Every table in the database, with its columns

Every table in one database. `truncated` is the honest half of a listing with a ceiling on it: a database with more tables than one request describes says so, rather than handing back a short list that looks complete. There is no cursor, because the engine answers the whole schema in one query and paging it in kernel would be kernel pretending to be the database.
​SharedDatabaseTableView[] · required
database
​string · required

The database's own name on its server, which is the root a browser draws the tree under.

truncated
​boolean · required

Create Database Table

POST
/api/v2/databases/{database_id}/tables

Creates a table from engine-agnostic column definitions (integer, decimal, float, text, boolean, date_time, date, time, json, binary, uuid), which each engine renders in its own dialect. At least one column is required. Refused with 507 when the database has used up its storage quota.

Create Database Table › path Parameters

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Create Database Table › Request Body

​ColumnDefinitionRequest[] · required
name
​string · required

Create Database Table › Responses

Created

No data returned

Get Database Table

GET
/api/v2/databases/{database_id}/tables/{table_name}

One table's schema: the same entry listDatabaseTables publishes, for a single table, so a client opening one table does not have to fetch and filter the whole schema.

404 for a table that is not there, for a database that is not there, and for one belonging to somebody else. A database with more tables than one listing describes (truncated) also answers 404 for the ones past that ceiling, which the listing has already told the client about.

Get Database Table › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Get Database Table › Responses

The table's columns, primary key and size estimates

One table's schema.
​SharedDatabaseTableView · required
database
​string · required

Drop Database Table

DELETE
/api/v2/databases/{database_id}/tables/{table_name}

Drops a table and all of its rows. Permanent: it cannot be undone. Always allowed, including on a database over its quota.

Drop Database Table › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Drop Database Table › Responses

Dropped

No data returned

Alter Database Table

PATCH
/api/v2/databases/{database_id}/tables/{table_name}

Applies a list of structural operations to one table: add, drop, rename or modify a column, or rename the table. At least one operation is required. Refused with 507 when the database has used up its storage quota.

Alter Database Table › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Alter Database Table › Request Body

Alter Database Table › Responses

Altered

No data returned

List Database Table Rows

GET
/api/v2/databases/{database_id}/tables/{table_name}/rows

One page of one table's rows, with optional sorting and filtering.

limit defaults to 50 and is CLAMPED to 500 rather than refused, so a client asking for more gets the most it may have; the page states the limit it was actually served, and a smaller number than was sent means it was clamped. cursor is the opaque next_cursor from the previous page and is never constructed by hand.

sort is column, column:asc or column:desc. filters is a JSON-encoded array of {"column", "operator", "value"}, where operator is one of equals, not_equals, contains, starts_with, ends_with, greater_than, less_than, greater_than_or_equal, less_than_or_equal, is_null, is_not_null; the last two take no value. A sort or filters that does not parse, or that names a column the table does not have, is refused: it is never silently dropped, because an unfiltered page answering a filtered request looks like it worked.

pagination_mode says how the page was produced. keyset seeks on the table's keyset_cursor_column and is stable while rows are being written; offset is used when no such column exists and can repeat or skip a row if the table changes between pages; single_page means the whole result fitted and there is nothing to page. Use has_more and next_cursor rather than comparing the row count to limit.

This route reads; writing a row is insertDatabaseTableRow, updateDatabaseTableRow and deleteDatabaseTableRow.

List Database Table Rows › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

List Database Table Rows › query Parameters

cursor
​string | null · style: form
filters
​string | null · style: form

JSON-encoded array of filter objects (operator, column, value).

JSON-encoded array of filter objects (operator, column, value).

limit
​integer | null · uint16 · min: 0 · max: 65535 · style: form

Rows per page. Defaults to 50, clamped to 500.

Rows per page. Defaults to 50, clamped to 500.

sort
​string | null · style: form

Format: "column" (asc) or "column:asc" / "column:desc".

Format: "column" (asc) or "column:asc" / "column:desc".

List Database Table Rows › Responses

One page of rows

One page of one table's rows. The page carries its own `limit`, `pagination_mode`, `next_cursor` and `has_more`, so a client never infers "there is more" from a full page. A `limit` that comes back smaller than the one sent means the request was clamped, which is deliberately visible rather than silent.
​SharedDatabaseRowsView · required
database
​string · required

Insert Database Table Row

POST
/api/v2/databases/{database_id}/tables/{table_name}/rows

Inserts one row. values maps column names to values; columns are validated against the table's metadata, so a name the table does not have is refused. Refused with 507 when the database has used up its storage quota. Requires the authority to change the organization's projects.

Insert Database Table Row › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Insert Database Table Row › Request Body

​object · required

Insert Database Table Row › Responses

The number of rows inserted

What one row write did.
​SharedDatabaseWriteResultView · required
database
​string · required

Delete Database Table Row

DELETE
/api/v2/databases/{database_id}/tables/{table_name}/rows

Deletes one row, found by the value of the table's single-column primary key. Always allowed, including on a database over its quota: deleting is how a customer recovers.

Delete Database Table Row › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Delete Database Table Row › Request Body

primary_key_value
​required

Arbitrary JSON.

Delete Database Table Row › Responses

The number of rows deleted

What one row write did.
​SharedDatabaseWriteResultView · required
database
​string · required

Update Database Table Row

PATCH
/api/v2/databases/{database_id}/tables/{table_name}/rows

Updates one row, found by the value of the table's single-column primary key. A table with no primary key, or a composite one, cannot be edited this way. Refused with 507 when the database has used up its storage quota.

Update Database Table Row › path Parameters

table_name
​string · required · style: simple

The table's name, exactly as listDatabaseTables reported it.

The table's name, exactly as listDatabaseTables reported it.

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Update Database Table Row › Request Body

​object · required
primary_key_value
​required

Arbitrary JSON.

Update Database Table Row › Responses

The number of rows updated

What one row write did.
​SharedDatabaseWriteResultView · required
database
​string · required

Get Database Usage

GET
/api/v2/databases/{database_id}/usage

How much storage the database uses against its quota: data and index bytes as the engine reports them, and the quota in bytes (null for a database that has none, such as a dedicated one). over_quota_since is set while the database is locked read-only for exceeding its quota; deleting data releases it.

The figure is cached for up to a minute and refreshed by any write made through this API.

Get Database Usage › path Parameters

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Get Database Usage › Responses

The database's storage usage and quota

​DatabaseUsageView · required

How full a database is.

database
​string · required

Execute Database SQL

POST
/api/v2/databases/{database_id}/sql

Runs ONE raw SQL statement against the database, as the database's own user, so it can do anything that user can to its own database. A statement that returns rows answers {kind: "rows"} capped at 200 rows (truncated says when it was cut); any other answers {kind: "command", affected_rows}. The statement is cancelled after 15 seconds. Refused with 507 when the database has used up its storage quota, since raw SQL is not restricted to statements that shrink it. The statement is recorded in the organization's audit feed.

Execute Database SQL › path Parameters

database_id
​string · uuid · required · style: simple

The database's own id, whatever kind of database it is.

The database's own id, whatever kind of database it is.

Execute Database SQL › Request Body

sql
​string · required

Execute Database SQL › Responses

The statement's result set or affected-row count

What one raw SQL statement did: either a result set (capped at 200 rows, `truncated` says when it was) or the count of rows it affected.
​SharedDatabaseSqlResultView · required
database
​string · required

List Service Endpoints

GET
/api/v2/projects/{project_id}/service-instances/{id}/endpoints

The public ports of a service: first the ones asked for at purchase that are not open yet (pending, with a pending_reason and nothing billed), then every port the service ever held, newest first, with its public address and port while it is active. A released port is kept so a month's bill can be read against it. The list also carries what an https add control needs: https_limit and https_count, domain_limit and domain_count (so the add buttons can disable themselves), and https_zone / https_host_suffix (the zone a new free host lands in, to preview <label>.<https_host_suffix>). Each https endpoint's domains carry a url, a failure code while failed, and the DNS record (dns) to create. Console only.

List Service Endpoints › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Service Endpoints › Responses

The service's public ports

`GET .../endpoints`. Not paginated: a service holds at most a few dozen ports. Pending requests come first, then every endpoint the service ever held, newest first.
​EndpointResponse[] · required
domain_count
​integer · uint · min: 0 · required

Customer domains attached now.

domain_limit
​integer · uint · min: 0 · required

The most customer domains one service may hold, across all its https endpoints (domain_limit when exceeded).

https_count
​integer · uint · min: 0 · required

Https endpoints open now (pending purchase requests are not counted).

https_limit
​integer · uint · min: 0 · required

The most https endpoints one service may hold open. An add button disables itself when https_count reaches it; the add is refused with https_limit past it.

https_host_suffix
​string | null

What follows the label in a free host, zone.<zone>.zaroz.cloud: preview the address as <label>. plus this. Present exactly when https_zone is. Before an endpoint exists the label is optional and the endpoint's id names the host.

https_zone
​string | null

The zone the free address of a new https endpoint lands in, e.g. hel1. Absent when the service has no placement to read yet.


Add Service Endpoint

POST
/api/v2/projects/{project_id}/service-instances/{id}/endpoints

Opens one endpoint on the service, of one of three kinds. kind: port (the default, so a request without a kind means what it always did) opens a raw TCP/UDP public port: send the container port and the protocol (tcp, udp or tcp_udp); Zaroz assigns the public address and port, which come back on the response. Ports beyond the plan's included ones are billed by the hour they are held, monthly in arrears. kind: unit opens a raw port like port but one per unit: every unit of a scaled Container gets its own public address and port for that container port, reaching that unit alone, and a new unit gets its own when it is added. The address stays the same across the unit's restarts and is released when the unit is scaled away; each is billed like a port, and removing one removes the same port from every unit. GET units lists each unit's addresses. kind: https publishes a website or API (WebSockets included): send the container port, and optionally a host label and the customer's own domains. It is served on a free platform host (https.host) with automatic TLS, the app serving plain HTTP; each domain must be one the organization has verified or a subdomain of one, exactly as for a Web App, and a domain that later loses verification stops being served. An https endpoint is free and never metered, and all or nothing: one refused domain opens nothing. A refusal names a reason code before the colon in details: not_ready (the service is not running yet), port_limit (the plan's ports are all used), port_in_use (that container port is already open with that protocol, or already has an https endpoint, or a per-unit port), no_address_available and no_port_available (nothing free right now), internal_port_out_of_range, unknown_protocol, protocol_required, invalid_host_label, host_taken, https_limit, and for a domain invalid_domain, platform_domain, domain_taken, domain_not_verified, domain_limit. Console only.

Add Service Endpoint › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Add Service Endpoint › Request Body

`POST .../endpoints`. For a raw `port` the customer names the container port and the protocol(s) and nothing else: the public address and port are assigned. For `https` they name the container port, and optionally the host label and their verified domains.
internal_port
​integer · uint32 · min: 1 · max: 65535 · required

The port the service listens on inside the container, 1 to 65535.

domains
​string[]

https only: the customer's own domains to serve on this endpoint. Each must be a domain the organization has verified, or a subdomain of one, exactly as for a Web App; certificates are issued automatically. All or nothing: one refused domain opens nothing. More can be added later.

Default: []
kind
​string · enum

port (the default, so a request written before kinds existed means what it always did), unit (a raw port per unit, see Add Service Endpoint) or https.

Enum values:
port
https
unit
Default: port
label
​string | null

https only: the label of the free host (<label>.zone.<zone>.zaroz.cloud), one DNS label. Omitted, the host is named after the endpoint's id.

Default: null
​

For port and unit: tcp, udp, or tcp_udp for one public number on both; required. For https: omit it (always TCP, TLS ends at the platform and the app serves plain HTTP); tcp is accepted.

Default: null

Add Service Endpoint › Responses

The opened endpoint: its public address for a port, its host and domains for an https endpoint

created_at
​string · date-time · required

When the port was opened, or, while pending, when it was asked for.

id
​string · uuid · required

The id to DELETE. For a pending request this is the request's id.

internal_port
​integer · uint16 · min: 0 · max: 65535 · required

The port the service listens on inside the container.

kind
​string · enum · required

port (raw TCP/UDP, billed), unit (a raw port that reaches one unit, billed like a port) or https (a host and domains, free).

Enum values:
port
https
unit
​EndpointOriginResponse · required
protocol
​EndpointProtocols · enum · required

The protocols one endpoint carries. TcpUdp is one endpoint, one price, over one allocation carrying both labels ([PortProtocol::Both]).

Enum values:
tcp
udp
tcp_udp
​EndpointStateResponse · required
​

The host and domains of an https endpoint. Absent for a port, which has the public_* fields instead.

last_public_ip
​string | null

The public address the port had, once it is closed, so a month's bill can be read against it. Absent while the port is open, while it is pending, and for a port closed before the address was kept.

last_public_port
​integer | null · uint16 · min: 0 · max: 65535

The public port the closed port had. Present exactly when last_public_ip is.

​

Why the port is not open yet. Present only while state is pending.

public_ip
​string | null

The public address, while the port is open.

public_port
​integer | null · uint16 · min: 0 · max: 65535

The public port Zaroz assigned, while the port is open.

released_at
​string | null · date-time

When the port was closed.

unit_ordinal
​integer | null · int32

The unit slot a unit endpoint reaches. Absent for the other kinds.


Remove Service Endpoint

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/endpoints/{endpoint_id}

Closes one endpoint. A raw port stops its billing from that moment (nothing was prepaid, so there is nothing to refund); an https endpoint takes down its host, its customer domains and their certificates. The id is the one the listing returned; for a port that was asked for at purchase and is still pending it withdraws the request. Closing a port that is already closed succeeds. An id that is not one of this service's ports, including one belonging to another service, is a 404: the same answer either way. Console only.

Remove Service Endpoint › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
endpoint_id
​string · uuid · required · style: simple

Remove Service Endpoint › Responses

Removed

No data returned

Add Service Endpoint Domain

POST
/api/v2/projects/{project_id}/service-instances/{id}/endpoints/{endpoint_id}/domains

Serves one more of the customer's own domains on an https endpoint, with an automatic certificate. The domain must be one the organization has verified, or a subdomain of one: the same rule and the same certificate issuance as a Web App's custom domain. Attaching a domain already attached to this endpoint returns it. Until the certificate is issued the domain reports pending; point its DNS at the platform for issuance to succeed. A raw port, a closed endpoint and another service's endpoint are a 404. A refusal names its code before the colon in details: 400 invalid_domain for a name that is not a domain, and 422 platform_domain, domain_taken, domain_not_verified, domain_limit, not_ready. Console only.

Add Service Endpoint Domain › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
endpoint_id
​string · uuid · required · style: simple

Add Service Endpoint Domain › Request Body

`POST .../endpoints/{endpoint_id}/domains`.
domain
​string · required

A domain the organization has verified, or a subdomain of one.

Add Service Endpoint Domain › Responses

The attached domain with its state

One customer domain on an https endpoint.
domain
​string · required
id
​string · uuid · required

The id to DELETE .../endpoints/{endpoint_id}/domains/{id}.

​required

Verification state of what is served on it: the domain itself was verified for the organization before it could be attached (a domain that loses verification stops being served and disappears from here); this is the route and certificate.

url
​string · required

https:// plus domain.

​

The DNS record the customer creates so the domain reaches this endpoint. Present on reads of an endpoint once its free host is known; absent on the response to attaching a domain (read the endpoint again).

​

Why the domain is failed: a stable code and a sentence. Present only while status is failed. certificate_failed (usually DNS not pointing at the platform yet) or route_failed.


Remove Service Endpoint Domain

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/endpoints/{endpoint_id}/domains/{route_id}

Stops serving one customer domain on an https endpoint and removes its certificate; the domain can be attached again later. The id is the one the endpoint's https.domains returned. A domain that is not on this endpoint, including one of another endpoint or service, is a 404: the same answer either way. Console only.

Remove Service Endpoint Domain › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
endpoint_id
​string · uuid · required · style: simple
route_id
​string · uuid · required · style: simple

The id the endpoint's https.domains returned.

The id the endpoint's https.domains returned.

Remove Service Endpoint Domain › Responses

Removed

No data returned

Set Service Endpoint Domain Paths

PUT
/api/v2/projects/{project_id}/service-instances/{id}/endpoints/{endpoint_id}/domains/{route_id}/paths

Replaces the ordered path routes of one customer domain on an https endpoint, so app.com can reach one service and app.com/api another. Each path names an open https endpoint of a service in the same project and whether to strip the path before the request reaches it. The longest matching path wins and the endpoint the domain is attached to answers everything else. The whole set is replaced: send an empty list to remove every path. Returns the effective routing the new set produces. A refusal names its code before the colon in details: 400 invalid_path (grammar), target_not_found (another project's, a missing and a closed endpoint answer alike); 422 path_limit, duplicate_path, target_not_http. A domain that is not on this endpoint is a 404. Console only.

Set Service Endpoint Domain Paths › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
endpoint_id
​string · uuid · required · style: simple
route_id
​string · uuid · required · style: simple

The id the endpoint's https.domains returned.

The id the endpoint's https.domains returned.

Set Service Endpoint Domain Paths › Request Body

`PUT .../endpoints/{endpoint_id}/domains/{route_id}/paths`: the whole ordered set of path routes of one domain. An empty list removes them all.
​PathRouteRequest[] · required

Set Service Endpoint Domain Paths › Responses

The domain's effective routing after the change

The effective routing of one customer domain.
host
​string · required
path_limit
​integer · uint · min: 0 · required

The most path routes one domain may carry.

​RoutingRuleResponse[] · required

In the order they are evaluated; the last is always the default.

​RoutingTlsResponse · required
​RoutingUsedByResponse[] · required

Every service that answers some part of the domain, with the paths it serves.

​

Present when the request carried ?path=.


Get Service Endpoint Domain Routing

GET
/api/v2/projects/{project_id}/service-instances/{id}/endpoints/{endpoint_id}/domains/{route_id}/routing

How a customer domain routes now: its rules in the order they are evaluated, each with the exact rule, priority, target service and its state, and a status (serving, starting, shadowed when another rule with the same path always wins, broken when the target cannot answer); the certificate state, which covers the whole domain; and every service that uses the domain with the paths it serves. With ?path=/api/users it also says which rule takes that request and what path the target receives. An invalid path is a 400 invalid_sample_path. Console only.

Get Service Endpoint Domain Routing › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
endpoint_id
​string · uuid · required · style: simple
route_id
​string · uuid · required · style: simple

The id the endpoint's https.domains returned.

The id the endpoint's https.domains returned.

Get Service Endpoint Domain Routing › query Parameters

path
​string | null · style: form

A request path to trace, like /api/users?x=1: which rule takes it and what the target receives. The query string is ignored.

A request path to trace, like /api/users?x=1: which rule takes it and what the target receives. The query string is ignored.

Get Service Endpoint Domain Routing › Responses

The domain's effective routing

The effective routing of one customer domain.
host
​string · required
path_limit
​integer · uint · min: 0 · required

The most path routes one domain may carry.

​RoutingRuleResponse[] · required

In the order they are evaluated; the last is always the default.

​RoutingTlsResponse · required
​RoutingUsedByResponse[] · required

Every service that answers some part of the domain, with the paths it serves.

​

Present when the request carried ?path=.


Set Subscription Renewal

PUT
/api/v2/subscriptions/{id}/renewal

Decides whether this subscription is charged again when its current period ends. This is not a cancellation. Setting auto_renew to false stops the next invoice and nothing else: the subscription keeps its status, no end date is scheduled, and the service keeps running. A customer who wants the service to stop, rather than the billing, wants PUT /subscriptions/{id}/cancellation instead. Idempotent -- sending the value already stored succeeds and answers changed: false, which a client must not render as a failure. Refused with a business-rule error for a subscription that has no next period to decide about (already ending, terminated, cancelled or failed), and for one paid once under a lifetime price, which has no renewal at all. A suspended subscription may stop renewing but may not start again: suspension is the platform's consequence for an unpaid or disputed invoice and it turns renewal off as part of itself, so resuming it goes through lifting the suspension. Answered 409 when the setting moved underneath the request, which is retryable. Requires the role that may buy for this organization (Owner or Admin), not merely the authority to change what runs inside it; a restricted API token needs subscriptions.renewal.manage on the organization (or globally), and organizations.purchase does not open it.

Set Subscription Renewal › path Parameters

id
​string · uuid · required · style: simple

Set Subscription Renewal › Request Body

The renewal decision, sent whole. A `PUT` with the complete value rather than a `PATCH` with an optional one: there is exactly one field, so "omitted" and "unchanged" would be the same request and a client could never tell whether its write was understood. Sending the value it wants makes the request idempotent and makes a retry safe.
auto_renew
​boolean · required

true to be charged again when the current period ends, false to stop after it.

Set Subscription Renewal › Responses

The subscription's renewal setting as it now stands

What the subscription's renewal now says. Deliberately small, and deliberately not `SubscriptionResponse`. This write changed one fact; echoing the pinned Price, the region and the internal address back would invite a client to believe those could have changed here, and would cost four more reads on every call to say something the `GET` already says better.
auto_renew
​boolean · required
changed
​boolean · required

Whether this request is what changed the setting. false means it already said this -- a retry, or somebody else deciding the same thing first. Both are successes; a client must not render false as a failure.

status
​string · enum · required

The subscription's commercial status, unchanged by this write.

Returned so a client can see that for itself rather than take it on trust: turning renewal off is not a cancellation, and an Active subscription is still Active afterwards.

Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed
subscription_id
​string · uuid · required
cancels_at
​string | null · date-time

When this subscription is scheduled to end. Unchanged by this route, which is why it is returned: turning renewal off is not a cancellation, and a client can see here that no end was scheduled. It is non-null only for a subscription somebody scheduled to end through PUT /subscriptions/{id}/cancellation, which is the only writer of this field.


Quote a subscription resize

POST
/api/v2/subscriptions/{id}/resize/quote

Prices a shape change against the subscription's own pinned price without committing to it, and reports per dimension whether stock currently allows growing it. Writes nothing and reserves nothing -- the stock read is a live, non-reserving preview, so it can go stale by the time POST .../resize is called. Send a full target shape, not a delta: a dimension left unchanged is sent at its current value. cpu, memory and storage may all change in one request, each up or down (storage only up), within the product's own minimum and maximum. Growth is priced now; a reduction is free and reported with downsize_applies_at, the end of the current billing period.

Quote a subscription resize › path Parameters

id
​string · uuid · required · style: simple

Quote a subscription resize › Request Body

​ResizeResourcesRequest · required

The target shape a resize names, in the same units CheckoutResourcesRequest already uses -- see that type's own doc comment. A resize always names a full target shape, not a delta: the dimension a customer does not want to change is simply sent at its current value. Any of the three may change in the same request, each up or down (storage only up); a request replaces any reduction scheduled earlier, so send a scheduled dimension at its scheduled value to keep it.

Quote a subscription resize › Responses

What the resize would cost, and whether stock allows the growth

​ResizeBillingResponse · required
​ResizeDimensionResponse[] · required
downsize_applies_at
​string | null · date-time

When the shrinking dimensions take effect: the end of the current billing period, right before the next renewal is priced at the smaller shape. null when nothing shrinks.


Request a subscription resize

POST
/api/v2/subscriptions/{id}/resize

Requests a shape change for this subscription: cpu, memory and storage together, each up or down (storage only up), within the product's own minimum and maximum. The GROWING dimensions reserve capacity for 30 minutes and raise a Pending Resize invoice for the prorated difference -- pay it through the existing invoice-id-keyed payment endpoints, and the growth applies once payment settles. The SHRINKING dimensions are never billed and never credited: they are scheduled (scheduled_downsize) and take effect at the end of the current billing period, right before the next renewal is priced at the smaller shape. A request replaces any reduction scheduled earlier. Requires the role that may buy for this organization (Owner or Admin); a restricted API token needs orders.billing_resize.manage on the organization (or globally), as every resize route does.

Request a subscription resize › path Parameters

id
​string · uuid · required · style: simple

Request a subscription resize › Request Body

​ResizeResourcesRequest · required

The target shape a resize names, in the same units CheckoutResourcesRequest already uses -- see that type's own doc comment. A resize always names a full target shape, not a delta: the dimension a customer does not want to change is simply sent at its current value. Any of the three may change in the same request, each up or down (storage only up); a request replaces any reduction scheduled earlier, so send a scheduled dimension at its scheduled value to keep it.

Request a subscription resize › Responses

The invoice to pay for a growth, and/or the scheduled reduction

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: outcome, invoice
type = object · requires: outcome, scheduled_downsize
Properties for Variant 1:
Something grows: pay `invoice` to settle the growth. `scheduled_downsize` is the reduction half of a request that also shrank a dimension, `null` otherwise.
​ResizeInvoiceResponse · required

A minimal, self-contained invoice summary for a resize response -- deliberately not the full InvoiceResponse from kernel-http-v2-billing (a cross-crate dependency this feature crate does not otherwise carry): enough for a client to know what it owes and pay it through the existing invoice-id-keyed payment endpoints, nothing more.

outcome
​string · const · required
Const value: invoice
​

Cancel a subscription resize

POST
/api/v2/subscriptions/{id}/resize/cancel

Cancels this subscription's pending resize, if any. For a pending GROWTH, voids its Pending invoice and releases the capacity hold it took, in one transaction. For a scheduled DOWNSIZE, drops it; the running service is never touched, since a downsize only ever changes it at the end of the billing period. Both are cancelled when both are pending. Refuses with 409 only when the one thing pending is a growth whose invoice has already been paid, since money has moved and the growth is on its way to being applied. Idempotent: calling this when nothing is pending answers 204, the same status a successful cancel answers.

Cancel a subscription resize › path Parameters

id
​string · uuid · required · style: simple

Cancel a subscription resize › Responses

The pending resize (if any) was cancelled, or there was none to cancel -- both answer the same way

No data returned

Quote a subscription plan change

POST
/api/v2/subscriptions/{id}/resize/plan-change/quote

Prices a change to another of this product's own authored catalog plans (plan_id, from GET /catalog/products/{id}/plans) against the subscription's own pinned price, without committing to it -- the plan-change equivalent of resize/quote. Refused if this product's ResizePolicy does not offer a self-service plan change, or if plan_id does not name one of this product's own active, storefront-visible plans.

Quote a subscription plan change › path Parameters

id
​string · uuid · required · style: simple

Quote a subscription plan change › Request Body

`ZC-1391`'s self-service plan change: names another of this product's own authored catalog `Plan`s (`i32`, `impl_int_id!` -- not a UUID, the same convention `CheckoutRequest::plan_id` already uses) rather than a raw target shape. The picker itself is `GET /api/v2/catalog/products/{id}/plans`, the same storefront listing a purchase uses -- this route does not duplicate it.
plan_id
​integer · int32 · required

Quote a subscription plan change › Responses

What the plan change would cost, and whether stock allows the growth

​ResizeBillingResponse · required
​ResizeDimensionResponse[] · required
downsize_applies_at
​string | null · date-time

When the shrinking dimensions take effect: the end of the current billing period, right before the next renewal is priced at the smaller shape. null when nothing shrinks.


Request a subscription plan change

POST
/api/v2/subscriptions/{id}/resize/plan-change

Requests a change to another of this product's own authored catalog plans (plan_id, from GET /catalog/products/{id}/plans). For a GROWTH, reserves capacity for 30 minutes and raises a Pending Resize invoice for the prorated difference, exactly like POST .../resize. Dimensions the plan makes smaller are not billed and take effect at the end of the current billing period. Refused if this product's ResizePolicy does not offer a self-service plan change, if plan_id does not name one of this product's own active, storefront-visible plans, or if the plan has less storage than the service runs today (storage only grows).

Request a subscription plan change › path Parameters

id
​string · uuid · required · style: simple

Request a subscription plan change › Request Body

`ZC-1391`'s self-service plan change: names another of this product's own authored catalog `Plan`s (`i32`, `impl_int_id!` -- not a UUID, the same convention `CheckoutRequest::plan_id` already uses) rather than a raw target shape. The picker itself is `GET /api/v2/catalog/products/{id}/plans`, the same storefront listing a purchase uses -- this route does not duplicate it.
plan_id
​integer · int32 · required

Request a subscription plan change › Responses

The invoice to pay for a growth, or the scheduled downsize record

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: outcome, invoice
type = object · requires: outcome, scheduled_downsize
Properties for Variant 1:
Something grows: pay `invoice` to settle the growth. `scheduled_downsize` is the reduction half of a request that also shrank a dimension, `null` otherwise.
​ResizeInvoiceResponse · required

A minimal, self-contained invoice summary for a resize response -- deliberately not the full InvoiceResponse from kernel-http-v2-billing (a cross-crate dependency this feature crate does not otherwise carry): enough for a client to know what it owes and pay it through the existing invoice-id-keyed payment endpoints, nothing more.

outcome
​string · const · required
Const value: invoice
​

Batch Service Instance Runtime Stats

GET
/api/v2/service-instances/runtime-stats

Returns live uptime, CPU (millicores) and memory (bytes) for the primary workload of each requested service instance, so a page of service cards fills its runtime chips in one request. Replaces /v1's POST /orders/runtime-stats. At most 50 ids are read (the first 50, repeats dropped). An instance the caller may not see, one whose subscription is no longer served, and one the cluster could not read is omitted from the answer rather than refused, so the response never reveals which ids exist.

Batch Service Instance Runtime Stats › query Parameters

ids
​string · required · style: form

Comma-separated service instance ids. Anything that is not a UUID is ignored, as is an id the caller may not see.

Comma-separated service instance ids. Anything that is not a UUID is ignored, as is an id the caller may not see.

Batch Service Instance Runtime Stats › Responses

Only instances the caller may see and the cluster answered for appear; an instance with no reading is absent, never zero.

Only instances the caller may see and the cluster answered for appear; an instance with no reading is absent, never zero.

List Scanner Subscriptions

GET
/api/v2/scanner/subscriptions

Lists the subscriptions of every product scoped to the manage_zaroz_scanner permission (product_staff_scopes), newest first, paginated, each with its status, product, current billing period, organization, owning user (id, display name, email) and a display name. Requires manage_zaroz_scanner; an elevated session is NOT required, and API tokens of every kind are refused. Read-only, and carries no price or billing data.

List Scanner Subscriptions › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20
status
​style: form

Narrows the list to one commercial status. Omitted returns every status.

Narrows the list to one commercial status. Omitted returns every status.

List Scanner Subscriptions › Responses

Scanner subscriptions

​ScannerSubscriptionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Get Scanner Subscription

GET
/api/v2/scanner/subscriptions/{id}

Gets one subscription of a product scoped to manage_zaroz_scanner. A subscription of any other product answers 404, indistinguishable from one that does not exist. Same gate as the list: manage_zaroz_scanner, no elevated session, no API tokens.

Get Scanner Subscription › path Parameters

id
​string · uuid · required · style: simple

Get Scanner Subscription › Responses

The subscription

One subscription of a staff-scoped product, as the product's operators see it: enough to tell tenants apart, and nothing about money. Deliberately carries no price, invoice, payment method or resource: the route exists so a product's operations team can see who the tenants are, not to read a customer's billing.
created_at
​string · date-time · required
display_name
​string · required

The organization's name, with a distinct owner's name in parentheses.

id
​string · uuid · required

The subscription id, which is the tenant key.

​ScannerSubscriptionOrganizationResponse · required
product_id
​string · uuid · required
product_slug
​string · required
status
​SubscriptionStatusResponse · enum · required
Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed
​

null when the subscription is not billing (pending or ended).

​

null when the organization has no owner left.


List My Service Instances

GET
/api/v2/service-instances

Lists the signed-in customer's own service instances across every organization they may see -- the organizations they hold a membership in, plus their personal organization (same scope listMySubscriptions uses). There are no orders in /v2: this is the customer-scoped replacement for /v1's GET /api/v1/orders, keyed on subscription_id, never on an order id.

run_state and availability are read from the database, not a live cluster call: run_state folds each instance's own resources' last recorded statuses (the same fold the live status stream uses), and availability reports whether the platform can currently resolve a cluster for the instance's hardware profile, not whether that cluster answered a live health check. A cluster that cannot be resolved for one row never fails the rest of the list.

List My Service Instances › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20
project_id
​string | null · uuid · style: form

Narrows the list to one project. Omitted returns every service instance the caller may see, including those not yet attributed to a project.

Narrows the list to one project. Omitted returns every service instance the caller may see, including those not yet attributed to a project.

Default: null

List My Service Instances › Responses

The caller's own service instances, paginated

​MyServiceInstanceSummaryResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Get Service Instance

GET
/api/v2/service-instances/{id}

Gets one running service the caller may see.

Get Service Instance › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance › Responses

The service instance

One running service, as the customer refers to it. A subscription is a purchase of a *product* and carries no name; this is the thing that does -- their VPS, their container. See `2026-09-02-000004_service_instance_identity`.
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
project_id
​string · uuid · required

The project this service is addressed under. Its slug is unique here, not platform-wide.

​object · required

How much CPU, memory and storage this service is currently configured with (ZC-1287). The same shape MyServiceInstanceSummaryResponse carries per row, published here because until now it was carried ONLY there -- on the route that returns every service the caller owns. A client that wants one service's size had to page the caller's whole inventory to find three numbers, and that listing filters by project_id only, so there was no way to ask about one. That blocked the resize screen, whose form has to open showing what the customer already has. This is the configured shape, which is what the customer bought and what a resize names a full target against -- not a live measurement of what the workload is using. GET .../resources/{resource_id}/volume is where a measurement lives.

slug
​string · required
subscription_id
​string · uuid · required
updated_at
​string · date-time · required
​

Set only while the debt ladder holds this service stopped (V-0482): own_debt when its organization has an unpaid balance, restricted when it is frozen because of a debt elsewhere (contact the organization's owner). null otherwise, and always null on routes other than GET ./service-instances and GET ./service-instances/{id}. Never identifies the debtor.

operating_system_id
​string | null · uuid

For a VPS, the id of the operating system it runs, as listed by GET /api/v2/catalog/products/{id}/operating-systems (V-0671). A client uses it to offer only reinstall targets with the same monthly fee. null for every other service.

​

This subscription's pending resize (ZC-1332), or null when there is none. GET .../service-instances/{id} is the only route that populates this -- every other route returning this same shape (rename, reprovision, ...) always answers null here rather than resolving it, since none of them has a reason to.


Update Service Instance

PATCH
/api/v2/service-instances/{id}

Renames and/or re-slugs one running service. Omitted fields are left unchanged. Renaming never moves the slug: a slug change is a separate, explicit field, because it moves every URL pointing at the service.

Update Service Instance › path Parameters

id
​string · uuid · required · style: simple

Update Service Instance › Request Body

Omitted fields are left unchanged. `name` and `slug` are separate on purpose: renaming is a display change, re-slugging moves every URL pointing at the service. A client that wants both sends both, deliberately.
name
​string | null
slug
​string | null

Update Service Instance › Responses

The instance as stored

One running service, as the customer refers to it. A subscription is a purchase of a *product* and carries no name; this is the thing that does -- their VPS, their container. See `2026-09-02-000004_service_instance_identity`.
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
project_id
​string · uuid · required

The project this service is addressed under. Its slug is unique here, not platform-wide.

​object · required

How much CPU, memory and storage this service is currently configured with (ZC-1287). The same shape MyServiceInstanceSummaryResponse carries per row, published here because until now it was carried ONLY there -- on the route that returns every service the caller owns. A client that wants one service's size had to page the caller's whole inventory to find three numbers, and that listing filters by project_id only, so there was no way to ask about one. That blocked the resize screen, whose form has to open showing what the customer already has. This is the configured shape, which is what the customer bought and what a resize names a full target against -- not a live measurement of what the workload is using. GET .../resources/{resource_id}/volume is where a measurement lives.

slug
​string · required
subscription_id
​string · uuid · required
updated_at
​string · date-time · required
​

Set only while the debt ladder holds this service stopped (V-0482): own_debt when its organization has an unpaid balance, restricted when it is frozen because of a debt elsewhere (contact the organization's owner). null otherwise, and always null on routes other than GET ./service-instances and GET ./service-instances/{id}. Never identifies the debtor.

operating_system_id
​string | null · uuid

For a VPS, the id of the operating system it runs, as listed by GET /api/v2/catalog/products/{id}/operating-systems (V-0671). A client uses it to offer only reinstall targets with the same monthly fee. null for every other service.

​

This subscription's pending resize (ZC-1332), or null when there is none. GET .../service-instances/{id} is the only route that populates this -- every other route returning this same shape (rename, reprovision, ...) always answers null here rather than resolving it, since none of them has a reason to.


Get Service Instance Capabilities

GET
/api/v2/service-instances/{id}/capabilities

Lists what this running service can do: the structural capabilities read off its own resources, and the behavioural ones its product declares.

Get Service Instance Capabilities › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Capabilities › Responses

Every capability this instance exposes: the structural ones derived from its own resources, and the behavioural ones its product declares. Structural wins on a collision.

​Capability[]
oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability, root_path
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability, mount_path, resource_id
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability, verbs
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability, id, url +1 more
type = object · requires: capability, url, resource_id +1 more
type = object · requires: capability, secrets_allowed
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
type = object · requires: capability
Properties for Variant 1:
capability
​string · const · required
Const value: log_stream

Get Service Instance Features

GET
/api/v2/service-instances/{id}/features

Lists the product features this running service's product declares: what only the product understands, such as a game's player roster, as opposed to the platform capabilities listed by the sibling capabilities route. Ids only; a client keys its own panels and copy off them. Grants nothing: each feature's own route enforces its own access.

Get Service Instance Features › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Features › Responses

The features this instance's product declares, possibly none.

The `ProductFeature`s one running service's product declares (`ZC-1278`). A wrapper rather than a bare array, unlike `GET .../capabilities`, so the answer can grow a field without breaking every generated client.

Get Service Instance Minecraft Inventory

GET
/api/v2/service-instances/{id}/minecraft/inventory

Lists the jars currently in this Minecraft instance's mods/ or plugins/ folder, merged against kernel's own background-resolved cache. The listing never waits on a hash or on Modrinth: a jar kernel has not resolved yet is returned immediately with state: "resolving", and reappears already resolved (or unresolved, with a reason) on a later read once the background resolver has caught up. Read-only -- installing a mod or plugin still goes through the Files capability. 404 when the product does not declare a matching mods/plugins feature, or when the stored loader has no jar-based inventory at all (Vanilla), the same answer a service belonging to somebody else gives.

Get Service Instance Minecraft Inventory › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Minecraft Inventory › Responses

Every jar currently in the folder, with whatever kernel knows about it.

The whole inventory: which kind it is, and every jar currently on disk.
​MinecraftInventoryItemResponse[] · required

Unordered, matching DirectoryListingResponse::entries's own contract: sorting is the panel's.

kind
​MinecraftInventoryKindResponse · enum · required

Which inventory this is. Mirrors [InventoryKind]'s own two values; a third, "none", never reaches the wire because the route answers 404 instead (see the controller).

Enum values:
mods
plugins

Start Service Instance

POST
/api/v2/service-instances/{id}/start

Starts the workloads this service owns. Explicit rather than a /v1-style toggle(active): a boolean states what the caller believes the current state to be, so two concurrent callers who both read "running" are indistinguishable from one stopping and another starting. Refused with 404 when the owning subscription is suspended, cancelled, terminated or failed -- notably a suspended subscription, which only a payment can lift. It is an opaque 404 rather than a business-rule 422 on purpose (R7), and it is decided after authorization so it cannot tell a stranger an instance exists. A subscription that is merely Pending gets the business-rule 422 instead, from the domain. Refused with 409 operation-in-progress while another operation holds this service, or 409 workload-busy when the service's units are being converged by another pass and the wait ran out (retry shortly); the response names which operation and when it started, so a caller can tell "a restore is running" from "something is broken". While a backup is being restored the code is restore-in-progress instead, and the service status reports the restore as its in-flight operation until it finishes.

Start Service Instance › path Parameters

id
​string · uuid · required · style: simple

Start Service Instance › Responses

The verb that ran, and for each workload it acted on the recorded status it left from and the one it is heading to. Intent, not observation: whether the workload got there, and every later transition, arrives on GET /service-instances/{id}/status/stream.

What `POST /service-instances/{id}/start`, `/stop` and `/restart` did. Answers "what happened when I asked" rather than re-describing the service: the verb that ran, and for each workload it acted on, the state it left from and the state it is heading to. It is purpose-shaped rather than a live `status` on [`ServiceInstanceResponse`] because a live status on every instance read would be a cluster fact the kernel publishes in two places, and the two would disagree the moment one was a couple of seconds staler than the other. **This is a statement of intent, not an observation.** Both statuses use the vocabulary `GET /subscriptions/{id}/resources` publishes ([`ResourceStatusResponse`]) and are the recorded status of each resource: `previous_status` as it stood when the verb took its operation lease, and `target_status` as the verb recorded it. Whether the workload actually got there, and every transition after this one (`stopping` settling to `suspended`, a start going `crash_looping`), arrives on `GET /service-instances/{id}/status/stream`, which is the one place live state is published. A client that needs to know when a start finished subscribes there; it does not poll this route. Deliberately absent: - **The instance's name, slug and project.** The caller named the instance by id to get here; `GET /service-instances/{id}` is the read for those, and echoing them made the old answer look informative while saying nothing about the verb. - **A `succeeded` or `completed` flag.** A 2xx already means the cluster accepted the request, and nothing at this moment knows more than that. - **The aggregate run state** (`status/stream`'s `state`). It is folded from live cluster reads this route does not make, and a guess at it here would be exactly the stale second source described above. - **Resources the verb did not touch** (volumes, services, routes, sidecars). They have no power state, so listing them would only invite a client to read their unchanged status as the verb's effect.
action
​string · enum · required

The verb that ran. Echoed so a client holding several in-flight answers, or a log of them, needs no request to interpret one.

Enum values:
start
stop
restart
​ResourceLifecycleTransitionResponse[] · required

Every workload the verb acted on, in the order it acted. Never empty: an instance with nothing to act on is refused with a 422 instead. More than one entry only when a product's blueprint produces several switchable primaries, all of which a lifecycle verb switches together.

service_instance_id
​string · uuid · required

Stop Service Instance

POST
/api/v2/service-instances/{id}/stop

Stops the workloads this service owns. Data volumes are untouched -- this powers the service off, it does not cancel or delete anything. Refused with 404 when the subscription is suspended, cancelled, terminated or failed -- the same opaque answer every other route gives for those, so it cannot tell a stranger an instance exists. Note this route is narrower than the domain rule behind it: a suspended subscription MAY be stopped in principle, and the admin route still does, but a customer suspended for non-payment is refused every lifecycle verb here. Refused with 409 operation-in-progress while another operation holds this service; the response names which operation and when it started, so a caller can tell "a restore is running" from "something is broken". While a backup is being restored the code is restore-in-progress instead, and the service status reports the restore as its in-flight operation until it finishes.

Stop Service Instance › path Parameters

id
​string · uuid · required · style: simple

Stop Service Instance › Responses

The verb that ran, and for each workload it acted on the recorded status it left from and the one it is heading to. Intent, not observation: whether the workload got there, and every later transition, arrives on GET /service-instances/{id}/status/stream.

What `POST /service-instances/{id}/start`, `/stop` and `/restart` did. Answers "what happened when I asked" rather than re-describing the service: the verb that ran, and for each workload it acted on, the state it left from and the state it is heading to. It is purpose-shaped rather than a live `status` on [`ServiceInstanceResponse`] because a live status on every instance read would be a cluster fact the kernel publishes in two places, and the two would disagree the moment one was a couple of seconds staler than the other. **This is a statement of intent, not an observation.** Both statuses use the vocabulary `GET /subscriptions/{id}/resources` publishes ([`ResourceStatusResponse`]) and are the recorded status of each resource: `previous_status` as it stood when the verb took its operation lease, and `target_status` as the verb recorded it. Whether the workload actually got there, and every transition after this one (`stopping` settling to `suspended`, a start going `crash_looping`), arrives on `GET /service-instances/{id}/status/stream`, which is the one place live state is published. A client that needs to know when a start finished subscribes there; it does not poll this route. Deliberately absent: - **The instance's name, slug and project.** The caller named the instance by id to get here; `GET /service-instances/{id}` is the read for those, and echoing them made the old answer look informative while saying nothing about the verb. - **A `succeeded` or `completed` flag.** A 2xx already means the cluster accepted the request, and nothing at this moment knows more than that. - **The aggregate run state** (`status/stream`'s `state`). It is folded from live cluster reads this route does not make, and a guess at it here would be exactly the stale second source described above. - **Resources the verb did not touch** (volumes, services, routes, sidecars). They have no power state, so listing them would only invite a client to read their unchanged status as the verb's effect.
action
​string · enum · required

The verb that ran. Echoed so a client holding several in-flight answers, or a log of them, needs no request to interpret one.

Enum values:
start
stop
restart
​ResourceLifecycleTransitionResponse[] · required

Every workload the verb acted on, in the order it acted. Never empty: an instance with nothing to act on is refused with a 422 instead. More than one entry only when a product's blueprint produces several switchable primaries, all of which a lifecycle verb switches together.

service_instance_id
​string · uuid · required

Restart Service Instance

POST
/api/v2/service-instances/{id}/restart

Restarts the workloads this service owns in place, without passing through a stopped state a client has to observe. /v1 has no restart at all, so a customer wanting one sends stop and then start and races whatever runs in between; this is the whole reason the /v2 surface is three verbs rather than a toggle. Leaves the recorded desired state alone: a restart does not turn a stopped service on. Refused with 409 operation-in-progress while another operation holds this service, or 409 workload-busy when the service's units are being converged by another pass and the wait ran out (retry shortly); the response names which operation and when it started, so a caller can tell "a restore is running" from "something is broken". While a backup is being restored the code is restore-in-progress instead, and the service status reports the restore as its in-flight operation until it finishes.

Restart Service Instance › path Parameters

id
​string · uuid · required · style: simple

Restart Service Instance › Responses

The verb that ran, and for each workload it acted on the recorded status it left from and the one it is heading to. Intent, not observation: whether the workload got there, and every later transition, arrives on GET /service-instances/{id}/status/stream.

What `POST /service-instances/{id}/start`, `/stop` and `/restart` did. Answers "what happened when I asked" rather than re-describing the service: the verb that ran, and for each workload it acted on, the state it left from and the state it is heading to. It is purpose-shaped rather than a live `status` on [`ServiceInstanceResponse`] because a live status on every instance read would be a cluster fact the kernel publishes in two places, and the two would disagree the moment one was a couple of seconds staler than the other. **This is a statement of intent, not an observation.** Both statuses use the vocabulary `GET /subscriptions/{id}/resources` publishes ([`ResourceStatusResponse`]) and are the recorded status of each resource: `previous_status` as it stood when the verb took its operation lease, and `target_status` as the verb recorded it. Whether the workload actually got there, and every transition after this one (`stopping` settling to `suspended`, a start going `crash_looping`), arrives on `GET /service-instances/{id}/status/stream`, which is the one place live state is published. A client that needs to know when a start finished subscribes there; it does not poll this route. Deliberately absent: - **The instance's name, slug and project.** The caller named the instance by id to get here; `GET /service-instances/{id}` is the read for those, and echoing them made the old answer look informative while saying nothing about the verb. - **A `succeeded` or `completed` flag.** A 2xx already means the cluster accepted the request, and nothing at this moment knows more than that. - **The aggregate run state** (`status/stream`'s `state`). It is folded from live cluster reads this route does not make, and a guess at it here would be exactly the stale second source described above. - **Resources the verb did not touch** (volumes, services, routes, sidecars). They have no power state, so listing them would only invite a client to read their unchanged status as the verb's effect.
action
​string · enum · required

The verb that ran. Echoed so a client holding several in-flight answers, or a log of them, needs no request to interpret one.

Enum values:
start
stop
restart
​ResourceLifecycleTransitionResponse[] · required

Every workload the verb acted on, in the order it acted. Never empty: an instance with nothing to act on is refused with a 422 instead. More than one entry only when a product's blueprint produces several switchable primaries, all of which a lifecycle verb switches together.

service_instance_id
​string · uuid · required

Reprovision Service Instance

POST
/api/v2/service-instances/{id}/reprovision

Queues a teardown and re-render of every Kubernetes object this service owns, from the configuration currently saved on it. keep_data: true keeps the data volume(s) and re-attaches them; keep_data: false deletes and re-creates them empty.

keep_data: false destroys this service's data volumes and cannot be undone. Nothing in kernel can bring them back; only an offsite backup taken beforehand can. The field is required rather than defaulted precisely so that a request which lost it in transit is a 400 and not a wipe.

Public endpoints survive the teardown: External Services, their port allocations and their datapath entries are retained, because an ip:port is something the customer has already handed out -- to players, to a DNS record, to a webhook provider.

This is not POST /api/v2/service-instances/{id}/deployments. That route re-renders over what is already running and has no destructive arm; this one deletes first.

Answers 202 with an operation id, not the outcome. The rebuild runs on a durable queue so it survives a kernel restart mid-rebuild; poll GET /api/v2/service-instances/{id}/operations/{operation_id} with the id this response carries until status leaves pending/processing. The underlying rebuild still takes an exclusive lease over the service's power state, data volume and specification, so a queued reprovision waits its turn behind a backup, a restore, a deployment or another rebuild already holding this service rather than being refused outright.

Reprovision Service Instance › path Parameters

id
​string · uuid · required · style: simple

Reprovision Service Instance › Request Body

`POST /api/v2/service-instances/{id}/reprovision` (`ZC-1025`, owner ruling 2026-09-17, part of `ZC-0968`). # `keep_data` has no default, deliberately Every other optional flag in these contracts carries `#[serde(default)]`. This one must not: `bool`'s `Default` is `false`, and `false` here means **delete the customer's volumes**. A client that forgot the field, or a proxy that dropped it, would silently ask for the destructive arm. Required means the worst a malformed request can produce is a 400. Spelled `keep_data` rather than the admin route's `preserve_data` on purpose -- the owner's ruling states the field this way, this is a different route with its own contract, and `#[serde(deny_unknown_fields)]` means a client that sends the wrong name is refused rather than silently defaulted onto the destructive arm.
keep_data
​boolean · required

true keeps the volumes and rebuilds everything around them. false destroys this service's data volumes and cannot be undone -- only an offsite backup taken beforehand can bring them back.

Required. See this type's doc comment for why it has no default.

Reprovision Service Instance › Responses

The queued operation's id, for polling its status

What starting a reprovision did: queued it. `202`, not `200` (`ZC-1025` rework, owner ruling: reprovision must survive a kernel restart mid-rebuild, so it is a durably queued `operations` row now, not a synchronous answer). Deliberately does not say whether the rebuild finished, or whether it wiped anything -- neither is known yet at the moment this response is written. `GET .../operations/{operation_id}` (below) is where a client reads that once the queue has picked the row up. Structurally identical to the admin route's `AdminReprovisionServiceInstanceResponse` (`kernel-http-v2-admin`) for the same reason the two contracts have always been parallel rather than shared: `v2-subscriptions` must not depend on `v2-admin`.
operation_id
​string · uuid · required

The queued row's id. Poll GET .../operations/{operation_id} with it until status leaves pending/processing.

service_instance_id
​string · uuid · required

Get Service Instance Operation

GET
/api/v2/service-instances/{id}/operations/{operation_id}

Reads one queued operation's current status -- today, only ever a reprovision queued by POST .../reprovision. status starts pending, moves to processing while a worker holds it, and settles at completed or dead_letter (the retry budget spent). dead_letter still means the underlying rebuild may have partially applied; last_error carries a fixed step code, never a provider or database error string. A read only -- it never extends or steals a worker's lease, so polling this on an interval is always safe.

Get Service Instance Operation › path Parameters

id
​string · uuid · required · style: simple
operation_id
​string · uuid · required · style: simple

Get Service Instance Operation › Responses

The operation's current status

`GET /api/v2/service-instances/{id}/operations/{operation_id}` (`ZC-1025`). The first, and so far only, reader of `OperationRepository::find_by_id` -- added alongside it because queuing a customer-facing write with no way to learn whether it finished would just move the "did it work" question from the response body to a support ticket. Read-only: unlike `claim`, this never changes what a drainer sees, so refreshing this page cannot itself extend or steal a worker's lease.
attempts
​integer · uint16 · min: 0 · max: 65535 · required
kind
​string · required

The operation's own wire kind, e.g. "service_instance_reprovision". A plain string rather than a closed response enum: this route is generic over every kind the Provisioning queue ever carries, not reprovision-specific, and a new kind must not need a new response type to be readable through it.

max_attempts
​integer · uint16 · min: 0 · max: 65535 · required
operation_id
​string · uuid · required
service_instance_id
​string · uuid · required
status
​OperationStatusResponse · enum · required

Mirrors kernel_domain::domain::operations::OperationStatus 1:1, kept as its own /v2 response type for the reason every other mirrored enum in this file is: a rename on the domain enum must not silently change a wire value.

Enum values:
pending
processing
completed
dead_letter
completed_at
​string | null · date-time

Set once status reaches completed. Still None for dead_letter: exhausting the retry budget is not a completion, and a client polling for a timestamp here would wait forever on a row that has already stopped trying.

last_error
​string | null

A fixed step code ("unroutable:keep_data", "provision", ...), never a provider or database error string -- see ProcessOperationError::step (kernel-tasks), which is what writes this column. None while the operation has never failed an attempt.


List Service Instance Activity

GET
/api/v2/service-instances/{id}/activity

Lists what happened to this service, most recent first. A member of the owning organization reads it; a service-share holder needs the activity permission. Platform-internal events are excluded structurally, and so are the per-open console and file-session tickets: only what the owner did to the service is listed, and the pagination total counts that same set. A Zaroz staff member's action is flagged actor_is_staff and carries no staff identity.

List Service Instance Activity › path Parameters

id
​string · uuid · required · style: simple

List Service Instance Activity › query Parameters

action
​string | null · style: form

Exact match on the action name, e.g. service.restarted.

Exact match on the action name, e.g. service.restarted.

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20

List Service Instance Activity › Responses

Activity entries

​ServiceInstanceActivityEntry[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


List Service Shares

GET
/api/v2/service-instances/{id}/shares

The live shares of this service: who it is shared with (the address the owner typed, plus status pending or accepted, and the member's username and avatar_asset_id once the grantee has accepted) and what each may do. Owner or Admin of the owning organization only; anyone else who can see the service gets 403, anyone who cannot gets 404.

List Service Shares › path Parameters

id
​string · uuid · required · style: simple

List Service Shares › Responses

The live shares, oldest first, and the per-service limit

​ServiceShareResponse[] · required
limit
​integer · uint · min: 0 · required

How many live shares one service may carry.


Share Service

POST
/api/v2/service-instances/{id}/shares

Shares this service with one person, by email, with a list of permissions (console, files, power, settings, databases, database_browse, database_rows, database_schema, external_access, schedules, billing; seeing the service is implied). billing reads this subscription's invoices, resizes it and withdraws a scheduled cancellation, never cancels, renews or pays. An address an account has verified gets access at once, any other waits until an account verifies it; either way the answer is the same pending share with no member details, and it turns accepted in the list once the grantee opens Shared with me. A share never makes anyone a member of the organization, never shows its members or other services, and cannot be re-shared. Owner or Admin of the owning organization only, on a product that offers sharing (shareable in the service's capabilities; otherwise 404). 409 already_shared when the address already has a live share here, 409 share_limit_reached past the per-service limit, 429 past 20 new shares an hour per service or 60 per user, 422 cannot_share_with_self for the caller's own address.

Share Service › path Parameters

id
​string · uuid · required · style: simple

Share Service › Request Body

Share this service with one person.
email
​string · required

The person's email address. An address an account has verified is shared with that account at once, any other waits until an account verifies it and opens "Shared with me".

​ServiceSharePermission[] · required

What the person may do, besides seeing the service: any of console, files, power, settings. Empty is allowed and means "may see it is up".

Share Service › Responses

The new share

One share, as the owner sees it. Carries the address the owner typed and, only once the grantee has accepted the share by opening it, that member's username and avatar for the Access tab. Create and update never carry them, and a new share is always `pending`, so typing an address reveals nothing about whether an account exists. This surface is the share owner's alone (`ManageServiceSharesPolicy`).
created_at
​string · date-time · required
email
​string · required
id
​string · uuid · required
​ServiceSharePermission[] · required
service_instance_id
​string · uuid · required
status
​string · enum · required

pending until the grantee opens the share in their own list, then accepted.

Enum values:
pending
accepted
updated_at
​string · date-time · required
avatar_asset_id
​string | null · uuid

The member's avatar asset (/assets/{id}); null until accepted or when they have none.

username
​string | null

The member's username; null until the share is accepted.


Revoke Service Share

DELETE
/api/v2/service-instances/{id}/shares/{share_id}

Revokes a share. The grantee's very next request is refused, including any new console, terminal, logs, framebuffer or files ticket; a ticket already minted lives out its own short validity (seconds to a few minutes), and a terminal already attached stays open until it disconnects. Owner or Admin of the owning organization only.

Revoke Service Share › path Parameters

id
​string · uuid · required · style: simple
share_id
​string · uuid · required · style: simple

Revoke Service Share › Responses

Revoked

No data returned

Update Service Share

PATCH
/api/v2/service-instances/{id}/shares/{share_id}

Replaces what a share permits. Takes effect on the grantee's next request. Owner or Admin of the owning organization only; 404 for a share that is not a live share of this service.

Update Service Share › path Parameters

id
​string · uuid · required · style: simple
share_id
​string · uuid · required · style: simple

Update Service Share › Request Body

Replace what a share permits.
​ServiceSharePermission[] · required

The complete new set, replacing the old one.

Update Service Share › Responses

The share

One share, as the owner sees it. Carries the address the owner typed and, only once the grantee has accepted the share by opening it, that member's username and avatar for the Access tab. Create and update never carry them, and a new share is always `pending`, so typing an address reveals nothing about whether an account exists. This surface is the share owner's alone (`ManageServiceSharesPolicy`).
created_at
​string · date-time · required
email
​string · required
id
​string · uuid · required
​ServiceSharePermission[] · required
service_instance_id
​string · uuid · required
status
​string · enum · required

pending until the grantee opens the share in their own list, then accepted.

Enum values:
pending
accepted
updated_at
​string · date-time · required
avatar_asset_id
​string | null · uuid

The member's avatar asset (/assets/{id}); null until accepted or when they have none.

username
​string | null

The member's username; null until the share is accepted.


List Services Shared With Me

GET
/api/v2/me/shared-service-instances

The services shared with the caller, each with its granted permissions and the owner's display name, and nothing else about the owner. Also claims any share addressed to the caller's verified email that was waiting for an account, so a person who signs up after being shared with finds it here. A service that has ended is not listed. Each row carries the service's live resources (id, kind, role, status) that the console, files, logs and metrics routes are addressed by. Open one through the ordinary /service-instances/{id}/... routes, within its permissions.

List Services Shared With Me › Responses

The caller's live shares


Leave Shared Service

DELETE
/api/v2/me/shared-service-instances/{share_id}

Gives up a share the caller holds. 404 for any share that is not a live share held by the caller.

Leave Shared Service › path Parameters

share_id
​string · uuid · required · style: simple

Leave Shared Service › Responses

Left

No data returned

Get Service Instance Setup

GET
/api/v2/service-instances/{id}/setup

Whether provisioning is still waiting for this service to be configured. awaiting_configuration is true while the service has been paid for and provisioning is still waiting for its ServiceConfiguration to be written -- through PUT .../source for a product that needs one. Retired: this route used to also return the product's authored setup schema and the customer's redacted answers, and accepted a PUT submitting them; both are gone.

Get Service Instance Setup › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Setup › Responses

Whether this service is still awaiting configuration

Whether provisioning is still waiting for this service to be configured. This used to also carry the product's authored question tree and the customer's redacted answers (`setup_schema`, `answers`), and a `PUT` on this same route submitted them. Both are gone (`docs/V2Plan/99-setup-schema-removal.md`): the data-driven setup form they served is retired, and `PUT /api/v2/console/service-instances/{id}/source` is what bootstraps a service's `ServiceConfiguration` now (`docs/V2Plan/00-program.md` §59). What is left of this route is a read console's product-listing pages still depend on (`console/lib/api/services.ts`, `service-shape.ts`): the one boolean that distinguishes "paid for and idle, waiting on configuration" from "running", which lives nowhere else a listing can read it per instance.
awaiting_configuration
​boolean · required

True while provisioning is still waiting for a configuration. A service in this state has been paid for and has no workload running.

service_instance_id
​string · uuid · required

Get Service Instance Setup Steps

GET
/api/v2/service-instances/{id}/setup/steps

Lists this service's declared post-checkout setup steps, in the product's own declaration order, each with its own TYPED params (tagged by step, {"step": "none"} for a step that reads none) and its stored answers (null when unanswered). A product declaring no steps answers with an empty steps list and awaiting_configuration: false, not a 404. awaiting_configuration is true while at least one declared step has never been answered -- the same gate provisioning itself checks before it will render this service's workload for the first time. 404 for a service belonging to somebody else, the same answer an invented id gives.

Get Service Instance Setup Steps › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Setup Steps › Responses

Every declared step, its params, and its answers if any

`GET .../setup/steps`: every step this product declares, in declaration order.
awaiting_configuration
​boolean · required

Whether provisioning is still waiting on at least one of these steps. A product declaring no steps is never awaiting one.

​ServiceInstanceSetupStepResponse[] · required

Answer Service Instance Setup Step

PUT
/api/v2/service-instances/{id}/setup/steps/{step}

Answers one setup step this service's product declares. The body's own step tag must match the path segment, or the request is refused with a 400. Refused with 404 for a step the product does not declare, the same answer a service belonging to somebody else gives. apply is kernel's verdict about what landing this answer costs the running service: live when the submitted answers are unchanged from what is already stored, in which case nothing is saved and nothing rolls out; otherwise restart. Answering the LAST unanswered step a product declares is what starts this service's first provisioning -- until then, saving is all a write can do, because a product still missing an answer has nothing coherent to render. Once every declared step is answered, this same route re-renders an already-running service exactly the way a capability configuration write does elsewhere, and takes the same configuration-change lease so the two cannot collide; it is refused with 409 while another such operation holds this service. pending is true when the answer was saved but the (re)provisioning attempt itself failed -- the value is still stored, and a later read or another answer can still succeed. The audit entry names the step and the verdict, never the answers. For server_passwords, answers.passwords maps each password's id to either {"generate": true} (kernel mints it; refused for a password the product marked non-generatable) or {"value": "..."} (a supplied value, checked and encrypted before it is stored); the id may be a subset of the product's declared passwords, leaving the rest untouched. The response never contains a value or a ciphertext for this step, only which ids are now set -- reveal it with its own request. For minecraft_software, every network-checkable value (the Minecraft version and, for Forge/NeoForge, the loader version; the Modrinth project id; the FTB pack id) is checked against the same providers GET .../setup/steps/{step}/options/{field} renders, and refused with a 400 naming the value when it is confirmed not to exist. An upstream outage never refuses the save -- it is accepted and logged instead, the same way a value this route cannot check (a custom loader jar/URL) always was. For app_runtime, the request names only runtime, variant, and start_command; the product's own runtime table resolves and freezes image and install_command at this moment, and neither is ever settable by the caller -- a later edit to the table changes nothing for this answer until it is saved again. For vm_access, the response never contains the password, only password_set: true; ssh_authorized_keys is raw multi-line OpenSSH text on the way in and a normalised list on the way out; Windows always overwrites username to Administrator and ignores every SSH/timezone field regardless of what is submitted, and a Linux answer that would leave the guest with no way in (ssh_password_auth: false with no keys) is refused with a 400. Refused with 409 for a step whose params lock after the first answer once one is already stored (app_admin's install_only; no step in this slice locks yet).

Answer Service Instance Setup Step › path Parameters

id
​string · uuid · required · style: simple
step
​string · required · style: simple

Answer Service Instance Setup Step › Request Body

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
type = object · requires: step, answers
Properties for Variant 1:
​MinecraftSoftwareDto · required
step
​string · const · required
Const value: minecraft_software

Answer Service Instance Setup Step › Responses

The stored answers for this step, and what landing them cost

`PUT .../setup/steps/{step}`.
​SetupStepAnswersResponseDto · required

One step's answers as a RESPONSE (GET .../setup/steps, PUT .../setup/steps/{step}'s own echo). Same tag shape as [SetupStepAnswersDto] for the three Minecraft steps, whose answers hold nothing secret and so render identically either direction; NEVER the same shape for [Self::ServerPasswords], which reports only which ids are set, never a value or a ciphertext -- see [ServerPasswordsResponseDto].

​required

What landing this answer cost the running service. live when the submitted answers matched what was already stored.

awaiting_configuration
​boolean · required

Whether provisioning is still waiting on at least one declared step after this write.

pending
​boolean · required

True when this answer completed the product's declared steps and (re)provisioning was started, but the attempt itself failed. The answer is still saved; a later read or retry can still succeed.

​SetupStepId · required

The closed vocabulary of setup steps kernel knows how to ask, validate and render. Adding a member is a reviewed code change here, in the dashboard's step registry, and in whichever product template starts reading its variables -- the same "closed set, matched code change" discipline [crate::domain::product_definitions::capabilities::CapabilityId] uses, for the same reason: a step nothing implements is a claim the platform cannot back.

Ord is derived so this can key a BTreeMap ([StepAnswersDocument::steps]): declaration order becomes iteration order there, which is incidental (the document is always looked up by key, never walked in order) but harmless.


List Service Instance Setup Step Options

GET
/api/v2/service-instances/{id}/setup/steps/{step}/options/{field}

Renders one setup-step field's OPTIONS -- what a customer may pick from -- separate from what they submit (PUT .../setup/steps/{step}; see that route's own admission behaviour). Only minecraft_software's version, loader_version, modrinth_project and ftb_pack_id fields, and fivem_server's artifact_channel (the fivem_builds provider: recommended, optional and latest, each with its current build as detail; custom is a typed build id, not an option), have a provider today; every other field (and any step or field this product does not declare) is refused with 404, the same answer a service belonging to somebody else gives. loader_version's options require loader (forge or neoforge) and version (the Minecraft version already chosen) as query parameters; missing or unsupported values are a 400. q searches Modrinth/FTB modpacks (trimmed, at most 64 characters) and is rate-limited per caller, since every request fans out to a third party we do not control; the other two fields read an already-cached manifest and are not. degraded is true when the upstream this field depends on (Mojang, Forge, NeoForge, Modrinth, FTB, or Cfx.re) could not be reached and options is stale or empty -- informational only, and never a reason by itself to refuse a later save.

List Service Instance Setup Step Options › path Parameters

field
​string · required · style: simple
id
​string · uuid · required · style: simple
step
​string · required · style: simple

List Service Instance Setup Step Options › query Parameters

loader
​string | null · style: form

forge or neoforge. Required for loader_version's options; ignored by every other field.

forge or neoforge. Required for loader_version's options; ignored by every other field.

Default: null
q
​string | null · style: form

Search text for the two search-shaped providers (Modrinth, FTB). Trimmed and capped at 64 characters; ignored by every other provider.

Search text for the two search-shaped providers (Modrinth, FTB). Trimmed and capped at 64 characters; ignored by every other provider.

Default: null
snapshots
​boolean | null · style: form

Whether version's options include snapshot builds. Defaults to false (stable releases only); ignored by every other field.

Whether version's options include snapshot builds. Defaults to false (stable releases only); ignored by every other field.

Default: null
version
​string | null · style: form

The Minecraft version already chosen. Required for loader_version's options; ignored by every other field.

The Minecraft version already chosen. Required for loader_version's options; ignored by every other field.

Default: null

List Service Instance Setup Step Options › Responses

The field's renderable options

`GET .../setup/steps/{step}/options/{field}`.
degraded
​boolean · required

True when the upstream this field's provider depends on (Mojang, Forge, NeoForge, Modrinth, or FTB) could not be reached and options is either stale or empty. Never a reason by itself to refuse a save -- see [AnswerSetupStepResponse]'s own admission behaviour.

​SetupOptionDto[] · required
cached_at
​string | null · date-time

When the underlying data was last fetched from the upstream, or null when nothing could be served at all (a fully degraded, empty result).


Reveal Service Instance Setup Password

POST
/api/v2/service-instances/{id}/setup/steps/server_passwords/{password_id}/reveal

Returns one server_passwords password's plaintext, once, and records that it was read. Its own request rather than folded into the setup-steps read, so the ordinary read stays structurally incapable of leaking a value and so the audit entry covers exactly the act that needs it. Gated on the same access as answering a setup step, not on the access to read one: an ordinary member who may see this service's setup answers may not read a password back. 404 for a service belonging to somebody else, a product that does not declare server_passwords, or a password id with no stored answer -- the same answer an invented id gives. 409 for a password the product marked "revealable": false (its own internal credential, never meant to be read back).

Reveal Service Instance Setup Password › path Parameters

id
​string · uuid · required · style: simple
password_id
​string · required · style: simple

Reveal Service Instance Setup Password › Responses

The password's plaintext

`POST .../setup/steps/server_passwords/{id}/reveal`: the plaintext, once.
id
​string · required
value
​string · required

Rotate Service Instance Setup Password

POST
/api/v2/service-instances/{id}/setup/steps/server_passwords/{password_id}/rotate

Rotates one server_passwords password to a freshly kernel-generated value and (re)renders the running service under it, the same configuration-change lease every other setup-step write takes; refused with 409 while another such operation holds this service, and also with 409 for a password the product marked "rotatable": false (kernel-generated but owned by the application afterwards -- rotating it here would desynchronize kernel's stored value from what the application actually has). Refused with 400 for a password the product marked non-generatable -- rotating a customer-supplied value would silently replace what they typed. Never returns the new value; read it back with the reveal request. 404 for a service belonging to somebody else, a product that does not declare server_passwords, or a password id with no stored answer.

Rotate Service Instance Setup Password › path Parameters

id
​string · uuid · required · style: simple
password_id
​string · required · style: simple

Rotate Service Instance Setup Password › Responses

What landing the rotation cost the running service

`POST .../setup/steps/server_passwords/{id}/rotate`: never the value (that is what reveal is for) -- only what landing the new value cost.
​required

What landing the rotation cost the running service. Always restart in slice 1c (see [kernel_core::domain::setup_steps::apply_mode]'s own doc comment: any change to any setup step's answers is Restart), carried as the same typed value the other setup-step responses use rather than hardcoding the string here.

id
​string · required
pending
​boolean · required

True when the rotation was saved but the reprovision attempt itself failed. The same pending vocabulary every other setup-step and capability write already uses.


Get Subscription Bucket

GET
/api/v2/subscriptions/{id}/bucket

Gets the subscription's bucket: its S3 endpoint and bucket name, cap, public-read setting, the access the data plane allows right now (open, read_only over the cap, suspended, deleted) and the latest measured size. 404 when the subscription has no bucket.

Get Subscription Bucket › path Parameters

id
​string · uuid · required · style: simple

Get Subscription Bucket › Responses

The bucket

​BucketView · required

Create Subscription Bucket

POST
/api/v2/subscriptions/{id}/bucket

Creates the subscription's bucket in a region with a storage cap (default 100 GiB, 1 GiB to 100 TiB). One bucket per subscription: a second is refused with 409. Only an active subscription may create (422). region_id defaults to the region of the subscription's price. Requires the same authority as buying a subscription for this organization.

Create Subscription Bucket › path Parameters

id
​string · uuid · required · style: simple

Create Subscription Bucket › Request Body

cap_bytes
​integer | null · uint64 · min: 0

Storage cap in bytes, 1 GiB to 100 TiB. Absent takes 100 GiB. Above the cap the keys turn read-only until usage falls or the cap is raised.

label
​string | null

Display name. Blank or absent becomes "Bucket".

region_id
​integer | null · int32

Where the bucket lives. Defaults to the region of the subscription's price; when the price names a region this must match it.

Create Subscription Bucket › Responses

The created bucket

​BucketView · required

Delete Subscription Bucket

DELETE
/api/v2/subscriptions/{id}/bucket

Deletes the subscription's bucket. Its keys stop working at once and the slot frees immediately, but the data stays for a recovery window (seven days) before a background task purges it for good. Requires the same authority as buying a subscription for this organization.

Delete Subscription Bucket › path Parameters

id
​string · uuid · required · style: simple

Delete Subscription Bucket › Responses

Scheduled for deletion

No data returned

Update Subscription Bucket

PATCH
/api/v2/subscriptions/{id}/bucket

Updates the bucket's label, storage cap or public-read setting; absent fields are left alone. A cap change and a public-read change reach the data plane at once. Not allowed once the bucket is scheduled for deletion (422). Requires the same authority as buying a subscription for this organization.

Update Subscription Bucket › path Parameters

id
​string · uuid · required · style: simple

Update Subscription Bucket › Request Body

Partial update: absent fields are left alone.
cap_bytes
​integer | null · uint64 · min: 0
label
​string | null
public_read
​boolean | null

Serve the bucket's objects to anonymous readers.

Update Subscription Bucket › Responses

The updated bucket

​BucketView · required

List Subscription Bucket Keys

GET
/api/v2/subscriptions/{id}/bucket/keys

Lists the bucket's active access keys, oldest first. Secrets are never returned after creation.

List Subscription Bucket Keys › path Parameters

id
​string · uuid · required · style: simple

List Subscription Bucket Keys › Responses

The active keys

​BucketKeyView[] · required

Create Subscription Bucket Key

POST
/api/v2/subscriptions/{id}/bucket/keys

Creates a named access key, read_only or read_write. The secret is in this response only: it is never stored in readable form and cannot be shown again. At most twenty active keys per bucket (429). A key created while the bucket is suspended is inert until service resumes. Requires the same authority as buying a subscription for this organization.

Create Subscription Bucket Key › path Parameters

id
​string · uuid · required · style: simple

Create Subscription Bucket Key › Request Body

scope
​string · required

read_only or read_write.

name
​string | null

Display name. Blank or absent becomes "Key".

Create Subscription Bucket Key › Responses

The key and its one-time secret

A new key. The secret is in this response and nowhere else, ever.
​BucketKeyView · required
secret_access_key
​string · required

Revoke Subscription Bucket Key

DELETE
/api/v2/subscriptions/{id}/bucket/keys/{key_id}

Revokes one access key: it stops working at once, along with every presigned URL signed with it. Another bucket's key, an unknown id and an already revoked key all answer 404. Requires the same authority as buying a subscription for this organization.

Revoke Subscription Bucket Key › path Parameters

key_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Revoke Subscription Bucket Key › Responses

Revoked

No data returned

Get Subscription Bucket Usage

GET
/api/v2/subscriptions/{id}/bucket/usage

The bucket's measured usage over a window (default: this month so far, at most 93 days): hourly stored-byte and traffic samples, the window's average stored size in GiB-months and the bytes sent out, which are the two billed meters.

Get Subscription Bucket Usage › path Parameters

id
​string · uuid · required · style: simple

Get Subscription Bucket Usage › query Parameters

from
​string | null · date-time · style: form

Start of the window, inclusive. Defaults to the start of this month.

Start of the window, inclusive. Defaults to the start of this month.

to
​string | null · date-time · style: form

End of the window, exclusive. Defaults to now.

End of the window, exclusive. Defaults to now.

Get Subscription Bucket Usage › Responses

The usage

​BucketUsageView · required

Get Subscription Registry

GET
/api/v2/subscriptions/{id}/registry

Gets the subscription's container registry: its login host, namespace and image prefix, cap, the access it allows right now (open, read_only over the cap, suspended, deleted) and the latest measured size. 404 when the subscription has no registry.

Get Subscription Registry › path Parameters

id
​string · uuid · required · style: simple

Get Subscription Registry › Responses

The registry

​RegistryView · required

Create Subscription Registry

POST
/api/v2/subscriptions/{id}/registry

Creates the subscription's container registry in a region with a spend cap (default 100 GiB, 1 GiB to 100 TiB). One registry per subscription: a second is refused with 409. Only an active subscription may create (422). region_id defaults to the region of the subscription's price. Requires the same authority as buying a subscription for this organization.

Create Subscription Registry › path Parameters

id
​string · uuid · required · style: simple

Create Subscription Registry › Request Body

cap_bytes
​integer | null · uint64 · min: 0

Spend cap in bytes of stored images, 1 GiB to 100 TiB. Absent takes 100 GiB. Above it pushes are refused until usage falls or the cap is raised; pulls and deletes keep working.

label
​string | null

Display name. Blank or absent becomes "Registry".

region_id
​integer | null · int32

Where the registry lives. Defaults to the region of the subscription's price; when the price names a region this must match it.

Create Subscription Registry › Responses

The created registry

​RegistryView · required

Delete Subscription Registry

DELETE
/api/v2/subscriptions/{id}/registry

Deletes the subscription's registry. Its credentials stop minting tokens at once and the slot frees immediately, but the images stay for a recovery window (seven days) before a background task purges them for good. Requires the same authority as buying a subscription for this organization.

Delete Subscription Registry › path Parameters

id
​string · uuid · required · style: simple

Delete Subscription Registry › Responses

Scheduled for deletion

No data returned

Update Subscription Registry

PATCH
/api/v2/subscriptions/{id}/registry

Updates the registry's label or spend cap; absent fields are left alone. A cap change takes effect at the next token request. Not allowed once the registry is scheduled for deletion (422). Requires the same authority as buying a subscription for this organization.

Update Subscription Registry › path Parameters

id
​string · uuid · required · style: simple

Update Subscription Registry › Request Body

Partial update: absent fields are left alone.
cap_bytes
​integer | null · uint64 · min: 0
label
​string | null

Update Subscription Registry › Responses

The updated registry

​RegistryView · required

List Subscription Registry Credentials

GET
/api/v2/subscriptions/{id}/registry/credentials

Lists the registry's active credentials, oldest first. Secrets are never returned after creation.

List Subscription Registry Credentials › path Parameters

id
​string · uuid · required · style: simple

List Subscription Registry Credentials › Responses

The active credentials


Create Subscription Registry Credential

POST
/api/v2/subscriptions/{id}/registry/credentials

Creates a named robot credential: pull, push (pull and push) or admin (pull, push and delete). The secret is in this response only: it is never stored in readable form and cannot be shown again. At most twenty active credentials per registry (429). Requires the same authority as buying a subscription for this organization.

Create Subscription Registry Credential › path Parameters

id
​string · uuid · required · style: simple

Create Subscription Registry Credential › Request Body

scope
​string · required

pull, push (pull and push) or admin (pull, push and delete).

name
​string | null

Display name. Blank or absent becomes "Credential".

Create Subscription Registry Credential › Responses

The credential and its one-time secret

A new credential. The secret is in this response and nowhere else, ever.
​RegistryCredentialView · required
secret
​string · required

The password to log in with.


Revoke Subscription Registry Credential

DELETE
/api/v2/subscriptions/{id}/registry/credentials/{credential_id}

Revokes one credential: it mints no further tokens, and a token already issued lapses within five minutes. Another registry's credential, an unknown id and an already revoked credential all answer 404. Requires the same authority as buying a subscription for this organization.

Revoke Subscription Registry Credential › path Parameters

credential_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Revoke Subscription Registry Credential › Responses

Revoked

No data returned

List Subscription Registry Repositories

GET
/api/v2/subscriptions/{id}/registry/repositories

Lists the repositories under the registry's namespace, as the data plane reports them.

List Subscription Registry Repositories › path Parameters

id
​string · uuid · required · style: simple

List Subscription Registry Repositories › Responses

The repositories


Delete Subscription Registry Repository

DELETE
/api/v2/subscriptions/{id}/registry/repositories

Deletes one repository (every tag) by its path below the registry's namespace; the storage is reclaimed by the data plane's garbage collection. A path that is not a valid repository path is refused with 422. Requires the same authority as buying a subscription for this organization.

Delete Subscription Registry Repository › path Parameters

id
​string · uuid · required · style: simple

Delete Subscription Registry Repository › query Parameters

path
​string · required · style: form

The repository path below the registry's namespace, e.g. app/web.

The repository path below the registry's namespace, e.g. app/web.

Delete Subscription Registry Repository › Responses

Deleted

No data returned

Get Subscription Registry Usage

GET
/api/v2/subscriptions/{id}/registry/usage

The registry's measured usage over a window (default: this month so far, at most 93 days): hourly stored-byte samples (logical bytes), the window's average stored size in GiB-months and the bytes served out, which are the billed meters.

Get Subscription Registry Usage › path Parameters

id
​string · uuid · required · style: simple

Get Subscription Registry Usage › query Parameters

from
​string | null · date-time · style: form

Start of the window, inclusive. Defaults to the start of this month.

Start of the window, inclusive. Defaults to the start of this month.

to
​string | null · date-time · style: form

End of the window, exclusive. Defaults to now.

End of the window, exclusive. Defaults to now.

Get Subscription Registry Usage › Responses

The usage

​RegistryUsageView · required

List Subscription Shared Databases

GET
/api/v2/subscriptions/{id}/shared-databases

Lists this subscription's active shared databases, oldest first. Each entry carries its own connection details and credentials -- this is the screen a customer copies them from. Ten free 64 MiB databases per subscription (owner ruling ZC-0006); an empty list is normal. Dashboard only.

List Subscription Shared Databases › path Parameters

id
​string · uuid · required · style: simple

List Subscription Shared Databases › Responses

The subscription's active shared databases

A subscription's active shared databases, oldest first. Each carries its credentials: this is the screen a customer copies them from.
​SharedDatabaseView[] · required

Create Subscription Shared Database

POST
/api/v2/subscriptions/{id}/shared-databases

Creates one shared database on the subscription's entitlement, with a 64 MiB quota. Refused with 429 (quota-exceeded) once the subscription already holds ten -- see GET .../entitlement to check first. region_id is strict when given: a region with no enabled host for the chosen engine is refused (503) rather than placed elsewhere; omit it to place on the least-loaded host for that engine anywhere. Only an active subscription may create (422 otherwise). Requires the same authority as buying a subscription for this organization.

Create Subscription Shared Database › path Parameters

id
​string · uuid · required · style: simple

Create Subscription Shared Database › Request Body

engine
​string · required

mariadb or postgres; see GET .../shared-databases/engines for which are available right now.

display_name
​string | null

The customer's own label. Blank or absent becomes "Database N".

region_id
​integer | null · int32

Place it in this region. Strict when given: a region with no enabled host for engine is refused rather than silently placed elsewhere. Absent places it on the least-loaded host for engine.

Create Subscription Shared Database › Responses

The created shared database, with its credentials

​SharedDatabaseView · required

Get Subscription Shared Database Entitlement

GET
/api/v2/subscriptions/{id}/shared-databases/entitlement

Reports where this subscription stands against its shared-database entitlement: how many of its ten free 64 MiB databases it holds now, and how many remain. held is a real count, never assumed from the limit.

Get Subscription Shared Database Entitlement › path Parameters

id
​string · uuid · required · style: simple

Get Subscription Shared Database Entitlement › Responses

Usage against the entitlement

Where the subscription stands against its entitlement.
held
​integer · uint64 · min: 0 · required

Active databases the subscription holds now. A real count.

limit
​integer · uint64 · min: 0 · required

The entitlement: 10.

quota_bytes_each
​integer · uint64 · min: 0 · required

Each database's quota in bytes: 67108864 (64 MiB).

remaining
​integer · uint64 · min: 0 · required

limit - held, never below zero.


List Subscription Shared Database Engines

GET
/api/v2/subscriptions/{id}/shared-databases/engines

Lists the shared database engines with at least one enabled host right now -- what POST .../shared-databases will accept as engine. Gated on being able to read this subscription even though the answer is platform-wide, so a client can fetch it alongside the create form without a second authorization surface.

List Subscription Shared Database Engines › path Parameters

id
​string · uuid · required · style: simple

List Subscription Shared Database Engines › Responses

Available engines

Engines with at least one enabled host.
engines
​string[] · required

List Subscription Shared Database Regions

GET
/api/v2/subscriptions/{id}/shared-databases/regions

Lists the enabled region and engine pairs with at least one enabled host right now -- what POST .../shared-databases will accept as region_id for a given engine. Gated on being able to read this subscription even though the answer is platform-wide, for the same reason GET .../engines is.

List Subscription Shared Database Regions › path Parameters

id
​string · uuid · required · style: simple

List Subscription Shared Database Regions › Responses

Available region and engine pairs

Enabled region and engine pairs with at least one enabled host.

Delete Subscription Shared Database

DELETE
/api/v2/subscriptions/{id}/shared-databases/{database_id}

Deletes one of the subscription's shared databases. Its entitlement slot frees at once; the database itself lingers on its host for a short recovery window before a background task drops it. Another subscription's database, or an unknown id, both answer 404 -- the two are deliberately indistinguishable. Requires the same authority as buying a subscription for this organization.

Delete Subscription Shared Database › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Delete Subscription Shared Database › Responses

Deleted

No data returned

Rotate Subscription Shared Database Credentials

POST
/api/v2/subscriptions/{id}/shared-databases/{database_id}/rotate-credentials

Replaces one shared database's password on its host, then in its stored credentials, and returns it with the new password. The username and connection addresses do not change. Another subscription's database, or an unknown id, both answer 404. Requires the same authority as buying a subscription for this organization.

Rotate Subscription Shared Database Credentials › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Rotate Subscription Shared Database Credentials › Responses

The database with its new credentials

​SharedDatabaseView · required

Start Shared Database Export

POST
/api/v2/subscriptions/{id}/shared-databases/{database_id}/transfers/export

Queues an export of this shared database's data to a JSON dump. Postgres only today (422 for mariadb) -- see crates/domain/operations/src/domain/shared_databases/transfer.rs for why. Answers 202 with an operation id, not the outcome. Poll GET .../transfers/{operation_id} with it until status leaves pending/processing, then GET .../transfers/{operation_id}/download for the bytes. The dump carries row data only, never CREATE TABLE DDL -- restoring it with POST .../transfers/import requires the destination's tables to already exist. Requires the same authority as buying a subscription for this organization.

Start Shared Database Export › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Start Shared Database Export › Responses

The queued export's operation id, for polling its status

What starting an export or an import answers with: `202` plus the queued operation's id, never the outcome. Poll `GET .../transfers/{operation_id}` with it.
allocation_id
​string · uuid · required
operation_id
​string · uuid · required

Start Shared Database Import

POST
/api/v2/subscriptions/{id}/shared-databases/{database_id}/transfers/import

Queues an import of a previously completed export's dump into this shared database. source_operation_id must name a completed export queued for a database on THIS SAME subscription (404 otherwise, indistinguishable from an invented id) -- possibly a different database than this one, which is how this route implements "transfer": export one shared database, then import into another. The destination's tables must already exist with the same names and columns the export recorded; a row for a table that does not exist fails the whole operation. Answers 202 with an operation id, not the outcome. Poll GET .../transfers/{operation_id} with it. Requires the same authority as buying a subscription for this organization.

Start Shared Database Import › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Start Shared Database Import › Request Body

`POST .../shared-databases/{database_id}/transfers/import`. Names a PREVIOUSLY COMPLETED export (by its operation id) to restore from -- never a raw upload. See `GET .../transfers/{operation_id}` to find a `completed` export's id, and its own docs for why this route trusts no bytes the caller did not already get from a `completed` export of a database on this same subscription.
source_operation_id
​string · uuid · required

Start Shared Database Import › Responses

The queued import's operation id, for polling its status

What starting an export or an import answers with: `202` plus the queued operation's id, never the outcome. Poll `GET .../transfers/{operation_id}` with it.
allocation_id
​string · uuid · required
operation_id
​string · uuid · required

Upload Shared Database Dump

POST
/api/v2/subscriptions/{id}/shared-databases/{database_id}/transfers/upload

Queues an import of an uploaded SQL dump into this shared database, as /v1 did. The request body is the raw file (plain SQL, or gzipped SQL as .sql.gz; PostgreSQL pg_dump --format=plain including COPY .. FROM stdin blocks, or mysqldump). It is streamed to storage, never held in memory, and capped at 256 MiB (422 over it). The format is checked from the file's first bytes: an empty file, a binary file, a pg_dump -Fc archive or a Zaroz JSON export (import those by operation id instead) is a 422. The database must have storage left (507 when it is locked over quota; the import also stops, failed, if it fills the quota while restoring). Statements run as the database's own user, in order, stopping at the first error, so a failed import can leave the statements before it applied. USE, CREATE|DROP|ALTER DATABASE and PostgreSQL ALTER .. OWNER TO statements are skipped; DELIMITER lines (stored routines and triggers) are honoured. Answers 202 with an operation id, not the outcome. Poll GET .../transfers/{operation_id}. Requires the same authority as buying a subscription for this organization.

Upload Shared Database Dump › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Upload Shared Database Dump › Responses

The queued import's operation id, for polling its status

What starting an export or an import answers with: `202` plus the queued operation's id, never the outcome. Poll `GET .../transfers/{operation_id}` with it.
allocation_id
​string · uuid · required
operation_id
​string · uuid · required

Get Shared Database Transfer Operation

GET
/api/v2/subscriptions/{id}/shared-databases/{database_id}/transfers/{operation_id}

Reads one queued export or import's current status. status starts pending, moves to processing while a worker holds it, and settles at completed or dead_letter (the retry budget spent). A read only -- it never extends or steals a worker's lease, so polling this on an interval is always safe.

Get Shared Database Transfer Operation › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple
operation_id
​string · uuid · required · style: simple

Get Shared Database Transfer Operation › Responses

The operation's current status

`GET .../shared-databases/{database_id}/transfers/{operation_id}`: one queued transfer's current status. Mirrors `ServiceInstanceOperationResponse` (`contracts::service_instances`) in shape and in the same generic-`kind`-as-a-string reasoning: this route is generic over both kinds the `DatabaseTransfer` queue carries, not export-or-import-specific.
allocation_id
​string · uuid · required
attempts
​integer · uint16 · min: 0 · max: 65535 · required
kind
​string · required

"shared_database_export" or "shared_database_import".

max_attempts
​integer · uint16 · min: 0 · max: 65535 · required
operation_id
​string · uuid · required
status
​OperationStatusResponse · enum · required

Mirrors kernel_domain::domain::operations::OperationStatus 1:1, kept as its own /v2 response type for the reason every other mirrored enum in this file is: a rename on the domain enum must not silently change a wire value.

Enum values:
pending
processing
completed
dead_letter
completed_at
​string | null · date-time
last_error
​string | null

Download Shared Database Transfer

GET
/api/v2/subscriptions/{id}/shared-databases/{database_id}/transfers/{operation_id}/download

Streams a completed export's JSON dump. Refused with 422 (business-rule-violation) while the export has not finished yet -- poll GET .../transfers/{operation_id} first. An import's operation id, another subscription's operation, or an invented id all answer the same 404.

Download Shared Database Transfer › path Parameters

database_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple
operation_id
​string · uuid · required · style: simple

Download Shared Database Transfer › Responses

no content

No data returned

Stream Service Instance Status

GET
/api/v2/service-instances/{id}/status/stream

Holds a text/event-stream open for one service instance the caller may see, so a workspace shows its live state without polling. Replaces /v1's GET /orders/{id}/status/stream. Answers 404 for an instance the caller does not reach, and equally for one whose subscription is suspended, terminated, cancelled or failed: this route reads the cluster, and a service that is no longer being served has no live state to report. Four frames, addressed by the SSE event: name. status carries the whole picture -- the aggregate run state, which of the product's declared lifecycle verbs make sense to click right now, the operation kernel is currently carrying out against the service (a deploy, a restart, a reprovision, ...) when one is in flight, every visible resource with its live usage, container statuses and memory pressure, the in-guest readiness gate where the product has one, and crash or startup-failure detail while the service is in those states. It is sent on connect, whenever a resource's status changes, and on a five-second tick, because usage and container statuses are live cluster reads that no event announces. network_metrics and resource_metrics carry one sample each, pushed as the collector records it; they are a live tail, not a history, and the resource metrics read is what a chart should backfill from. vm_progress reports a virtual machine's bring-up phase while one is being built. The server closes the connection after fifteen minutes and the first frame sets the reconnect interval, so a browser EventSource re-opens it by itself; the caller's access is also re-checked periodically while the connection is held.

Stream Service Instance Status › path Parameters

id
​string · uuid · required · style: simple

Stream Service Instance Status › Responses

An open event stream. The schema describes the data: payload of one status frame, not a single JSON response body, and not the two metrics frames or the vm_progress frame, which carry their own shapes.

The `status` frame: the whole workspace, re-rendered from one payload. Sent on connect, on every `ProvisionStatusChanged` for a resource this instance owns, and on a five-second tick -- the tick because usage and container statuses are live cluster reads that nothing publishes an event for, so a stream that only reacted to events would show a frozen CPU figure on a perfectly healthy service.
​LifecycleVerb[] · required

Which of the product's declared [Capability::Lifecycle] verbs make sense to click right now (ZC-1154): the console renders that static list as its full set of buttons (a stopped container still offers Stop, greyed, because the workload kind supports it), and this is the subset that should render live. It never adds a verb the static list omits.

The intersection of three independent answers, all required:

  1. the product's declared Capability::Lifecycle verb list (what the workload kind supports at all, structural, read off its own resources);
  2. [kernel_core::domain::provisions::ServiceRuntimeState::permits_lifecycle_verb] (whether the verb does something sensible given the workload's live condition -- state above, converted); and
  3. [SubscriptionStatus::permits_lifecycle_verb] (whether the customer is commercially entitled to ask for it right now).

A verb missing from any one of the three is not permitted. This is deliberately a new field and state is unchanged: the frontend entitlement decision console/components/apps/bg-status-vocabulary.ts tried to own client-side is a decision this list now makes instead, but console still needs both fields -- one to render the full, disabled set, one to know which of them are live -- and adding a field to an SSE payload is backward compatible where removing state would not be.

​ResourceStatusFrameResponse[] · required

The instance's visible resources. Empty is normal: a per-seat licence provisions none (design doc §8.1), and so does an instance whose workloads have not been applied yet, which is what state: NotProvisioned says.

state
​string · enum · required

One word for the whole service, folded from its primary workloads' statuses (or, when it declares none, from all of them).

Enum values:
not_provisioned
provisioning
running
stopping
suspended
crash_looping
degraded
failed
​

Live crash detail while state is CrashLooping. Absent otherwise, and absent while crash looping if the cluster could not be reached inside the bounded timeout.

​

Live startup-failure detail while state is Failed because the workload never started (e.g. an image whose declared user cannot be verified non-root). Carries the kubelet's own reason and message so a customer is told why rather than left on a perpetual "Starting".

​

The in-guest first-boot gate, where the product has one (a Windows VPS). null means there is no gate, not that the guest is not ready: a consumer treating absence as "not ready" would never show a Linux service as usable.

​

The operation kernel is currently carrying out against this service, if any (ZC-1162). null is the common case: nothing is happening.

This is the field that answers "is a deploy happening right now", which state cannot: state: Provisioning does not distinguish a first-time bring-up from an in-place redeploy or reprovision, and a rolling update leaves the old pods serving, so state stays Running for the whole deployment and never signals anything happened at all (found while building ZC-1154). Kernel already tracks this fact as a typed, leased operation (kernel_core::domain::service_operations); this is that fact surfaced, not a new one invented for the stream.

A new field, like permitted_lifecycle_verbs above and for the same reason: adding a field to an SSE payload is backward compatible, changing state is not.


Purchase Subscription Trial

POST
/api/v2/subscriptions/{id}/trial/purchase

Buys a free trial. A trial still running is bought as an upgrade: the free period ends at payment and the paid period starts then, on the same running service, with nothing stopped or reprovisioned (the remaining free days are given up). A trial that ended without being kept is suspended with its data intact and deleted three days after it ended; until then it is bought as a restore, which brings the same service back. Price it first with GET /subscriptions/{id}/trial/purchase/quote. It moves no money: pay the returned invoice with POST /billing/invoices/{id}/pay-with-wallet or a card checkout on that invoice. Once the payment settles the subscription is active and paid from that moment and renewal is turned on; a restored trial's workload is started again, an upgraded one's keeps running untouched. If the trial ends before an upgrade invoice is paid it stays payable as a restore, unless the trial converts on renewal, which cancels it. Idempotent -- while the invoice is unpaid a repeated request answers 200 with the same invoice and created: false; the first answers 201. An invoice whose checkout failed or expired is replaced by a new one. Answered 409 with code trial_purchase_window_closed once the three days are over (no invoice is raised), trial_already_converted for a trial already bought, and trial_purchase_already_paid while a paid purchase is completing; 409 without those codes when another purchase request for the same subscription is in flight, which is retryable. 422 for a subscription that is not a buyable free trial. Requires the authority to pay this organization's invoices by card: its purchase role or its Billing role. Returns 404 for a subscription the caller cannot see.

Purchase Subscription Trial › path Parameters

id
​string · uuid · required · style: simple

Purchase Subscription Trial › Responses

The purchase invoice an earlier request raised, still unpaid

The invoice that buys an abandoned free trial. Small on purpose, like the renewal and cancellation responses: this write raised one invoice, and what the client needs is where to send the money and by when. The invoice itself is `GET /api/v2/billing/invoices/{id}`, and paying it is `pay-with-wallet` or a card checkout on that invoice.
amount_cents
​integer · int64 · required

Pre-tax amount, in cents.

created
​boolean · required

true when this request raised the invoice (201), false when it handed back the invoice an earlier request raised (200). Both are success; a client must not render the second as a failure.

currency
​string · required
deadline
​string · date-time · required

The moment the trial is handed over for deletion. Payment must be settled before it; a payment already in progress is honoured for a bounded time past it.

invoice_id
​string · uuid · required

The one payable purchase invoice for this trial.

​required

upgrade for a trial still running (the paid period starts at payment, on the same running service), restore for a lapsed one.

subscription_id
​string · uuid · required
total_amount_cents
​integer · int64 · required

Amount including tax, in cents -- what the customer pays.


Quote Subscription Trial Purchase

GET
/api/v2/subscriptions/{id}/trial/purchase/quote

Prices buying this free trial without raising anything: an upgrade of a trial still running (the free period ends at payment and the paid period starts then, on the same running service) or a restore of a lapsed one. One period of the trial's pinned price at the caller's own price, tax included, computed by the same code POST /subscriptions/{id}/trial/purchase raises its invoice with. Promotional credit is applied only when that invoice is issued and can only lower it. While a purchase invoice is open this answers that invoice exactly and names it in open_invoice_id. Readable by anybody who can see the subscription; buying needs the organization's purchase or billing role. The same refusals as the purchase: 409 trial_purchase_window_closed, trial_already_converted, trial_purchase_already_paid, and 422 for a subscription that is not a buyable free trial. Returns 404 for a subscription the caller cannot see.

Quote Subscription Trial Purchase › path Parameters

id
​string · uuid · required · style: simple

Quote Subscription Trial Purchase › Responses

What the purchase charges

What buying this free trial charges, before anything is raised (`V-0153`, `/v1`'s trial-upgrade preview). One period of the trial's pinned Price at the caller's own price, priced by the same code the purchase raises its invoice with, so these figures are the invoice's. The one thing they cannot include for an invoice not yet raised is promotional credit, which the ledger applies as a discount when the invoice is issued and which can only lower it. Once a purchase invoice is open, this answers that invoice exactly and names it in `open_invoice_id`.
amount_cents
​integer · int64 · required

Pre-tax amount, in cents.

currency
​string · required
deadline
​string · date-time · required

When the offer lapses: the trial's own end for an upgrade, the moment the trial is handed over for deletion for a restore.

​TrialPurchaseKindResponse · required

Which way a free trial is bought (V-0153).

subscription_id
​string · uuid · required
tax_amount_cents
​integer · int64 · required

Tax, in cents.

total_amount_cents
​integer · int64 · required

Amount including tax, in cents -- what the purchase charges.

open_invoice_id
​string | null · uuid

The purchase invoice already raised and still payable, whose figures these are; null when buying would raise a new one.

tax_label
​string | null
tax_rate
​number | null · double

The tax rate applied, as a fraction (0.21), when there is one.


List Unit Logs

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/logs

A page of the stored output of this service's units, read from the log store: all units merged by instant, or one with unit_id (live or scaled away, so a gone unit's logs stay readable, and a stopped service's too). Lines are kept 30 days.

direction=backward (default) is newest first and is how a panel opens and scrolls back; direction=forward with the last follow_cursor is how a follow polls (every poll_after_ms). lines are always oldest first. cursor is opaque and resumes exactly, without a duplicate or a gap; follow_cursor is the one that follows the newest line of the page, which is how a follow starts after the first page. search is a case-insensitive substring run on the server.

Each line has an id that is the same for the same line on the live stream. markers are lifecycle facts in the span this page covers, with the kernel's sentence in text: a pod started, was replaced, stopped, lost or removed, a container restarted (with its reason and exit code), or lines the rate cap moved to the archive (an overflow marker with from, to and count). A marker has a stable id; a poll that meets one twice draws it once. coverage is the kernel's sentence about what was read.

503 log-store-unavailable when the store does not answer; live tail is not affected. Needs the LogStream capability and the same access as reading a service's logs. Console only.

List Unit Logs › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Unit Logs › query Parameters

container
​string | null · style: form

One container name.

One container name.

Default: null
cursor
​string | null · style: form

A next_cursor from an earlier response. Opaque; hand back exactly what was given.

A next_cursor from an earlier response. Opaque; hand back exactly what was given.

Default: null
direction
​style: form

backward (default) or forward.

backward (default) or forward.

from
​string | null · date-time · style: form

Start of the window, RFC 3339. Clamped to the 30 days logs are kept.

Start of the window, RFC 3339. Clamped to the 30 days logs are kept.

Default: null
include_agent
​boolean | null · style: form

Include the unit agent's own lines (container zaroz-agent). Hidden by default; naming that container in container shows them too.

Include the unit agent's own lines (container zaroz-agent). Hidden by default; naming that container in container shows them too.

Default: null
limit
​integer | null · uint32 · min: 0 · style: form

Lines per page: 500 by default, at most 2000.

Lines per page: 500 by default, at most 2000.

Default: null
search
​string | null · style: form

Case-insensitive substring, at most 200 characters. Not a regular expression.

Case-insensitive substring, at most 200 characters. Not a regular expression.

Default: null
to
​string | null · date-time · style: form

End of the window, RFC 3339. Defaults to now.

End of the window, RFC 3339. Defaults to now.

Default: null
unit_id
​string | null · uuid · style: form

One unit, live or gone. Absent reads every unit of the service. A unit that is not one of this service's is a 404.

One unit, live or gone. Absent reads every unit of the service. A unit that is not one of this service's is a 404.

Default: null

List Unit Logs › Responses

The page

`GET .../units/logs`.
coverage
​string · required

The kernel's sentence about what this read covered.

​LogCoverageNoteResponse[] · required

The same, as separate facts a client may word itself.

​UnitLogLineResponse[] · required

Oldest first, whatever the direction read.

​UnitLogMarkerResponse[] · required

Lifecycle facts in the span this page owns, oldest first.

poll_after_ms
​integer · uint64 · min: 0 · required

How long a follow polls for, in milliseconds, so a client never hard-codes it.

truncated
​boolean · required

More lines exist in the direction read.

​UnitLogWindowResponse · required
follow_cursor
​string | null

The cursor that follows the newest line of this page: how a follow starts after the first (newest) page of a backward read, and where the next poll continues from after a forward one. Null for an empty page read backward.

next_cursor
​string | null

Where to continue in the direction read. Reading backward it is null once the retention edge is reached; reading forward it is the cursor to poll with next.


Download Unit Logs

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/logs/download

The same filters as listUnitLogs (without a cursor or a limit), streamed oldest first as a gzip file, as text (one line per log line: instant, u<slot>/<pod>, container, stream, text) or as ndjson (the fields of the list route). At most one download runs per organization at a time (429 otherwise), and one request carries at most 1 GiB of raw output: past that the file ends with a line saying it was cut and the instant to resume from. Console only.

Download Unit Logs › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Download Unit Logs › query Parameters

container
​string | null · style: form
Default: null
format
​style: form

text (default) or ndjson.

text (default) or ndjson.

from
​string | null · date-time · style: form
Default: null
include_agent
​boolean | null · style: form

Include the unit agent's own lines, as on the list route.

Include the unit agent's own lines, as on the list route.

Default: null
search
​string | null · style: form
Default: null
to
​string | null · date-time · style: form
Default: null
unit_id
​string | null · uuid · style: form
Default: null

Download Unit Logs › Responses

The gzip file

No data returned

Create Unit Log Stream Ticket

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/{unit_id}/logs/stream-ticket

Mints a short-lived signed URL for following one container of one running unit live, through the cluster's own edge gateway (a WebSocket, valid for a minute, re-minted by whoever keeps reading). since resumes from an instant, normally the at of the newest line on screen; the line id is the same here as in listUnitLogs, so a line seen in both is drawn once. 409 unit-not-running when the unit has no running pod (scaled away, stopped, or not started yet): its stored history is still readable. Live tail never touches the log store. The grant is recorded in the organization's audit feed. Console only.

Create Unit Log Stream Ticket › path Parameters

unit_id
​string · uuid · required · style: simple

The unit to follow.

The unit to follow.

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Create Unit Log Stream Ticket › query Parameters

container
​string | null · style: form

Which container the ticket is minted for. May be omitted when the pod runs exactly one.

Which container the ticket is minted for. May be omitted when the pod runs exactly one.

Default: null
since
​string | null · style: form

RFC 3339 instant to resume the stream from, normally the at of the last line already on screen.

RFC 3339 instant to resume the stream from, normally the at of the last line already on screen.

Default: null

Create Unit Log Stream Ticket › Responses

A signed, short-lived URL for one container's live output

`POST /api/v2/console/service-instances/{id}/resources/{resource_id}/logs/stream-ticket`.
container
​string · required

The container the ticket is minted for. The gateway refuses any other, so a client switching containers mints a new ticket.

expires_at
​string · date-time · required

When the ticket stops being accepted. A client re-mints on expiry; the short life is what makes a leaked URL harmless.

url
​string · required

The gateway URL to open a WebSocket against. Carries the signed ticket in its query string and is single-purpose: it grants a read of exactly one container's output and nothing else.


Get Unit Metrics

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/metrics

CPU, memory and network of a service's units over 1h, 6h, 24h, 7d or 30d, each as 120 buckets (30 s, 3 min, 12 min, 84 min or 6 h wide) bucketed by the metrics store. series is a comma-separated list of cpu, memory, network_in, network_out, log_lines_archived (the lines a container wrote above the log rate cap, which are moved to the archive and not dropped) and restarts (container terminations per bucket, counted from the kernel's termination records, zero where none ended); it defaults to all. Without unit_id each series has one aggregate line (the units summed) and a line per unit for at most ten units (the ten with the highest mean over the window), with omitted_units counting the rest; with unit_id it has that unit's line only and its ceiling as limit. A bucket with no sample is null, never zero. Each line carries latest, mean and max of its buckets, computed by the kernel. available_windows lists the windows worth offering for the service's age. coverage is the kernel's sentence about what was read, with coverage_code and coverage_params for a client that words it in another language. 503 metrics_store_unavailable when the metrics store does not answer. Console only.

Get Unit Metrics › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Get Unit Metrics › query Parameters

series
​string | null · style: form

Comma-separated, any of cpu, memory, network_in, network_out, log_lines_archived and restarts. Absent means all of them.

Comma-separated, any of cpu, memory, network_in, network_out, log_lines_archived and restarts. Absent means all of them.

unit_id
​string | null · uuid · style: form

Narrows the read to one unit of this service.

Narrows the read to one unit of this service.

window
​UnitMetricWindow · enum · style: form

1h (the default), 6h, 24h, 7d or 30d, each answered as 120 buckets.

1h (the default), 6h, 24h, 7d or 30d, each answered as 120 buckets.

Enum values:
1h
6h
24h
7d
30d
Default: 1h

Get Unit Metrics › Responses

The requested series over the window

`GET .../units/metrics`. `log_lines_archived` counts the lines a container wrote above the per-container rate cap, which are moved to the log archive rather than dropped (decision-0300, D5 revised).
available_windows
​UnitMetricWindow[] · required

The windows worth offering for this service: a service created an hour ago has no use for 30d.

Enum values:
1h
6h
24h
7d
30d
bucket_count
​integer · uint32 · min: 0 · required

How many buckets every points array holds.

coverage
​string · required

The kernel's sentence about what was read, shown beside the charts as given.

coverage_code
​string · required

Which sentence coverage is: metrics_shown or metrics_none_recorded.

​object · required

The facts in it: window and to (RFC 3339), for a client that words it itself.

from
​string · date-time · required

Start of the first bucket.

omitted_units
​integer · uint32 · min: 0 · required

Units that wrote samples in the window beyond the ones drawn.

​UnitMetricSeriesResponse[] · required
step_seconds
​integer · uint32 · min: 0 · required
to
​string · date-time · required

End of the window.

window
​UnitMetricWindow · enum · required

A window a customer may ask for.

Enum values:
1h
6h
24h
7d
30d
unit_id
​string | null · uuid

Get Unit Count Series

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/count

How many units a service ran over 1h, 6h, 24h, 7d or 30d, in 120 buckets: running is integrated from the same pod intervals the bill is metered from (the most and fewest units running at one instant in each bucket, and the most billed as extra), desired is the count the platform wanted at the end of each bucket (absent for a service with no scaling policy and no scaling history), included is the plan's included units, the billing boundary, and markers are the scaling decisions inside the window (scale up, scale down, refused, rollout paused) each with the kernel's sentence (summary, plus summary_code and summary_params for a client that words it itself). Console only.

Get Unit Count Series › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Get Unit Count Series › query Parameters

window
​UnitMetricWindow · enum · style: form

A window a customer may ask for.

Enum values:
1h
6h
24h
7d
30d
Default: 1h

Get Unit Count Series › Responses

Units over time

`GET .../units/count`: units over time, from the same pod intervals the bill is metered from, and the scaling decisions.
from
​string · date-time · required
included
​integer · int32 · required

The units the plan includes: units beyond this are billed as extra. Drawn as a threshold.

​UnitScalingMarkerResponse[] · required
​UnitCountBucketResponse[] · required
step_seconds
​integer · uint32 · min: 0 · required
to
​string · date-time · required
window
​UnitMetricWindow · enum · required

A window a customer may ask for.

Enum values:
1h
6h
24h
7d
30d
​array | null

Absent for a service that has no scaling policy and no scaling history.


List Unit Terminations

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/{unit_id}/terminations

The times a container of one unit was restarted, newest first, kept 30 days: the container, the kubelet's restart count, the reason (OOMKilled, Error, Completed, ... or unknown) and the exit code. from and to bound the observation time; page with cursor from next_cursor. A unit that is gone is still listed while its history is kept; a unit of another service is a 404. Console only.

List Unit Terminations › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
unit_id
​string · uuid · required · style: simple

List Unit Terminations › query Parameters

cursor
​string | null · style: form

The next_cursor of the previous page.

The next_cursor of the previous page.

from
​string | null · date-time · style: form

Earliest observation, inclusive. Defaults to, and is held to, the 30 days kept.

Earliest observation, inclusive. Defaults to, and is held to, the 30 days kept.

limit
​integer | null · uint32 · min: 0 · style: form

Rows per page, 1 to 200. Defaults to 50.

Rows per page, 1 to 200. Defaults to 50.

to
​string | null · date-time · style: form

Latest observation, inclusive. Defaults to now.

Latest observation, inclusive. Defaults to now.

List Unit Terminations › Responses

One page of terminations

`GET .../units/{unit_id}/terminations`, newest first.
​UnitTerminationEntryResponse[] · required
next_cursor
​string | null

Pass as cursor for the next page; absent on the last.


List Unit Pods

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/{unit_id}/pods

Every pod one unit has had, newest first: its incarnation (1, 2, 3, ...), name, whether the plan covered it (billing_class), when it started and ended, why it ended (end_reason: replaced, stopped, torn_down or lost; absent while it runs), how many container restarts it had and how its last container ended (reason and exit code). Page with cursor from next_cursor. A unit that is gone is still listed while its history is kept; a unit of another service and an invented id are the same 404. Console only.

List Unit Pods › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
unit_id
​string · uuid · required · style: simple

List Unit Pods › query Parameters

cursor
​string | null · style: form

The next_cursor of the previous page.

The next_cursor of the previous page.

limit
​integer | null · uint32 · min: 0 · style: form

Rows per page, 1 to 200. Defaults to 50.

Rows per page, 1 to 200. Defaults to 50.

List Unit Pods › Responses

One page of pods

`GET .../units/{unit_id}/pods`, newest first.
​UnitPodResponse[] · required
next_cursor
​string | null

Pass as cursor for the next page; absent on the last.


List Service Units

GET
/api/v2/projects/{project_id}/service-instances/{id}/units

The units this service runs, lowest slot first: each unit's id (the key of its logs), slot, state, whether it is covered by the plan (included) or billed as an extra unit, its current pod and when that pod started, its restarts in the last 24 hours, how its latest container ended, and its latest sampled load with the sample time. include_gone=true also lists the units scaled away within the 30 days their logs are kept, so those logs stay reachable (deleted_at says they are gone, kept_until when they age out).

Around the list: scalable (the product allows more than one unit), max_units (the product's maximum capped by the organization's limit), included_units, volume_locked (a shared volume holds the service to one unit) and multi_unit_in_use (more than one unit now, or an autoscaled policy), restarts_24h (container terminations of the whole service in the last 24 hours, gone units included) and desired_state (stopped when every live unit is meant to be stopped). organization_limits is the organization's live units and pod starts in the last hour, each with its limit and a level (ok, near from 80 percent, at_limit), and nearing for the console's notice that points to support; at the limit growth is refused and running units are never removed. A service that does not run as units, or has not been provisioned, has an empty list.

Besides a person who may see the service, a service principal may call this with a units.read or units.control grant on the service; to a principal, a service it holds no grant on is absent (404). Each unit has a key (its stable name: u{slot} for a unit the policy made), its labels and its origin. Narrow the list with label=key:value (repeat it to require several, at most 8, all must match), origin= and key_prefix=. The list leaves each unit's user_data out; include=user_data adds it. Console only.

List Service Units › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Service Units › query Parameters

include
​string | null · style: form

user_data returns each unit's user_data too. The list leaves it out by default to stay small; GET .../units/{key} always has it.

user_data returns each unit's user_data too. The list leaves it out by default to stay small; GET .../units/{key} always has it.

include_gone
​boolean | null · style: form

Also list the units scaled away within the 30 days their logs are kept, after the live ones of their slot, so their logs stay reachable. Off by default.

Also list the units scaled away within the 30 days their logs are kept, after the live ones of their slot, so their logs stay reachable. Off by default.

key_prefix
​string | null · style: form

Only units whose key starts with this text (lowercase letters, digits, hyphens).

Only units whose key starts with this text (lowercase letters, digits, hyphens).

label
​array | null · style: form

Only units with this label, label=key:value. Repeat it to require several (all must match, at most 8). A value may contain colons.

Only units with this label, label=key:value. Repeat it to require several (all must match, at most 8). A value may contain colons.

origin
​style: form

Only units of this origin: policy, code or warm.

Only units of this origin: policy, code or warm.

List Service Units › Responses

The units

`GET .../units`. Not paginated: a Container has at most a few dozen units.
​UnitResponse[] · required
desired_state
​string · enum · required

What the service as a whole is meant to be doing: stopped when it has live units and none is meant to run, else running.

Enum values:
running
stopped
included_units
​integer · int32 · required

How many units the plan's price covers; units above it are billed as extras.

max_units
​integer · int32 · required

The most units this service may run: the product's maximum capped by the organization's limit.

multi_unit_in_use
​boolean · required

The service runs more than one unit now, or its policy is autoscaled.

restarts_24h
​integer · int64 · required

Container terminations across every unit of the service in the last 24 hours, gone units included. Computed by the kernel; clients do not sum the units.

scalable
​boolean · required

The product allows more than one unit.

volume_locked
​boolean · required

A shared volume holds the service to exactly one unit, whatever the product allows.

​

The organization's units and pod starts against its limits, which the console's near-limit notice reads. Absent for a service that does not run as units.


Create Service Unit

POST
/api/v2/projects/{project_id}/service-instances/{id}/units

Creates a unit by its key for the program that controls this service's units (policy mode code), starting it unless start is false (a unit created stopped holds no slot and costs nothing). Idempotent on the key: a live unit with that key is returned with created: false and 200; when it is stopped and start is true it is started, its labels and user_data replaced by the ones given (the call that wakes a stopped unit). A running unit given other labels or user_data is refused 409 unit_input_differs: a running unit's input changes only by allocate or by PATCH of its labels. A new unit answers 201 with created: true.

Before any pod is created the call checks the service's maximum (container_unit_limit), the organization's unit limit (org_unit_limit) and start rate (429 start_rate), and the spend cap (spend_cap); a unit created stopped checks the stopped-unit cap (stopped_unit_limit). Outside code mode the answer is 409 policy_not_code. Every refusal has a stable code and a docs_url. Besides a person who may change the service, a service principal with a units.control grant on it may call this; to a principal, a service it holds no such grant on is absent (404). Console only.

Create Service Unit › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Create Service Unit › Request Body

`POST .../units`: create a unit by key, or get the live one with that key.
key
​string · required

1 to 40 lowercase letters, digits or hyphens, starting and ending with a letter or digit. Never changes. u followed by digits, keys starting w- or zaroz, and the words the units routes use (allocate, events, policy, ...) are reserved.

​object | null

At most 16 pairs; replaced whole. Absent means none for a new unit.

Default: null
​

What the unit does when its process exits. Defaults to the policy's default_on_exit.

Default: null
start
​boolean

false creates the unit stopped: it holds no slot and costs nothing until it starts. Defaults to true.

Default: true
user_data
​object | array | string | number | boolean | null

Any JSON value up to 64 KiB, at most 32 levels deep. Not a secret store.

Default: null

Create Service Unit › Responses

The live unit with that key (created: false)

A unit as a control call left it.
created
​boolean · required

true only when this call made the unit.

​object · required

The unit, with its user_data.


Get Service Unit

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/{key}

One live unit by its key: everything the list shows for it plus its user_data (null when it has none). user_data is not a secret store, every reader of the unit sees it. A key the service has no unit under, an invented one and another service's answer the same 404. Besides a person who may see the service, a service principal with a units.read or units.control grant on it may read it. Console only.

Get Service Unit › path Parameters

id
​string · uuid · required · style: simple
key
​string · required · style: simple

The unit's key, GET .../units lists it.

The unit's key, GET .../units lists it.

project_id
​string · uuid · required · style: simple

Get Service Unit › Responses

The unit

desired_state
​string · enum · required

running when the platform wants the unit's pod to exist, stopped when it wants it gone.

Enum values:
running
stopped
key
​string · required

The unit's stable name and the first label of its private name: u{ordinal} for a unit the policy made, the customer's own key otherwise. Addresses the unit in .../units/{key}.

​object · required

The unit's labels, {} when it has none. Filter the list with label=key:value.

​required

Who made the unit.

​UnitPublicEndpointResponse[] · required

The public addresses that reach this unit alone, one per per-unit port (kind: unit endpoints). Held for as long as the unit's slot exists, so each survives every pod restart of the unit. Empty when the service has no per-unit port.

restarts_24h
​integer · int64 · required

Container terminations in the last 24 hours.

​required

What the unit's pod says, or what its desired state implies when it has no pod. A unit that is gone reads as suspended; deleted_at says it is gone.

unit_id
​string · uuid · required

The unit's id, the key of its logs. A slot can have had several units over time.

user_data
​object | array | string | number | boolean | null · required

What the unit was given as input. Present only when asked for (include=user_data on the list, always on GET .../units/{key}); null means the unit has none. Not a secret store: every reader of the unit sees it.

​

Set once allocate handed the unit to a caller (Units phase 2); null while it has not been. An allocation is never undone: the unit ends by its exit, a stop or a destroy.

​

Whether the unit's pod is covered by the plan's price (included) or billed as an extra unit (extra). Absent while the unit has no pod.

current_incarnation
​integer | null · int32

The newest pod this unit has had, running or not. Present for a stopped or gone unit too.

deleted_at
​string | null · date-time

When the unit was scaled away. Only listed with include_gone=true.

incarnation
​integer | null · int32

How many pods this unit has had, counting the current one. Absent while it has no pod.

kept_until
​string | null · date-time

When a gone unit's logs, pods and terminations age out and are no longer listed. Present exactly when deleted_at is.

​
​

Absent for a unit never sampled, a gone one, or while samples cannot be read.

ordinal
​integer | null · int32

The slot, 1..N, held while the unit can run. Null for a stopped unit of the customer's code or the warm buffer, which gives its slot back (Units phase 2).

pod_name
​string | null
private_name
​string | null

The name another service in the same project resolves to reach this unit alone, <key>.<service slug> (for example u2.worker). The service's slug by itself reaches every ready unit. Absent when the service has no private name.

started_at
​string | null · date-time

When the current pod was created.


Destroy Service Unit

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/units/{key}

Drains the unit, then destroys it, running or stopped. Final: the key answers 404 afterwards, and may be used again by a later create. code mode only (409 policy_not_code). A service principal with units.control on the service may call this. Console only.

Destroy Service Unit › path Parameters

id
​string · uuid · required · style: simple
key
​string · required · style: simple

The unit's key, GET .../units lists it.

The unit's key, GET .../units lists it.

project_id
​string · uuid · required · style: simple

Destroy Service Unit › Responses

Destroyed

No data returned

Update Service Unit Labels

PATCH
/api/v2/projects/{project_id}/service-instances/{id}/units/{key}

Replaces the unit's labels whole: {} clears them. At most 16 pairs; a key is 1 to 32 lowercase letters, digits, dots, underscores or hyphens, a value at most 64 characters without control characters, and the whole map at most 2 KiB. A refusal names its code before the colon in details. Works in every policy mode. Returns the unit. Besides a person who may change the service, a service principal with a units.control grant on it may call this. Console only.

Update Service Unit Labels › path Parameters

id
​string · uuid · required · style: simple
key
​string · required · style: simple

The unit's key, GET .../units lists it.

The unit's key, GET .../units lists it.

project_id
​string · uuid · required · style: simple

Update Service Unit Labels › Request Body

`PATCH .../units/{key}`: the labels, replaced whole. `{}` clears them.
​object · required

At most 16 pairs. A key is 1 to 32 lowercase letters, digits, dots, underscores or hyphens; a value is at most 64 characters without control characters; the whole map at most 2 KiB serialized.

Update Service Unit Labels › Responses

The unit as stored

desired_state
​string · enum · required

running when the platform wants the unit's pod to exist, stopped when it wants it gone.

Enum values:
running
stopped
key
​string · required

The unit's stable name and the first label of its private name: u{ordinal} for a unit the policy made, the customer's own key otherwise. Addresses the unit in .../units/{key}.

​object · required

The unit's labels, {} when it has none. Filter the list with label=key:value.

​required

Who made the unit.

​UnitPublicEndpointResponse[] · required

The public addresses that reach this unit alone, one per per-unit port (kind: unit endpoints). Held for as long as the unit's slot exists, so each survives every pod restart of the unit. Empty when the service has no per-unit port.

restarts_24h
​integer · int64 · required

Container terminations in the last 24 hours.

​required

What the unit's pod says, or what its desired state implies when it has no pod. A unit that is gone reads as suspended; deleted_at says it is gone.

unit_id
​string · uuid · required

The unit's id, the key of its logs. A slot can have had several units over time.

user_data
​object | array | string | number | boolean | null · required

What the unit was given as input. Present only when asked for (include=user_data on the list, always on GET .../units/{key}); null means the unit has none. Not a secret store: every reader of the unit sees it.

​

Set once allocate handed the unit to a caller (Units phase 2); null while it has not been. An allocation is never undone: the unit ends by its exit, a stop or a destroy.

​

Whether the unit's pod is covered by the plan's price (included) or billed as an extra unit (extra). Absent while the unit has no pod.

current_incarnation
​integer | null · int32

The newest pod this unit has had, running or not. Present for a stopped or gone unit too.

deleted_at
​string | null · date-time

When the unit was scaled away. Only listed with include_gone=true.

incarnation
​integer | null · int32

How many pods this unit has had, counting the current one. Absent while it has no pod.

kept_until
​string | null · date-time

When a gone unit's logs, pods and terminations age out and are no longer listed. Present exactly when deleted_at is.

​
​

Absent for a unit never sampled, a gone one, or while samples cannot be read.

ordinal
​integer | null · int32

The slot, 1..N, held while the unit can run. Null for a stopped unit of the customer's code or the warm buffer, which gives its slot back (Units phase 2).

pod_name
​string | null
private_name
​string | null

The name another service in the same project resolves to reach this unit alone, <key>.<service slug> (for example u2.worker). The service's slug by itself reaches every ready unit. Absent when the service has no private name.

started_at
​string | null · date-time

When the current pod was created.


Get Scaling Policy

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/policy

The scaling policy in force. A service that was never given one is fixed at one unit, with updated_at the service's creation. While a template change is still reaching the units, rollout_status says how: the strategy, the target_template, how many stale_units still run an older one and how many are replaced_units, and under replace_idle the units it waiting_on (allocated ones are replaced when they end, busy ones at their next idle). Console only.

Get Scaling Policy › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Get Scaling Policy › Responses

The policy in force

`GET .../units/policy`: the policy in force. A service that was never given one is `fixed` at one unit.
contract_version
​integer · int32 · required
​required

code: what a unit does when its process exits, unless it was created with its own. Always restart in the other modes.

destroy_stopped_after_days
​integer · int32 · required

A stopped unit of the customer's code is destroyed after this many days.

max_units
​integer · int32 · required
min_units
​integer · int32 · required
​ScalingMode · required

Whether the number of units is a constant or follows load and the clock. Stored as text and constrained in SQL to the same two words.

​required

auto: the phase-1 readiness; signal: Ready only once the unit signals it.

ready_timeout_seconds
​integer · int32 · required

A unit not Ready this long after its pod started is failed (units that carry the agent only).

​RolloutStrategy · required

How a template change reaches the units. Phase 1 has rolling; phase 2 adds replace_idle and next_start (plan 3.5), whose selection rules are P2-K8's: until it lands they are stored and validated, and roll as rolling does.

scale_down_stabilisation_seconds
​integer · int32 · required
scale_up_cooldown_seconds
​integer · int32 · required
​ScheduleRule[] · required

Bounds overrides that take effect when their cron expression fires and hold until the next rule fires.

updated_at
​string · date-time · required
cpu_target_percent
​integer | null · int32
drain_idle_after_seconds
​integer | null · int32

autoscaled: a unit idle this long may be drained while above the minimum.

​

code: what happens to a unit idle for idle_after_seconds.

idle_after_seconds
​integer | null · int32
keep_accepting
​integer | null · int32

autoscaled: keep this many units accepting beside the ones that say they are full.

memory_target_percent
​integer | null · int32
​

Present while a template change is still reaching the units; absent once every running unit is on the current template. Only GET reads it.

spend_cap_cents
​integer | null · int64

The most, in cents, the extra units may cost in a month before scaling refuses to grow. Absent means the worst case the maximum allows.

warm_units
​integer | null · int32

code: ready, unallocated warm units kept on top of the others. Absent in the other modes.


Update Scaling Policy

PATCH
/api/v2/projects/{project_id}/service-instances/{id}/units/policy

Changes the scaling policy. Omitted fields keep their value; nullable fields take null to clear. A fixed policy is one count (min_units equals max_units; giving only one of them sets both) and applies immediately, adding or removing units. An autoscaled policy needs a CPU target, a memory target or a schedule rule and is acted on by the autoscaler within its bounds. max_units cannot exceed the service's unit limit, and a service with a shared volume runs exactly one unit. A code policy hands the count to the customer's program: its minimum is 0, it has no load targets or schedule, and warm_units ready units are kept on top; idle_action, default_on_exit, destroy_stopped_after_days and ready_timeout_seconds govern its units. keep_accepting and drain_idle_after_seconds are autoscaled signal rules. Leaving code mode answers 409 code_units_exist while units the program owns remain. A refusal names a reason code before the colon in details. 503 with count_not_applied means the policy is saved but the units could not be changed yet: send the same update again. Console only.

Update Scaling Policy › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Update Scaling Policy › Request Body

`PATCH .../units/policy`. Omitted fields keep their value; the nullable ones take `null` to clear. `mode: fixed` with only `min_units` (or only `max_units`) means exactly that many units. Switching to `fixed` drops the load targets and schedule rules unless the same request sets them. `mode: code` (Units phase 2) hands the count to the customer's program: the minimum becomes 0, the load targets and schedule rules are dropped, and `warm_units` (default 0) ready units are kept on top. Switching away from `code` is refused with `code_units_exist` while a unit the program owns is still there; the warm buffer is removed by the switch.
contract_version
​integer | null · int32
Default: null
cpu_target_percent
​integer | null · int32
Default: null
​
Default: null
destroy_stopped_after_days
​integer | null · int32
Default: null
drain_idle_after_seconds
​integer | null · int32
Default: null
​
Default: null
idle_after_seconds
​integer | null · int32
Default: null
keep_accepting
​integer | null · int32
Default: null
max_units
​integer | null · int32
Default: null
memory_target_percent
​integer | null · int32
Default: null
min_units
​integer | null · int32
Default: null
​
Default: null
​
Default: null
ready_timeout_seconds
​integer | null · int32
Default: null
​
Default: null
scale_down_stabilisation_seconds
​integer | null · int32
Default: null
scale_up_cooldown_seconds
​integer | null · int32
Default: null
​array | null
Default: null
spend_cap_cents
​integer | null · int64
Default: null
warm_units
​integer | null · int32
Default: null

Update Scaling Policy › Responses

The policy as stored

`GET .../units/policy`: the policy in force. A service that was never given one is `fixed` at one unit.
contract_version
​integer · int32 · required
​required

code: what a unit does when its process exits, unless it was created with its own. Always restart in the other modes.

destroy_stopped_after_days
​integer · int32 · required

A stopped unit of the customer's code is destroyed after this many days.

max_units
​integer · int32 · required
min_units
​integer · int32 · required
​ScalingMode · required

Whether the number of units is a constant or follows load and the clock. Stored as text and constrained in SQL to the same two words.

​required

auto: the phase-1 readiness; signal: Ready only once the unit signals it.

ready_timeout_seconds
​integer · int32 · required

A unit not Ready this long after its pod started is failed (units that carry the agent only).

​RolloutStrategy · required

How a template change reaches the units. Phase 1 has rolling; phase 2 adds replace_idle and next_start (plan 3.5), whose selection rules are P2-K8's: until it lands they are stored and validated, and roll as rolling does.

scale_down_stabilisation_seconds
​integer · int32 · required
scale_up_cooldown_seconds
​integer · int32 · required
​ScheduleRule[] · required

Bounds overrides that take effect when their cron expression fires and hold until the next rule fires.

updated_at
​string · date-time · required
cpu_target_percent
​integer | null · int32
drain_idle_after_seconds
​integer | null · int32

autoscaled: a unit idle this long may be drained while above the minimum.

​

code: what happens to a unit idle for idle_after_seconds.

idle_after_seconds
​integer | null · int32
keep_accepting
​integer | null · int32

autoscaled: keep this many units accepting beside the ones that say they are full.

memory_target_percent
​integer | null · int32
​

Present while a template change is still reaching the units; absent once every running unit is on the current template. Only GET reads it.

spend_cap_cents
​integer | null · int64

The most, in cents, the extra units may cost in a month before scaling refuses to grow. Absent means the worst case the maximum allows.

warm_units
​integer | null · int32

code: ready, unallocated warm units kept on top of the others. Absent in the other modes.


Quote Scaling Policy

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/policy/quote

What a proposed policy can cost in a month, without saving it. Takes the body of PATCH .../units/policy and is refused for the same reasons (a reason code before the colon in details; not_provisioned for a fixed policy on a service with no cluster yet). monthly_cents is the worst case: every unit above the plan's included ones running every hour of the longest month at the Price's extra_unit_hours rate, held to the policy's spend_cap_cents (cap_binds says the cap is what limits it). Each extra unit start bills at least minimum_billed_seconds; that minimum is shown but never changes the worst case. A Price with no extra_unit_hours line is metered: false and every amount is zero. Whether the wallet can hold the amount is the save's check, not the quote's. Console only.

Quote Scaling Policy › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Quote Scaling Policy › Request Body

`PATCH .../units/policy`. Omitted fields keep their value; the nullable ones take `null` to clear. `mode: fixed` with only `min_units` (or only `max_units`) means exactly that many units. Switching to `fixed` drops the load targets and schedule rules unless the same request sets them. `mode: code` (Units phase 2) hands the count to the customer's program: the minimum becomes 0, the load targets and schedule rules are dropped, and `warm_units` (default 0) ready units are kept on top. Switching away from `code` is refused with `code_units_exist` while a unit the program owns is still there; the warm buffer is removed by the switch.
contract_version
​integer | null · int32
Default: null
cpu_target_percent
​integer | null · int32
Default: null
​
Default: null
destroy_stopped_after_days
​integer | null · int32
Default: null
drain_idle_after_seconds
​integer | null · int32
Default: null
​
Default: null
idle_after_seconds
​integer | null · int32
Default: null
keep_accepting
​integer | null · int32
Default: null
max_units
​integer | null · int32
Default: null
memory_target_percent
​integer | null · int32
Default: null
min_units
​integer | null · int32
Default: null
​
Default: null
​
Default: null
ready_timeout_seconds
​integer | null · int32
Default: null
​
Default: null
scale_down_stabilisation_seconds
​integer | null · int32
Default: null
scale_up_cooldown_seconds
​integer | null · int32
Default: null
​array | null
Default: null
spend_cap_cents
​integer | null · int64
Default: null
warm_units
​integer | null · int32
Default: null

Quote Scaling Policy › Responses

The quote

`POST .../units/policy/quote`: what a proposed policy can cost in a month. Every figure is computed by the kernel from the Price; clients show them, never derive them.
cap_binds
​boolean · required

The spend cap is what limits monthly_cents.

extra_units
​integer · int64 · required

Units above the plan's included ones, at the policy's ceiling.

hourly_cents
​integer · int64 · required

What one extra unit costs for one hour, in cents.

metered
​boolean · required

The Price meters extra units. When false every amount is zero.

minimum_billed_seconds
​integer · int64 · required

Each extra unit start bills at least this long.

minimum_start_cents
​integer · int64 · required

What one extra unit start bills at that minimum, in cents.

monthly_cents
​integer · int64 · required

The most the policy can cost in a month, in cents: the worst case held to the cap.

worst_case_cents
​integer · int64 · required

Every extra unit running all month, before the spend cap, in cents.

worst_case_hours
​integer · int64 · required

Hours of extra-unit time the worst case bills (every extra unit, the longest month).


List Unit Events

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/events

The Container's unit events, oldest first: every unit transition (unit.created, unit.starting, unit.ready, unit.stopped, unit.exited, unit.failed, unit.destroyed, ...) and every scaling change (units.scaled), each as the contract-v1 envelope with a per-Container, gapless seq. Read with a cursor: after is the last seq you saw and next_after the one to send next. With wait (0 to 30 seconds) and nothing new, the request is held and returns as soon as an event arrives (long-poll). types keeps only the listed types, unit_key and label (key:value, matched against the labels the unit had when the event was written) keep only events about that unit, limit caps the page (500). To open on the latest, send newest=N (or before=<seq> for the page before a cursor): the answer is newest first, with has_earlier and next_before to load earlier and next_after to follow live; a backwards read does not wait and never answers 410. Events are kept 30 days; a cursor older than the oldest kept event answers 410 with code cursor_expired and resume_after, the cursor to read from the oldest kept event. A service principal with a units.read or units.control grant on the service may read events too; to a principal, a service it holds no grant on is absent (404). Console only.

List Unit Events › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Unit Events › query Parameters

after
​integer | null · int64 · style: form

The last seq already seen; events after it are returned. Default 0 (from the oldest retained). A cursor older than the oldest retained event answers 410 cursor_expired.

The last seq already seen; events after it are returned. Default 0 (from the oldest retained). A cursor older than the oldest retained event answers 410 cursor_expired.

before
​integer | null · int64 · style: form

Read backwards: events with a seq lower than this, newest first. Send next_before from the last page to "load earlier". Not with after, newest or wait; a backwards read never answers 410.

Read backwards: events with a seq lower than this, newest first. Send next_before from the last page to "load earlier". Not with after, newest or wait; a backwards read never answers 410.

label
​string | null · style: form

Only events about a unit that carried this label when the event was written, label=key:value (one; a value may contain colons).

Only events about a unit that carried this label when the event was written, label=key:value (one; a value may contain colons).

limit
​integer | null · int64 · style: form

At most this many events, 1 to 500. Default 100.

At most this many events, 1 to 500. Default 100.

newest
​integer | null · int64 · style: form

Read backwards from the head: the newest this many events (1 to 500), newest first. Takes the place of limit. Not with after, before or wait. next_after of the answer follows the log live.

Read backwards from the head: the newest this many events (1 to 500), newest first. Takes the place of limit. Not with after, before or wait. next_after of the answer follows the log live.

types
​string | null · style: form

Comma-separated event types to keep (for example unit.ready,unit.exited). Default every type.

Comma-separated event types to keep (for example unit.ready,unit.exited). Default every type.

unit_key
​string | null · style: form

Only events about the unit with this key. Events about the Container itself (units.scaled, rollout.*) have no unit and are left out. after reads stay gap-free: the cursor still moves only past events returned.

Only events about the unit with this key. Events about the Container itself (units.scaled, rollout.*) have no unit and are left out. after reads stay gap-free: the cursor still moves only past events returned.

wait
​integer | null · int64 · style: form

With nothing new, hold the request up to this many seconds (0 to 30) and return as soon as an event arrives. Default 0.

With nothing new, hold the request up to this many seconds (0 to 30) and return as soon as an event arrives. Default 0.

List Unit Events › Responses

A page of events

`GET .../units/events`.
​UnitEventResponse[] · required

Oldest first; newest first for a read with before or newest.

has_earlier
​boolean · required

Backwards reads only: whether events matching the filters exist before this page. Always false for a forward read.

next_after
​integer · int64 · required

Pass this as after next: the last returned seq, or the one you sent when nothing was new. After a backwards read it is the highest seq returned, so the view follows live from the page it opened on.

oldest_retained_seq
​integer · int64 · required

The oldest seq still kept (events are kept 30 days).

next_before
​integer | null · int64

Backwards reads only: pass this as before to load earlier events (the lowest seq returned); null for a forward read or an empty page.


List Scaling Decisions

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/decisions

The scaling decision log, newest first, kept 30 days. Consecutive identical decisions are one entry: at is the first, last_at the latest and repeat_count how many. from and to keep the entries whose run overlaps that range and action keeps one action; every entry carries summary, the kernel's sentence for it. Console only.

List Scaling Decisions › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Scaling Decisions › query Parameters

action
​style: form

Only decisions with this action.

Only decisions with this action.

Default: null
from
​string | null · date-time · style: form

Only decisions whose run (at to last_at) reaches this instant or later.

Only decisions whose run (at to last_at) reaches this instant or later.

Default: null
page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20
to
​string | null · date-time · style: form

Only decisions made at or before this instant.

Only decisions made at or before this instant.

Default: null

List Scaling Decisions › Responses

One page of the log

​ScalingDecisionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Allocate Service Unit

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/allocate

Hands you one warm unit that is ready, accepting, not allocated and not draining, preferring units on the current template and then the longest ready, and sets its labels and user_data when given (the match's roster). Atomic: two calls never receive the same unit. The warm buffer refills behind it. The unit's agent writes /zaroz/allocation.json and /zaroz/user-data.json and acknowledges; the answer comes as soon as the allocation is on its way, with unit.allocation.delivered_at null, and ?wait=delivered holds it until the agent acknowledges or 10 seconds pass. A delivery not acknowledged in 10 seconds is the event unit.allocation_undelivered; the unit stays allocated. An allocation is never undone: the unit ends by its exit, a stop or a destroy.

Idempotency-Key (optional header, up to 64 characters): the same key returns the same unit while it lives (replayed: true, 200); without one every call allocates another unit. With no warm unit ready, if_none: fail (the default) answers 409 no_warm_unit with warm_ready, warm_starting and retry_after_seconds; if_none: create cold-starts a warm unit already allocated to you and answers 202 with it starting, after the same limit and spend checks as a create. When the unit's region cannot be reached the allocation is undone and the answer is 503 region_unreachable. code mode only (409 policy_not_code). Every refusal has a stable code and a docs_url. A service principal with units.control on the service may call this. Console only.

Allocate Service Unit › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Allocate Service Unit › query Parameters

wait
​style: form

Allocate Service Unit › Headers

Idempotency-Key
​string · minLength: 1 · maxLength: 64 · style: simple

Optional, 1 to 64 printable ASCII characters. The same key returns the same unit while it lives, instead of allocating another.

Allocate Service Unit › Request Body

`POST .../units/allocate`: send `{}` to take any ready warm unit as it is.
​

fail (default) answers 409 no_warm_unit when no warm unit is ready; create cold-starts a new one already allocated to you (202).

Default: fail
​object | null

Replaces the allocated unit's labels whole when given.

Default: null
user_data
​object | array | string | number | boolean | null

Replaces the allocated unit's user_data whole when given: the match's roster, map, mode.

Default: null

Allocate Service Unit › Responses

Allocated, or the unit this Idempotency-Key already allocated

The unit an allocate call handed out.
created
​boolean · required

true when if_none: create cold-started the unit: it is still starting.

replayed
​boolean · required

true when this Idempotency-Key had already allocated the unit.

​object · required

The unit, with its user_data and allocation (delivered_at is null until its agent has the files).


Start Service Unit

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/{key}/start

Starts a stopped unit, its labels and user_data replaced by the ones given (send {} to keep them). A running unit is left as it is (200). Checks the same limits and spend cap as a create that starts. code mode only (409 policy_not_code). A service principal with units.control on the service may call this. Console only.

Start Service Unit › path Parameters

id
​string · uuid · required · style: simple
key
​string · required · style: simple

The unit's key, GET .../units lists it.

The unit's key, GET .../units lists it.

project_id
​string · uuid · required · style: simple

Start Service Unit › Request Body

`POST .../units/{key}/start`: send `{}` to start the unit as it is.
​object | null

Replaces the unit's labels whole when given.

Default: null
user_data
​object | array | string | number | boolean | null

Replaces the unit's user_data whole when given.

Default: null

Start Service Unit › Responses

The unit

A unit as a control call left it.
created
​boolean · required

true only when this call made the unit.

​object · required

The unit, with its user_data.


Stop Service Unit

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/{key}/stop

Drains the unit, then stops it: it keeps its key, labels and user_data, gives its slot back, and costs nothing while stopped. A stopped unit is left as it is (200). Past the service's stopped-unit cap the answer is 409 stopped_unit_limit. code mode only (409 policy_not_code). A service principal with units.control on the service may call this. Console only.

Stop Service Unit › path Parameters

id
​string · uuid · required · style: simple
key
​string · required · style: simple

The unit's key, GET .../units lists it.

The unit's key, GET .../units lists it.

project_id
​string · uuid · required · style: simple

Stop Service Unit › Responses

The unit

A unit as a control call left it.
created
​boolean · required

true only when this call made the unit.

​object · required

The unit, with its user_data.


List Unit Webhooks

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks

The service's webhooks, oldest first: where its unit events are delivered, which types, and how delivery is going (state, the acknowledged cursor_seq, the current run of failures). Never a secret. People with manage access only. Console only.

List Unit Webhooks › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Unit Webhooks › Responses

The webhooks

​UnitWebhookResponse[] · required

Create Unit Webhook

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks

Delivers this service's unit events to url: ordered batches (at most 50 events or 256 KiB) POSTed as {webhook_id, delivery_id, events}, the next batch only after a 2xx within 10 seconds, at least once (deduplicate on seq). A new webhook starts with the events after now. url is https:// and a public host; a host that resolves to a private, loopback, link-local, shared or platform address is refused (address_not_allowed), and is checked again on every delivery; redirects are not followed. event_types empty means every type. At most 5 per service (too_many_webhooks).

The response carries the signing secret this once. Each POST has Zaroz-Signature: v1=<hex HMAC-SHA256(secret, "{Zaroz-Timestamp}.{raw body}")> (the whole secret string is the key), Zaroz-Timestamp (Unix seconds; refuse one more than 5 minutes off), Zaroz-Webhook-Id and Zaroz-Delivery-Id. Failed deliveries are retried after 5 s, 30 s, 2 min, 10 min, 30 min, 1 h and then hourly; after 72 hours of failure the webhook is disabled and the organization is notified. Console only.

Create Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Create Unit Webhook › Request Body

`POST .../units/webhooks`.
url
​string · required

https:// and a public host; at most 2048 characters, no user name or password.

event_types
​string[]

Event types to deliver (for example unit.ready); empty or absent for every type.

Default: []

Create Unit Webhook › Responses

Created; the secret is shown this once

A create or a rotation: the webhook and its new signing secret, shown this once.
secret
​string · required

whsec_ and 43 characters. Sign check: Zaroz-Signature carries v1=<hex HMAC-SHA256(secret, "{Zaroz-Timestamp}.{raw body}")>, with the whole secret string as the key.

​UnitWebhookResponse · required

A webhook, never its secret.


Get Unit Webhook

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}

One webhook of the service, never its secret. An id that is not one of this service's webhooks is a 404. Console only.

Get Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Get Unit Webhook › Responses

The webhook

A webhook, never its secret.
attempt
​integer · uint32 · min: 0 · required

Consecutive failed attempts of the current batch.

created_at
​string · date-time · required
cursor_seq
​integer · int64 · required

The last event seq the receiver acknowledged.

event_types
​string[] · required

Empty means every type.

id
​string · uuid · required
next_attempt_at
​string · date-time · required

When the next attempt is due.

state
​string · required

active, failing (retrying the same batch) or disabled.

updated_at
​string · date-time · required
url
​string · required
disabled_reason
​string | null

Why it is disabled: failing_72h. null unless disabled.

failing_since
​string | null · date-time

When the current run of failures began. null when the last attempt succeeded.

previous_secret_expires_at
​string | null · date-time

Until when the previous secret still signs beside the current one after a rotation; null when only one secret signs.


Delete Unit Webhook

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}

Stops delivering to this webhook and deletes it with its delivery log. Console only.

Delete Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Delete Unit Webhook › Responses

Deleted

No data returned

Update Unit Webhook

PATCH
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}

Changes the URL and/or the event types; an absent field is left as it is, event_types: [] means every type. The URL is refused for the same reasons as on create. Delivery continues from the same cursor. Console only.

Update Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Update Unit Webhook › Request Body

`PATCH .../units/webhooks/{webhook_id}`: an absent field is left as it is.
event_types
​array | null

Empty for every type.

url
​string | null

Update Unit Webhook › Responses

The webhook as stored

A webhook, never its secret.
attempt
​integer · uint32 · min: 0 · required

Consecutive failed attempts of the current batch.

created_at
​string · date-time · required
cursor_seq
​integer · int64 · required

The last event seq the receiver acknowledged.

event_types
​string[] · required

Empty means every type.

id
​string · uuid · required
next_attempt_at
​string · date-time · required

When the next attempt is due.

state
​string · required

active, failing (retrying the same batch) or disabled.

updated_at
​string · date-time · required
url
​string · required
disabled_reason
​string | null

Why it is disabled: failing_72h. null unless disabled.

failing_since
​string | null · date-time

When the current run of failures began. null when the last attempt succeeded.

previous_secret_expires_at
​string | null · date-time

Until when the previous secret still signs beside the current one after a rotation; null when only one secret signs.


Rotate Unit Webhook Secret

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}/rotate-secret

Mints a new signing secret, shown in this response only. For 24 hours (previous_secret_expires_at) every delivery is signed with both, the new one first (Zaroz-Signature: v1=<new>,v1=<old>), so the receiver can switch without missing a delivery. Rotating again within those 24 hours drops the oldest. Console only.

Rotate Unit Webhook Secret › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Rotate Unit Webhook Secret › Responses

Rotated; the new secret is shown this once

A create or a rotation: the webhook and its new signing secret, shown this once.
secret
​string · required

whsec_ and 43 characters. Sign check: Zaroz-Signature carries v1=<hex HMAC-SHA256(secret, "{Zaroz-Timestamp}.{raw body}")>, with the whole secret string as the key.

​UnitWebhookResponse · required

A webhook, never its secret.


Test Unit Webhook

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}/test

Sends one signed webhook.ping event now and answers with the attempt (status, latency, error, the first 4 KiB of the answer), which also goes into the delivery log. The ping's seq is the webhook's cursor_seq, an event the receiver already has. Works in every state, a disabled webhook included, and changes neither the cursor nor the retries. At most 6 a minute per webhook (429 beyond). Console only.

Test Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Test Unit Webhook › Responses

The attempt

One attempt in the delivery log.
attempt
​integer · uint32 · min: 0 · required

The attempt's number for this batch, from 1.

attempted_at
​string · date-time · required
duration_ms
​integer · uint32 · min: 0 · required
id
​string · uuid · required
kind
​string · required

events (a batch) or ping (a test).

error
​string | null

Why it failed: address_not_allowed, dns, connect, timeout, http_status, redirect_not_followed, invalid_url, transport. null for a 2xx.

from_seq
​integer | null · int64

The first and last event seq of a batch; null for a ping.

next_attempt_at
​string | null · date-time

When the batch is tried again; null unless a batch failed and will be retried.

response_excerpt
​string | null

The first 4 KiB of the receiver's answer.

status_code
​integer | null · uint16 · min: 0 · max: 65535

The receiver's status; null when it never answered.

to_seq
​integer | null · int64

Enable Unit Webhook

POST
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}/enable

Makes the webhook active and due at once, with no failures counted, from its cursor. If events after the cursor were already removed (they are kept 30 days), delivery resumes from the oldest kept one and the first batch opens with an events.gap event (data: {from_seq, to_seq}) naming what was missed. On a failing webhook it retries now. Console only.

Enable Unit Webhook › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

Enable Unit Webhook › Responses

The webhook

A webhook, never its secret.
attempt
​integer · uint32 · min: 0 · required

Consecutive failed attempts of the current batch.

created_at
​string · date-time · required
cursor_seq
​integer · int64 · required

The last event seq the receiver acknowledged.

event_types
​string[] · required

Empty means every type.

id
​string · uuid · required
next_attempt_at
​string · date-time · required

When the next attempt is due.

state
​string · required

active, failing (retrying the same batch) or disabled.

updated_at
​string · date-time · required
url
​string · required
disabled_reason
​string | null

Why it is disabled: failing_72h. null unless disabled.

failing_since
​string | null · date-time

When the current run of failures began. null when the last attempt succeeded.

previous_secret_expires_at
​string | null · date-time

Until when the previous secret still signs beside the current one after a rotation; null when only one secret signs.


List Unit Webhook Deliveries

GET
/api/v2/projects/{project_id}/service-instances/{id}/units/webhooks/{webhook_id}/deliveries

The webhook's delivery log, newest first, kept 30 days: each attempt's time, the event range it carried (null for a test ping), its number for that batch, the receiver's status or the error code, the latency, the first 4 KiB of the answer and, for a failed batch, when it is tried again. limit 1 to 100 (default 50); before pages back from the last attempted_at seen. Console only.

List Unit Webhook Deliveries › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
webhook_id
​string · required · style: simple

List Unit Webhook Deliveries › query Parameters

before
​string | null · date-time · style: form

Only attempts older than this (the last attempted_at of the previous page).

Only attempts older than this (the last attempted_at of the previous page).

limit
​integer | null · int64 · style: form

1 to 100. Default 50.

1 to 100. Default 50.

List Unit Webhook Deliveries › Responses

One page of the log


Get Service API Access

GET
/api/v2/projects/{project_id}/service-instances/{id}/principal

The service's principal (its own identity for code that calls the Zaroz API) and its grants on the project's services, plus copies_total, copies_with_key and copies (per live copy, carries_key): which copies already run the pod template that mounts the key, since a change applies as copies restart. 404 with no_principal when the service has none. People with manage access only. Console only.

Get Service API Access › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Get Service API Access › Responses

The principal and its grants

A service's principal: its id, when it was made, its grants, and how far the mounted key has reached the service's copies.
​PrincipalCopyResponse[] · required

Each live copy, lowest slot first.

copies_total
​integer · uint · min: 0 · required

How many live copies the service has.

copies_with_key
​integer · uint · min: 0 · required

How many of them carry the mounted key already.

created_at
​string · date-time · required
​GrantDto[] · required
id
​string · uuid · required
service_instance_id
​string · uuid · required

The service the principal is the identity of.


Enable Service API Access

PUT
/api/v2/projects/{project_id}/service-instances/{id}/principal

Lets code in this service use the Zaroz API: creates the service's principal, or returns the one it has (idempotent; 201 when created, 200 when it existed). The kernel mounts a short-lived token into every unit of the service at /zaroz/credentials/token and sets ZAROZ_API_URL; this applies as the units restart, by the service's rollout rule. A new principal holds no grant. Console only.

Enable Service API Access › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Enable Service API Access › Responses

The service already had one

A service's principal: its id, when it was made, its grants, and how far the mounted key has reached the service's copies.
​PrincipalCopyResponse[] · required

Each live copy, lowest slot first.

copies_total
​integer · uint · min: 0 · required

How many live copies the service has.

copies_with_key
​integer · uint · min: 0 · required

How many of them carry the mounted key already.

created_at
​string · date-time · required
​GrantDto[] · required
id
​string · uuid · required
service_instance_id
​string · uuid · required

The service the principal is the identity of.


Disable Service API Access

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/principal

Revokes the service's principal: every token it holds (mounted and external) stops working at once and the mounted token is removed from the service. Its grants go with it. A later enable creates a new principal with no grants. 404 with no_principal when the service has none. Console only.

Disable Service API Access › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Disable Service API Access › Responses

Revoked

No data returned

Replace Service API Grants

PUT
/api/v2/projects/{project_id}/service-instances/{id}/principal/grants

Replaces what the service's principal may do: each grant names a service of the same project (the service itself included) and units.read (read its units) or units.control (also start, stop and create them). You must be able to manage every service you name. The list replaces the old one whole; an empty list removes every grant; at most 64. A grant naming a service that is not in this project, that you cannot manage or that is no longer live is refused with grant_target_unavailable and nothing changes. 404 with no_principal when the service has no API access. Takes effect on the program's next request. Console only.

Replace Service API Grants › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Replace Service API Grants › Request Body

`PUT .../principal/grants`: the whole grant list, replacing the old one. An empty list removes every grant. Duplicates are kept once.
​GrantDto[] · required

Replace Service API Grants › Responses

The principal with its new grants

A service's principal: its id, when it was made, its grants, and how far the mounted key has reached the service's copies.
​PrincipalCopyResponse[] · required

Each live copy, lowest slot first.

copies_total
​integer · uint · min: 0 · required

How many live copies the service has.

copies_with_key
​integer · uint · min: 0 · required

How many of them carry the mounted key already.

created_at
​string · date-time · required
​GrantDto[] · required
id
​string · uuid · required
service_instance_id
​string · uuid · required

The service the principal is the identity of.


List Grantable Services

GET
/api/v2/projects/{project_id}/service-instances/{id}/principal/grantable-services

The services of this project the caller may name in a grant of PUT .../principal/grants: the ones they can manage that are live, this service included. A service missing here would be refused with grant_target_unavailable, so the editor can offer only these. Works before the service has API access. People with manage access only. Console only.

List Grantable Services › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Grantable Services › Responses

The services that may be granted

`GET .../principal/grantable-services`: the services of the project the caller may name in a grant, by the rule `PUT .../principal/grants` enforces (the project's own services that the caller can manage and that are live, the service itself included). A service not listed would be refused with `grant_target_unavailable`.

List Service API Credentials

GET
/api/v2/projects/{project_id}/service-instances/{id}/principal/credentials

The external credentials of the service's principal, for code running outside Zaroz: name, who made it (created_by, created_by_name), expiry and last use, newest first; revoked ones are not listed, expired ones are. Never the secret. 404 with no_principal when the service has no API access. Console only.

List Service API Credentials › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

List Service API Credentials › Responses

The credentials

`GET .../principal/credentials`: the live (not revoked) external credentials, newest first, expired ones included.
​CredentialResponse[] · required

Create Service API Credential

POST
/api/v2/projects/{project_id}/service-instances/{id}/principal/credentials

Mints an external credential for the service's principal, for code running outside Zaroz (a matchmaker elsewhere, CI). It acts with the principal's grants. name is 1 to 64 characters; expires_in_days is 1 to 365 and defaults to 90. The token is in this response only and cannot be read again. 404 with no_principal when the service has no API access. Console only.

Create Service API Credential › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple

Create Service API Credential › Request Body

`POST .../principal/credentials`.
name
​string · required

1 to 64 characters, so the credential can be told apart when revoking it.

expires_in_days
​integer | null · int64

1 to 365; 90 when omitted.

Default: null

Create Service API Credential › Responses

Created; the token is shown this once

`POST .../principal/credentials`: the credential and, this once, its token.
​CredentialResponse · required

An external credential, without its secret.

token
​string · required

zaroz_sp_.... Shown once: it is stored only as a digest and cannot be read back. Send it as Authorization: Bearer <token>.


Revoke Service API Credential

DELETE
/api/v2/projects/{project_id}/service-instances/{id}/principal/credentials/{credential_id}

Revokes one external credential; it stops working at once. An id that is not a live credential of this service's principal (another service's, the mounted token's, or an invented one) is a 404, the same answer either way. Console only.

Revoke Service API Credential › path Parameters

id
​string · uuid · required · style: simple
project_id
​string · uuid · required · style: simple
credential_id
​string · uuid · required · style: simple

Revoke Service API Credential › Responses

Revoked

No data returned

Quote Wallet Credit For Subscription

GET
/api/v2/subscriptions/{id}/wallet-credit-quote

Quotes converting the unused time of this subscription into wallet credit. Read-only: nothing is written and the quote may be requested as often as the cancel screen refreshes. The answer is signed and valid for ten minutes. eligible: false says why in reason; below_minimum: true means the credit is under minimum_cents and redeeming would write nothing. Requires the same authority as cancelling the subscription, including the staff door.

Quote Wallet Credit For Subscription › path Parameters

id
​string · uuid · required · style: simple

Quote Wallet Credit For Subscription › Responses

The signed quote

A signed, ten-minute quote for converting the unused time of a subscription into wallet credit (spec 13.2, F24). Issuing it writes nothing.
as_of
​string · date-time · required

The instant every figure below was computed at; redemption recomputes at exactly this instant.

below_minimum
​boolean · required

True when total_cents is under minimum_cents: the conversion is not offered and redeeming this quote writes nothing.

currency
​string · required
eligible
​boolean · required
expires_at
​string · date-time · required
​WalletCreditQuoteLineResponse[] · required
minimum_cents
​integer · int64 · required
period_remaining_seconds
​integer · int64 · required
period_total_seconds
​integer · int64 · required
quote_id
​string · required

Opaque and signed. Send it back unchanged to POST /subscriptions/{id}/cancel-with-wallet-credit; an altered, foreign or expired one is refused.

total_cents
​integer · int64 · required

What the wallet would be credited, in minor units of currency.

reason
​string | null

Why the subscription is not eligible, when eligible is false: one of subscription_not_active, policy_not_prorated_to_wallet, disputed_or_charged_back_invoice, bundle_member.


Cancel Subscription With Wallet Credit

POST
/api/v2/subscriptions/{id}/cancel-with-wallet-credit

Pays the customer. Redeems a wallet-credit quote: cancels the subscription now and credits the wallet with the unused time, recomputed at the quote's own instant under the subscription, invoice and wallet locks, all in one transaction (any failure writes nothing). Refused with 422 for an invalid quote (altered, forged, or issued for another subscription), an ineligible subscription (which includes a quote already redeemed, because the subscription is then no longer active), or a credit under the minimum; refused with 409 for an expired quote, a credit that changed since the quote (request a new one), or a credit that needs staff to issue. Requires the same authority as cancelling the subscription (a restricted API token needs subscriptions.cancel), including the staff door, which is audited as staff.

Cancel Subscription With Wallet Credit › path Parameters

id
​string · uuid · required · style: simple

Cancel Subscription With Wallet Credit › Request Body

quote_id
​string · required

The quote_id of a quote issued for this subscription.

Cancel Subscription With Wallet Credit › Responses

The subscription was cancelled and the wallet credited

The committed conversion.
cancels_at
​string · date-time · required

When the subscription stops: the instant of this request.

credit_note_ids
​string[] · required

One credit note per credited invoice.

status
​string · enum · required

pending_termination: the subscription now ends and the reaper tears the service down on its next tick.

Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed
subscription_id
​string · uuid · required
total_credited_cents
​integer · int64 · required

The amount credited to the wallet, in minor units.


List Service Instance Deployments

GET
/api/v2/service-instances/{id}/deployments

This service's deployment history, newest first, and the current state of the workloads a deployment acts on. number is what a customer cites in a support ticket: unique within the service and never reused, but deliberately NOT contiguous, because it is drawn from the same per-service sequence every operation uses. status says how the operation ended and never whether the rollout worked -- nothing in the platform records a per-deployment outcome, so succeeded and failed do not exist here rather than being guessed. abandoned means the process holding the deployment died part-way; interrupted means an operator forced it open. actor is masked: a teammate is named by user id, while an operator and the platform are deliberately anonymous. workloads answers "is my new version serving" and is a statement about now rather than about any one deployment; it is empty for a service that has never been provisioned, and also when the resources could not be read, which degrades the banner rather than the panel. activeDeploymentId is kernel's answer to "is one running right now", read from this service's live operations rather than from the page above, so it is the same answer on every page and a client must not recompute it by scanning data. There are no phases and no build log, because nothing in kernel clones or builds a repository. 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

List Service Instance Deployments › path Parameters

id
​string · uuid · required · style: simple

List Service Instance Deployments › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20

List Service Instance Deployments › Responses

The deployment history and the current state of this service's workloads

The whole panel: the history, and what is running now.
​DeploymentResponse[] · required

Newest first.

​PaginationMeta · required

Pagination metadata included in paginated responses

​WorkloadRolloutResponse[] · required

The instance's workloads and their current state. Empty for a service that has never been provisioned, which is an ordinary state for one that has been paid for and not yet deployed.

active_deployment_id
​string | null · uuid

The deployment happening right now, or null when nothing is.

Kernel's verdict, not a scan of data (R2, 13-deployments.md §6). It is read from the leases live on the instance rather than from the page above, so it is the same answer on page one and on page nine; a client that recomputed it by looking for a running row would report "nothing deploying" for any customer who paged back through their history.

It may name a deployment that is not in data at all, and that is correct rather than a gap: the running one is always the newest, so it is on page one, and a client on a later page should still show the banner.


Start Service Instance Deployment

POST
/api/v2/service-instances/{id}/deployments

Puts this service's currently saved configuration into service, and records the attempt. It takes no body, deliberately: a deployment ships the source as it is already stored, so there is nowhere in this request to put configuration and no way to deploy something that was not saved first (00-program.md §8). There is no forceRebuild flag either, and it is absent rather than accepted-and-ignored, because nothing in kernel builds an image and a flag that skips a cache nobody has is a promise. Refused with a business-rule error when the service has never been told what to run, which is the state the panel renders by pointing at Source. Takes a deploy lease, so it is refused with 409 while a restore, a resize or another deployment holds this service, and the refusal names that operation and when it started. Two deployments of one service can never overlap. A rollout that cannot be applied is reported as a failure with a fixed sentence: the underlying cluster or template message is logged rather than returned, because it quotes registry hostnames and internal paths. The response's failureReason carries kernel's own closed-vocabulary cause for the same failure (never the raw message), so a client can show more than a generic retry hint without kernel ever repeating an internal hostname or path back to the caller. The response is the deployment as it now stands, with the number a customer cites in a support ticket.

Start Service Instance Deployment › path Parameters

id
​string · uuid · required · style: simple

Start Service Instance Deployment › Responses

The deployment that was started, with its number

One attempt to put a version of this service into service. The wire form of [`Deployment`](kernel_core::domain::service_operations::deployments::Deployment), which exists separately only because `impl_uuid_id!` derives no `JsonSchema` and a typed id therefore cannot appear in an OpenAPI schema. Every field is copied verbatim; there is no computation here and no field the domain type does not already carry.
​required

Who started it, masked. A customer is named by their user id; an operator and the platform are deliberately anonymous.

id
​string · uuid · required
number
​integer · int64 · required

The number a customer cites in a support ticket.

Unique within this service and never reused, because the lease rows it comes from are never deleted. Not contiguous: it is drawn from the same per-instance sequence every operation on the service uses, so a service's deployments are #3, #7, #12. A panel renders it as an identifier, never as a count.

started_at
​string · date-time · required
​required

How the operation ended. Not whether it worked: see this module's header.

​required

Why it ran. A closed union with two arms: manual, somebody pressed Deploy, and config_change, a setting was written and the workload was re-rendered because of it. The three the fixture also carries (push, tag_moved, rollback) each need a subsystem kernel does not have, and are absent rather than declared and unreachable.

config_change carries a summary: kernel's own sentence about what changed, for a client to print verbatim (R2). Do not parse it and do not compose your own -- only kernel knows which key was written -- and note that its wording is prose rather than a compatibility surface.

duration_seconds
​integer | null · int64

Whole seconds, null whenever finishedAt is. Computed by kernel (R2) from the two fields beside it, so it can never disagree with them.

​

Why the rollout failed, when outcome is failed. Kernel's own closed vocabulary -- never the raw cluster or template message, which is logged rather than returned (ZC-1084).

null here is not the same statement null on outcome is. It is null for every deployment that is not a recorded failure, for a ConfigChange-kind deployment (not yet classified), and for a lease released before this field existed. None of those is the unknown word, which is a positive statement that kernel tried to classify and could not. See DeploymentFailureReason's own doc for the full vocabulary and why each word is there.

finished_at
​string | null · date-time

null while it is still running, and also for one that was abandoned: the row records when the lease would have lapsed, not when the holder stopped, and reporting the expiry would invent a duration.

image_digest
​string | null

The immutable digest that was pulled, when the reference pinned one.

Almost always null, and that is honest rather than missing. Nothing in kernel resolves a tag to a digest against a third-party registry: both manifest clients authenticate HTTP basic only, while ghcr.io, Docker Hub and Quay all require a Bearer-token exchange. It is non-null only where the customer pinned a digest themselves. A client must not derive one from imageReference.

image_reference
​string | null

The image this deployment shipped, as ${service.image} resolved when the deployment started.

A fact about the past, not about now. It is captured when the deployment begins, because that is the only moment it exists: the value lives on the instance's configuration and the customer overwrites it on their next tag change. A client comparing this with the Source capability's current reference is comparing "what is running" with "what is saved", which is the question 00-program.md §8 is about.

null for a deployment from before the provenance migration and for a product whose image the customer does not choose. It is not reconstructable, so null here means the answer is gone rather than that it is available from another route.

​

Whether the rollout applied: succeeded, failed, or null.

null is "not recorded", never a failure. It is every deployment from before the provenance migration, every one still running, and every holder that died or was forced open before it could say. Nothing is backfilled and nothing can be, so a panel renders null as an absence rather than as a red badge.

Separate from status on purpose: a failed deployment is ended and failed at once, because the handler releases its lease on its error path too.


Get Service Instance Deployment

GET
/api/v2/service-instances/{id}/deployments/{deployment_id}

One deployment of this service, by id: the same row, with the same fields, that listServiceInstanceDeployments returns for it, so a panel can link to a deployment and read it back without paging the history. status says how the operation ended and outcome whether the rollout applied, and outcome is null when nothing recorded one, which is never a failure. failureReason is kernel's own closed-vocabulary cause for a failed Deploy-kind deployment (image reference invalid, hostname taken, cluster rejected the workload, and so on) -- never the raw error, which is still logged rather than returned, because it quotes registry hostnames and internal paths. failureReason is null whenever outcome is not failed, and also for a ConfigChange-kind deployment, which does not yet classify its failures. There are no phases and no build log: nothing in kernel clones or builds a repository. 404 for a deployment id that names nothing, one that belongs to a different service, and an operation on this service that is not a deployment, all identically; and 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

Get Service Instance Deployment › path Parameters

deployment_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Get Service Instance Deployment › Responses

The deployment, as the history lists it

One attempt to put a version of this service into service. The wire form of [`Deployment`](kernel_core::domain::service_operations::deployments::Deployment), which exists separately only because `impl_uuid_id!` derives no `JsonSchema` and a typed id therefore cannot appear in an OpenAPI schema. Every field is copied verbatim; there is no computation here and no field the domain type does not already carry.
​required

Who started it, masked. A customer is named by their user id; an operator and the platform are deliberately anonymous.

id
​string · uuid · required
number
​integer · int64 · required

The number a customer cites in a support ticket.

Unique within this service and never reused, because the lease rows it comes from are never deleted. Not contiguous: it is drawn from the same per-instance sequence every operation on the service uses, so a service's deployments are #3, #7, #12. A panel renders it as an identifier, never as a count.

started_at
​string · date-time · required
​required

How the operation ended. Not whether it worked: see this module's header.

​required

Why it ran. A closed union with two arms: manual, somebody pressed Deploy, and config_change, a setting was written and the workload was re-rendered because of it. The three the fixture also carries (push, tag_moved, rollback) each need a subsystem kernel does not have, and are absent rather than declared and unreachable.

config_change carries a summary: kernel's own sentence about what changed, for a client to print verbatim (R2). Do not parse it and do not compose your own -- only kernel knows which key was written -- and note that its wording is prose rather than a compatibility surface.

duration_seconds
​integer | null · int64

Whole seconds, null whenever finishedAt is. Computed by kernel (R2) from the two fields beside it, so it can never disagree with them.

​

Why the rollout failed, when outcome is failed. Kernel's own closed vocabulary -- never the raw cluster or template message, which is logged rather than returned (ZC-1084).

null here is not the same statement null on outcome is. It is null for every deployment that is not a recorded failure, for a ConfigChange-kind deployment (not yet classified), and for a lease released before this field existed. None of those is the unknown word, which is a positive statement that kernel tried to classify and could not. See DeploymentFailureReason's own doc for the full vocabulary and why each word is there.

finished_at
​string | null · date-time

null while it is still running, and also for one that was abandoned: the row records when the lease would have lapsed, not when the holder stopped, and reporting the expiry would invent a duration.

image_digest
​string | null

The immutable digest that was pulled, when the reference pinned one.

Almost always null, and that is honest rather than missing. Nothing in kernel resolves a tag to a digest against a third-party registry: both manifest clients authenticate HTTP basic only, while ghcr.io, Docker Hub and Quay all require a Bearer-token exchange. It is non-null only where the customer pinned a digest themselves. A client must not derive one from imageReference.

image_reference
​string | null

The image this deployment shipped, as ${service.image} resolved when the deployment started.

A fact about the past, not about now. It is captured when the deployment begins, because that is the only moment it exists: the value lives on the instance's configuration and the customer overwrites it on their next tag change. A client comparing this with the Source capability's current reference is comparing "what is running" with "what is saved", which is the question 00-program.md §8 is about.

null for a deployment from before the provenance migration and for a product whose image the customer does not choose. It is not reconstructable, so null here means the answer is gone rather than that it is available from another route.

​

Whether the rollout applied: succeeded, failed, or null.

null is "not recorded", never a failure. It is every deployment from before the provenance migration, every one still running, and every holder that died or was forced open before it could say. Nothing is backfilled and nothing can be, so a panel renders null as an absence rather than as a red badge.

Separate from status on purpose: a failed deployment is ended and failed at once, because the handler releases its lease on its error path too.


Get Service Instance Environment

GET
/api/v2/service-instances/{id}/environment

Lists this service's environment variables. A secret's value is always null: the browser never holds a secret's value, and null rather than a placeholder is the only unambiguous answer, because "[redacted]" is a string a customer can also type. Use revealServiceInstanceEnvironmentSecret to read one back, which is recorded. applies is kernel's verdict about when a change reaches the running container, not the client's guess, and it is restart for every variable today because environment renders into the pod template and there is no delivery path by which a running container picks up a change without a new pod. pending says whether the running container is still on older values, compared against what was last applied to the cluster rather than against local state, so a page reload does not make the banner disappear. 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

Get Service Instance Environment › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Environment › Responses

The variables, with every secret's value masked

The variables half of the panel, plus the two flags that belong to the whole of it.
secrets_allowed
​boolean · required

Whether this product's customers may store secrets at all. A panel that inferred this from a product slug would be computing a verdict.

​EnvironmentVariableResponse[] · required

Ordered by key, so two reads of an unchanged service agree.

​

Whether the running container is still on older values, and which act would pick them up. null when nothing is outstanding.

On the envelope rather than left to the client's local state, because a page reload must not make the banner disappear while the container is still running the old values.


Set Service Instance Environment Variable

PUT
/api/v2/service-instances/{id}/environment/{key}

Creates or replaces one environment variable, keyed on the path segment. One row at a time rather than a whole-array replace: once secrets are masked there is nothing for a client to send back for the rows it did not touch, so an array write would delete every secret it failed to re-supply. Omitting value (or sending null) means keep the stored value, and is accepted only for a secret that is already stored and is staying secret, since those are the only rows the client was never shown; anywhere else it is a 400 rather than a silently stored empty string. Moving a variable between plain and secret is this same request with a different secret, and the variable moves rather than being duplicated. ZAROZ_AUTH_TOKEN is reserved by the platform. Refused with a business-rule error when the product does not allow secrets. Takes a configuration-change lease, so it is refused with 409 while another operation holds this service, and re-renders the workload on success. A successful write appears in this service's deployment history as a config_change deployment, with a number a customer can cite and a summary naming this key. The response carries the row's own applies, so a client stops assuming the safe answer. pending says whether the re-render actually happened: saving and applying are separate acts, the commonest reason the second does not follow the first is a subscription that is not active yet, and a panel that reported the save as done would tell a customer rotating a leaked credential that the old value is gone while it is still live in the running container. The audit entry names the key and never the value.

Set Service Instance Environment Variable › path Parameters

key
​string · required · style: simple
id
​string · uuid · required · style: simple

Set Service Instance Environment Variable › Request Body

Creates or replaces one variable. The key is the path segment, not a field: a body that could disagree with the URL about which row is being written is a body with two answers.
secret
​boolean · required

Moving a variable between plain and secret is this same request with a different value here.

value
​string | null

Absent or null means keep the stored value, and is accepted only for a secret that is already stored and is staying secret: those are the only rows the client was never shown. Anywhere else it is a 400, never an empty string.

Default: null

Set Service Instance Environment Variable › Responses

The stored variable, with a secret's value still masked

One environment variable.
​required

When changing this reaches the running container. Kernel's verdict.

id
​string · required

Stable for as long as the key is.

Derived from the key rather than stored, so renaming a variable changes its id. Good enough to key a table row and an open dialog on; not durable, and nothing should persist it.

key
​string · required
secret
​boolean · required
​

Whether the running container is still on an older value after the request that produced this row, and which act would pick the new one up. null means nothing is outstanding.

Only a write answers this. In a list read every row carries null and [EnvironmentResponse::pending] on the envelope is the verdict for the set, because staleness is compared against the whole rendered workload rather than row by row. A write has no envelope to put it on and does know the answer for its own change: saving succeeded but the re-render did not, which is the case a panel must not report as done.

value
​string | null

null when secret is true, always.

Not "[redacted]": that is a string a customer can also type, so a client could not tell a masked value from a real one, and null is the only unambiguous answer.


Delete Service Instance Environment Variable

DELETE
/api/v2/service-instances/{id}/environment/{key}

Deletes one environment variable, plain or secret. A key that is not stored is a 404 on the row rather than a silent success, so a client cannot show a customer a change that did not happen. Takes a configuration-change lease and re-renders the workload, exactly as the write does, so it appears in this service's deployment history as a config_change deployment whose summary names the key that was removed. Returns the whole remaining set, because there is no row left to return. The audit entry names the key.

Delete Service Instance Environment Variable › path Parameters

key
​string · required · style: simple
id
​string · uuid · required · style: simple

Delete Service Instance Environment Variable › Responses

What remains, with every secret's value masked

The variables half of the panel, plus the two flags that belong to the whole of it.
secrets_allowed
​boolean · required

Whether this product's customers may store secrets at all. A panel that inferred this from a product slug would be computing a verdict.

​EnvironmentVariableResponse[] · required

Ordered by key, so two reads of an unchanged service agree.

​

Whether the running container is still on older values, and which act would pick them up. null when nothing is outstanding.

On the envelope rather than left to the client's local state, because a page reload must not make the banner disappear while the container is still running the old values.


Reveal Service Instance Environment Secret

POST
/api/v2/service-instances/{id}/environment/{key}/reveal

Returns one secret variable's plaintext, once, and records that it was read. Its own request rather than a query parameter on the list, so that the ordinary read stays structurally incapable of leaking a value and so that the audit entry covers exactly the act that needs it. A POST rather than a GET for the same reason: the entry is written before the value is handed over, and the key never lands in an access log alongside a cacheable response. Gated on the same permission as changing the service, not on the permission to see it: an ordinary member may list a service's variables and may not read its credentials back. 400 for a variable that is not a secret; 404 for one that is not stored.

Reveal Service Instance Environment Secret › path Parameters

key
​string · required · style: simple
id
​string · uuid · required · style: simple

Reveal Service Instance Environment Secret › Responses

The secret's plaintext

One secret's plaintext, returned once.
key
​string · required
value
​string · required

Get Service Instance Runtime Config

GET
/api/v2/service-instances/{id}/runtime-config

How this service's container is started. entrypoint and command are the two the platform actually renders; command is one string, tokenised by kernel, because a client splitting a command line would be computing a value. null means the image's own declaration is used, and is a different answer from an empty string. imageDefaultsAvailable reports whether kernel could actually ask the image's registry this time: true and a null imageDefault means the image declares nothing there; false means the registry could not be asked (an unreachable host, an address kernel refuses to dial, or a registry -- Docker Hub and GHCR among them -- that demands a bearer-token exchange this inspector does not perform), and every imageDefault is null regardless of what the image actually declares, because reporting "the image declares nothing" when the truth is "the registry was never asked" would be a lie told in the customer's own words. workingDirectory's imageDefault is populated the same way even though its value stays refused on write below. stopSignal, stopGracePeriodSeconds, preDeployCommand and postDeployCommand are reported as unset and refused on write, because none of them has a delivery mechanism today and storing a value that changes nothing would make this panel state a falsehood: nothing renders a container workingDir override; the kubelet always sends SIGTERM and STOPSIGNAL is a property of the image rather than something a pod specification can override, which is why stopSignals is empty; the termination grace period is hardcoded to 60 seconds; and there is no post-rollout mechanism at all.

Get Service Instance Runtime Config › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Runtime Config › Responses

The startup overrides, beside what the image declares

How the process is started and stopped, and what runs around a rollout. Five of these fields are reported and refused rather than stored; see [`UpdateRuntimeConfigRequest`] and the route's own description for which and why.
​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​required

Kernel's verdict for the deploy commands, which is genuinely different: a pre-deploy command cannot be picked up by a restart, because a restart is not a deployment.

image_defaults_available
​boolean · required

False when the registry could not be asked at all.

​required

Kernel's verdict for the startup fields, which share one.

stop_signals
​string[] · required

The signals a customer may choose. Closed, because the platform decides, and supplied rather than hardcoded in the client for the same reason a region is never free text.

Empty today, and that is the honest answer rather than an oversight: there is no signal the platform can deliver but SIGTERM. A panel renders no options and therefore offers no choice, which is exactly the state of the world.

​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​

Whether the running container is still started the old way, and which act would pick the new configuration up. null when nothing is outstanding.

Same meaning and same reason as [EnvironmentVariableResponse::pending]: a save that could not be re-rendered is still a save, and the panel has to be able to say so.

post_deploy_command
​string | null
pre_deploy_command
​string | null
stop_grace_period_seconds
​integer | null · uint32 · min: 0

Always null today, meaning the platform's own 60 seconds.

stop_signal
​string | null

Always null: the kubelet always sends SIGTERM and a pod spec cannot override an image's STOPSIGNAL.


Update Service Instance Runtime Config

PUT
/api/v2/service-instances/{id}/runtime-config

Replaces the whole startup configuration. One PUT rather than a field at a time, because unlike the variables list this genuinely is one form with one save button and no per-row identity. null means follow the image's own declaration; an empty string is refused rather than collapsed into it, since "use the image's" and "override with nothing" are different instructions and only the first is something a container can be given. command is one string and is tokenised here, honouring single quotes, double quotes and backslash escapes; it is deliberately not wrapped in a shell, because inserting /bin/sh -c would put a process between the kubelet and the customer's program and change what receives SIGTERM. workingDirectory, stopSignal, stopGracePeriodSeconds, preDeployCommand and postDeployCommand are refused with the reason, because none of them has a delivery mechanism today and storing a value that changes nothing would make the panel state a falsehood in the customer's own words. Takes a configuration-change lease, so it is refused with 409 while another operation holds this service, and re-renders the workload on success, reporting through pending when that re-render did not happen. It appears in this service's deployment history as a config_change deployment; the summary says the run settings changed and quotes neither the entrypoint nor the command, both of which routinely carry credentials.

Update Service Instance Runtime Config › path Parameters

id
​string · uuid · required · style: simple

Update Service Instance Runtime Config › Request Body

One save for the whole runtime object, because unlike the variables list this genuinely is one form with one save button and no per-row identity. `null` means "follow the image" and is a different value from `""`, which means "override with nothing". The two must never be conflated: one asks the container to use its own declaration, the other asks it to use none.
command
​string | null

One string, tokenised by kernel. A client splitting a command line would be a component computing a value (R2).

Default: null
entrypoint
​string | null
Default: null
post_deploy_command
​string | null

Refused when not null; there is no post-rollout mechanism at all.

Default: null
pre_deploy_command
​string | null

Refused when not null; nothing renders an init container from this.

Default: null
stop_grace_period_seconds
​string | null

The text that was typed, unparsed, so a browser that rounds or clamps cannot decide a value the scheduler owns. Refused when not null; the grace period is hardcoded in the Kubernetes renderer today.

Default: null
stop_signal
​string | null

Refused when not null; see [RuntimeConfigResponse::stop_signals].

Default: null
working_directory
​string | null

Refused when not null; nothing renders a workingDir today.

Default: null

Update Service Instance Runtime Config › Responses

The stored startup configuration

How the process is started and stopped, and what runs around a rollout. Five of these fields are reported and refused rather than stored; see [`UpdateRuntimeConfigRequest`] and the route's own description for which and why.
​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​required

Kernel's verdict for the deploy commands, which is genuinely different: a pre-deploy command cannot be picked up by a restart, because a restart is not a deployment.

image_defaults_available
​boolean · required

False when the registry could not be asked at all.

​required

Kernel's verdict for the startup fields, which share one.

stop_signals
​string[] · required

The signals a customer may choose. Closed, because the platform decides, and supplied rather than hardcoded in the client for the same reason a region is never free text.

Empty today, and that is the honest answer rather than an oversight: there is no signal the platform can deliver but SIGTERM. A panel renders no options and therefore offers no choice, which is exactly the state of the world.

​RuntimeOverrideResponse · required

One startup value beside the one the image itself declares.

​

Whether the running container is still started the old way, and which act would pick the new configuration up. null when nothing is outstanding.

Same meaning and same reason as [EnvironmentVariableResponse::pending]: a save that could not be re-rendered is still a save, and the panel has to be able to say so.

post_deploy_command
​string | null
pre_deploy_command
​string | null
stop_grace_period_seconds
​integer | null · uint32 · min: 0

Always null today, meaning the platform's own 60 seconds.

stop_signal
​string | null

Always null: the kubelet always sends SIGTERM and a pod spec cannot override an image's STOPSIGNAL.


Get Process Tree

GET
/api/v2/service-instances/{id}/resources/{resource_id}/processes

Returns the live process tree (pid/ppid, %CPU, memory, uptime) inside a workload's container, from the terminal-bridge agent. Past the guard it always answers 200: status distinguishes a healthy snapshot (ok) from the workload being offline (not_running), the bridge being too old to support the call (unsupported), or a transient bridge failure (unavailable), so the page can explain instead of erroring.

Get Process Tree › path Parameters

id
​string · uuid · required · style: simple
resource_id
​string · uuid · required · style: simple

The workload whose processes are wanted.

The workload whose processes are wanted.

Get Process Tree › Responses

​ProcessInfoResponse[] · required
​ProcessTreeStatusResponse · required

Always a 200 with a discriminated status: a tree being unavailable is an expected outcome the page renders an explanation for, not an HTTP error.

truncated
​boolean · required
root_pid
​integer | null · uint32 · min: 0

List Service Instance Runs

GET
/api/v2/service-instances/{id}/runs

This service's run history, newest first by when the run was due (a manual trigger, which has no due time, sorts as the most recent activity rather than to the bottom). A row exists for every DECIDED run, including one that never executed: outcome names skipped_overlap, skipped_condition or missed for those, with a skipReason explaining why. outcome is null exactly while a fired run is in flight -- decided, not yet concluded -- never a seventh status string. startedAt is when the pod started (its gap from scheduledFor is the queue delay) and finishedAt, exitCode and terminationReason (OOMKilled, DeadlineExceeded, Evicted, ...) are filled in when the run ends. Resource consumption (peakCpuMillicores, peakMemoryBytes) and captured logs are not recorded yet. 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

List Service Instance Runs › path Parameters

id
​string · uuid · required · style: simple

List Service Instance Runs › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20

List Service Instance Runs › Responses

This instance's run history

The run history. Newest first, matching `ServiceRunRepository::list_by_subscription`'s own ordering.
​RunResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Trigger Service Instance Run

POST
/api/v2/service-instances/{id}/runs

Runs the service now: records a manual run (trigger.kind: "manual", naming the caller) that the kernel's scheduler launches as a Job within about ten seconds. A 200 means the run is queued, not that the container started: poll getServiceInstanceRun until startedAt and then outcome are set. Refused (422) while the subscription is not entitled to run now, such as when suspended, and (429) once the service already has five runs in progress. Takes no body and no operation lease.

Trigger Service Instance Run › path Parameters

id
​string · uuid · required · style: simple

Trigger Service Instance Run › Responses

The queued run

One run row.
created_at
​string · date-time · required
id
​string · uuid · required
​RunTriggerResponse · required

Who or what caused a run to be decided.

exit_code
​integer | null · int32
finished_at
​string | null · date-time

null until the run concludes, and always null for a skip.

log_object_key
​string | null

A pointer to this run's captured log, if one was captured. null today: log capture is a later slice.

​

null exactly while a fired run is in flight: decided, not yet concluded. See this module's header -- do not treat this as a status string with a seventh "running" value.

peak_cpu_millicores
​integer | null · int64
peak_memory_bytes
​integer | null · int64
scheduled_for
​string | null · date-time

When this run was due. null for a manual trigger, which has no due time by construction.

skip_reason
​string | null

Why a run was skipped or missed. null for one that fired.

​
started_at
​string | null · date-time

null for a run that was skipped, or one that has not started yet.

termination_reason
​string | null

The Kubernetes termination reason, e.g. OOMKilled. null unless the run concluded abnormally.


Get Service Instance Run

GET
/api/v2/service-instances/{id}/runs/{run_id}

Reads one run. A run_id naming a run that does not exist, or one belonging to a different service instance, is a 404 -- the same answer either way, so this route cannot be used to probe for another tenant's run ids.

Get Service Instance Run › path Parameters

run_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Get Service Instance Run › Responses

The run

One run row.
created_at
​string · date-time · required
id
​string · uuid · required
​RunTriggerResponse · required

Who or what caused a run to be decided.

exit_code
​integer | null · int32
finished_at
​string | null · date-time

null until the run concludes, and always null for a skip.

log_object_key
​string | null

A pointer to this run's captured log, if one was captured. null today: log capture is a later slice.

​

null exactly while a fired run is in flight: decided, not yet concluded. See this module's header -- do not treat this as a status string with a seventh "running" value.

peak_cpu_millicores
​integer | null · int64
peak_memory_bytes
​integer | null · int64
scheduled_for
​string | null · date-time

When this run was due. null for a manual trigger, which has no due time by construction.

skip_reason
​string | null

Why a run was skipped or missed. null for one that fired.

​
started_at
​string | null · date-time

null for a run that was skipped, or one that has not started yet.

termination_reason
​string | null

The Kubernetes termination reason, e.g. OOMKilled. null unless the run concluded abnormally.


List Service Instance Schedules

GET
/api/v2/service-instances/{id}/schedules

Lists this service's cron schedules. A subscription may own many, each with its own cron expression, timezone, overlap policy, timeout and guard conditions. The kernel's scheduler fires them (Kubernetes only executes), so each answers nextFireAt and upcomingFireTimes (UTC; empty while paused), computed by the same code that fires them. Whether a schedule has ever fired is in listServiceInstanceRuns. 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

List Service Instance Schedules › path Parameters

id
​string · uuid · required · style: simple

List Service Instance Schedules › query Parameters

page
​integer · uint32 · min: 0 · style: form

Page number (1-indexed). Defaults to 1.

Page number (1-indexed). Defaults to 1.

Default: 1
page_size
​integer · uint32 · min: 0 · style: form

Number of items per page. Defaults to 20, max 100.

Number of items per page. Defaults to 20, max 100.

Default: 20

List Service Instance Schedules › Responses

This instance's schedules

​ScheduleResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses


Create Service Instance Schedule

POST
/api/v2/service-instances/{id}/schedules

Creates one cron schedule on this service instance. Does not deploy or reconfigure anything: this saves a row the kernel's scheduler reads within ten seconds, and takes no operation lease. cronExpression is a standard five-field expression (minute hour day-of-month month day-of-week; no seconds, no @daily nicknames) and timezone an IANA name such as Europe/Helsinki; both are parsed by the scheduler's own parser, so one that saves is one that fires. A service owns at most twenty schedules. timeoutSeconds must be positive and is capped at an absolute ceiling (currently six hours) so a customer cannot author a container that Kubernetes will never terminate on its own -- a per-plan cap will replace this ceiling later. Every httpCheck condition's URL is resolved and refused if it points at a private, loopback, or otherwise non-public address, since kernel itself will eventually fetch it. Every jobSucceededSince condition's scheduleId must name one of this instance's own schedules; one that does not exist and one belonging to another tenant are refused identically, so this field cannot be used to probe for another organization's schedule ids. action is what each fire does and is required: run_job starts the service's job and is accepted only by a cron-job service; tasks (1 to 25) performs, in order, console commands, executables, start / stop / restart, backups and waits, each starting only after the one before it succeeded (the first failure ends the run), and is accepted by every other service. The wrong one for the service is refused with 400. With tasks, timeoutSeconds is not used (each task has its own).

Create Service Instance Schedule › path Parameters

id
​string · uuid · required · style: simple

Create Service Instance Schedule › Request Body

The fields a schedule is created or fully replaced with. One shape for both `POST` and `PUT`: unlike Environment's per-key writes, a schedule has no field a client is never shown back, so a whole-object replace deletes nothing a client did not deliberately drop.
​required

What each fire does (V-0702). Required: run_job for a cron-job service, tasks for any other.

cron_expression
​string · required

Five space-separated fields. Syntax only is checked here and in the domain; computing the next fire time is the scheduler's job, out of scope for this slice.

name
​string · required
​OverlapPolicyResponse · required

The three concurrency policies a schedule may declare. Mirrors [OverlapPolicy] one for one; see this module's header for why it is not reused directly.

timeout_seconds
​integer · int32 · required
timezone
​string · required

An IANA zone name, e.g. Europe/Helsinki. Checked syntactically only: this platform does not carry a timezone database yet, and adding one is a dependency decision for the owner, not this slice.

​ScheduleConditionResponse[]
Default: []
paused
​boolean
Default: false

Create Service Instance Schedule › Responses

The created schedule

One schedule, as stored.
​required

What each fire does.

​ScheduleConditionResponse[] · required
created_at
​string · date-time · required
cron_expression
​string · required
id
​string · uuid · required
invalid
​boolean · required

true when the stored schedule no longer validates (for example its time zone is no longer known): it cannot fire until it is edited. Every other field is the stored value as it is.

name
​string · required
​OverlapPolicyResponse · required

The three concurrency policies a schedule may declare. Mirrors [OverlapPolicy] one for one; see this module's header for why it is not reused directly.

paused
​boolean · required
timeout_seconds
​integer · int32 · required
timezone
​string · required
upcoming_fire_times
​string[] · required

The next few fires after now (at most [UPCOMING_FIRE_TIMES]), in UTC; empty while paused. The same computation the scheduler runs, so what is shown is what will happen, daylight-saving changes included.

updated_at
​string · date-time · required
invalid_reason
​string | null

Why the schedule is invalid; null when it is valid.

next_fire_at
​string | null · date-time

The next instant the kernel's scheduler will fire this schedule, in UTC. null while the schedule is paused or invalid, or when the expression never matches again.


Update Service Instance Schedule

PUT
/api/v2/service-instances/{id}/schedules/{schedule_id}

Replaces one schedule's whole configuration. A schedule_id naming a schedule that does not exist, or one belonging to a different service instance, is a 404 -- the same answer either way, since a distinguishable refusal would turn this route into an oracle for which schedule ids exist. The same absolute timeout ceiling, URL guard, and cross-instance schedule check createServiceInstanceSchedule applies also apply here, so a customer cannot raise a timeout or add a condition through an update that creation would have refused. Takes no operation lease and does not reconfigure the running workload.

Update Service Instance Schedule › path Parameters

schedule_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Update Service Instance Schedule › Request Body

Identical shape to [`CreateScheduleRequest`]; kept as its own type so the two routes can diverge later without a shared type forcing them back together, matching `UpdateRuntimeConfigRequest`'s reasoning beside `SetEnvironmentVariableRequest`.
​required

What each fire does (V-0702). Required: run_job for a cron-job service, tasks for any other.

cron_expression
​string · required
name
​string · required
​OverlapPolicyResponse · required

The three concurrency policies a schedule may declare. Mirrors [OverlapPolicy] one for one; see this module's header for why it is not reused directly.

timeout_seconds
​integer · int32 · required
timezone
​string · required
​ScheduleConditionResponse[]
Default: []
paused
​boolean
Default: false

Update Service Instance Schedule › Responses

The schedule as it now stands

One schedule, as stored.
​required

What each fire does.

​ScheduleConditionResponse[] · required
created_at
​string · date-time · required
cron_expression
​string · required
id
​string · uuid · required
invalid
​boolean · required

true when the stored schedule no longer validates (for example its time zone is no longer known): it cannot fire until it is edited. Every other field is the stored value as it is.

name
​string · required
​OverlapPolicyResponse · required

The three concurrency policies a schedule may declare. Mirrors [OverlapPolicy] one for one; see this module's header for why it is not reused directly.

paused
​boolean · required
timeout_seconds
​integer · int32 · required
timezone
​string · required
upcoming_fire_times
​string[] · required

The next few fires after now (at most [UPCOMING_FIRE_TIMES]), in UTC; empty while paused. The same computation the scheduler runs, so what is shown is what will happen, daylight-saving changes included.

updated_at
​string · date-time · required
invalid_reason
​string | null

Why the schedule is invalid; null when it is valid.

next_fire_at
​string | null · date-time

The next instant the kernel's scheduler will fire this schedule, in UTC. null while the schedule is paused or invalid, or when the expression never matches again.


Delete Service Instance Schedule

DELETE
/api/v2/service-instances/{id}/schedules/{schedule_id}

Deletes one schedule outright. Its run history is not deleted with it: service_runs.schedule_id is ON DELETE SET NULL precisely so a deleted schedule's history survives, and this route performs a plain repository delete with no application-level cascade that would defeat that. A schedule_id naming a schedule that does not exist, or one belonging to a different service instance, is a 404.

Delete Service Instance Schedule › path Parameters

schedule_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Delete Service Instance Schedule › Responses

Deleted

No data returned

Run Service Instance Schedule Now

POST
/api/v2/service-instances/{id}/schedules/{schedule_id}/runs

Runs this schedule now: records a manual run (trigger.kind: "manual", naming the caller and the schedule) that the kernel's scheduler executes within about ten seconds. A schedule with tasks performs them in order through the same services the console uses (so a task waits its turn behind a manual action instead of colliding with it); one without starts the service's job. The schedule's guard conditions are not evaluated. A 200 means the run is queued: poll getServiceInstanceRun until outcome is set; its terminationReason lists what each task did. Refused with 409 while the schedule's overlap policy forbids another run (skip: any run in progress; queue: one already waiting), 429 at the service-wide ceiling of five runs in progress, and 422 for a schedule that is invalid or a subscription that is not entitled to run now. A schedule that is not this service's is a 404. Takes no body.

Run Service Instance Schedule Now › path Parameters

schedule_id
​string · uuid · required · style: simple
id
​string · uuid · required · style: simple

Run Service Instance Schedule Now › Responses

The queued run

One run row.
created_at
​string · date-time · required
id
​string · uuid · required
​RunTriggerResponse · required

Who or what caused a run to be decided.

exit_code
​integer | null · int32
finished_at
​string | null · date-time

null until the run concludes, and always null for a skip.

log_object_key
​string | null

A pointer to this run's captured log, if one was captured. null today: log capture is a later slice.

​

null exactly while a fired run is in flight: decided, not yet concluded. See this module's header -- do not treat this as a status string with a seventh "running" value.

peak_cpu_millicores
​integer | null · int64
peak_memory_bytes
​integer | null · int64
scheduled_for
​string | null · date-time

When this run was due. null for a manual trigger, which has no due time by construction.

skip_reason
​string | null

Why a run was skipped or missed. null for one that fired.

​
started_at
​string | null · date-time

null for a run that was skipped, or one that has not started yet.

termination_reason
​string | null

The Kubernetes termination reason, e.g. OOMKilled. null unless the run concluded abnormally.


Get Service Instance Source

GET
/api/v2/service-instances/{id}/source

What this service runs, and the ways this product lets a customer change it. source is null for a service that has been paid for and never configured, which is the panel's empty state rather than an error. A registry source carries credentialUsername and never a password: the browser is never given one, and there is no route that reads one back. permittedArms is the product's own declaration, so a panel never holds the list as a literal; a product permitting exactly one arm renders no chooser, because a radio group with a single option asks a question with one answer. imageReference is what the next deployment will pull, composed here rather than in the client because the rules that build it are the platform's. pending compares that against what the primary workload is actually running, and is null when the resources could not be read, which is a different statement from false. watchTag is always false: nothing polls a registry for a moved tag, so the flag would promise a redeploy that never happens. 404 for a service whose product does not offer this capability, which is the same answer a service belonging to somebody else gives.

Get Service Instance Source › path Parameters

id
​string · uuid · required · style: simple

Get Service Instance Source › Responses

What runs, and the arms this product permits

The whole panel.
​SourceArmId[] · required

The arms this product permits, in the order it declared them.

A product with exactly one permitted arm renders no chooser at all: a radio group with a single option is a control that asks a question with one answer. Supplied by kernel rather than held as a frontend literal, for the same reason a region list is.

watch_tag
​boolean · required

Whether the platform re-pulls when the tag moves.

Always false, and that is the honest answer rather than a placeholder. Nothing polls a registry for a moved tag, so there is no watcher to turn on; a stored flag would promise a redeploy that never happens, which 12-source.md §8 names as the failure to avoid and then asks for anyway. A write that sets it is refused with the reason.

image_reference
​string | null

The reference the next deployment will pull, null when nothing has been chosen.

Composed here rather than in the panel (R2), because the rules that build it are the platform's: a digest wins over a tag, and Docker Hub's own images live under library/.

pending
​boolean | null

Whether what is running differs from what is saved.

null means kernel could not tell, because the instance's resources could not be read. That is a third answer on purpose: false would claim the running workload is up to date, which is a statement rather than an absence of one.

running_image
​string | null

The reference the workload is running right now, read off the instance's primary resource. null when nothing is deployed.

​

What this service runs, or null for one that has not been told yet.

null is the panel's empty state and covers a subscription that has been paid for and never configured. It is not an error and it is not a broken service.


Set Service Instance Source

PUT
/api/v2/service-instances/{id}/source

Chooses what this service runs. It does not start it. Saving here changes what the next deployment will pull; no rollout, no restart and no deployment happen on this request, which is the whole reason Source and Deployments are separate capabilities. An arm this product does not permit is refused with the arms it does, and an arm the platform cannot serve is refused with the reason: nothing in the platform clones or builds a repository, so the git arm has no write path today. credentials has three states, because two cannot express them: absent leaves the stored login alone, null clears it and pulls anonymously, and an object replaces it. Both halves or neither; a username with no password is refused while the form is still open. The password is never returned by any route. A Docker Hub repository with no namespace is qualified with library/, because that is where Docker publishes its own images and the registry answers for nothing else. A host that names an address inside the platform's own network is refused, since the pull happens from inside the cluster with the customer's credential attached. watchTag is refused when true: nothing polls a registry for a moved tag, and a flag with no watcher behind it promises a redeploy that never happens. Takes a reconfigure lease, so it is refused with 409 while another operation holds this service. A service with no configuration at all is given one, which is how a Container bought unconfigured gets its image.

Set Service Instance Source › path Parameters

id
​string · uuid · required · style: simple

Set Service Instance Source › Request Body

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: host, repository, tag +1 more
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
An image in a registry.
kind
​string · const · required
Const value: registry
host
​string · required

ghcr.io, docker.io, registry.example.com:5000.

repository
​string · required

acme/worker. A single-segment repository on Docker Hub is qualified with library/ by kernel, so a customer types nginx.

tag
​string · required
​

Three states, and two would not be enough.

BodyMeaning
field absentleave the stored credentials alone
nullclear them, pull anonymously
{ username, password }replace them

An Option that is None would have to mean either "unchanged" or "clear", and whichever it meant, the other would be unreachable from a panel that is never shown the password it would otherwise resend.

watch_tag
​boolean

Accepted so a client that sends it gets an answer rather than silence, and refused when true. See [SourceResponse::watch_tag].

Default: false

Set Service Instance Source › Responses

What runs now, and whether it has been deployed

The whole panel.
​SourceArmId[] · required

The arms this product permits, in the order it declared them.

A product with exactly one permitted arm renders no chooser at all: a radio group with a single option is a control that asks a question with one answer. Supplied by kernel rather than held as a frontend literal, for the same reason a region list is.

watch_tag
​boolean · required

Whether the platform re-pulls when the tag moves.

Always false, and that is the honest answer rather than a placeholder. Nothing polls a registry for a moved tag, so there is no watcher to turn on; a stored flag would promise a redeploy that never happens, which 12-source.md §8 names as the failure to avoid and then asks for anyway. A write that sets it is refused with the reason.

image_reference
​string | null

The reference the next deployment will pull, null when nothing has been chosen.

Composed here rather than in the panel (R2), because the rules that build it are the platform's: a digest wins over a tag, and Docker Hub's own images live under library/.

pending
​boolean | null

Whether what is running differs from what is saved.

null means kernel could not tell, because the instance's resources could not be read. That is a third answer on purpose: false would claim the running workload is up to date, which is a statement rather than an absence of one.

running_image
​string | null

The reference the workload is running right now, read off the instance's primary resource. null when nothing is deployed.

​

What this service runs, or null for one that has not been told yet.

null is the panel's empty state and covers a subscription that has been paid for and never configured. It is not an error and it is not a broken service.


Issue Registry Token

GET
/api/v2/public/registry/token

The container registry's token endpoint, the realm of its Bearer challenge (Docker token authentication). Send the registry credential as HTTP Basic auth and the requested scopes (repository:<namespace>/<repo>:pull,push, repeatable); the answer is a short-lived signed bearer token the registry verifies. Anything the credential may not do is left out of the token rather than refused, so another tenant's namespace cannot be probed. Invalid credentials answer 401, a suspended, unpaid or deleted registry 403.

Issue Registry Token › Responses

A bearer token carrying the granted access.

access_token
​string · required

The same token: OAuth2-style clients read this name.

expires_in
​integer · int64 · required

Seconds the token stays valid.

issued_at
​string · date-time · required
token
​string · required

The signed bearer token the registry verifies.


List Subscription Registry Tags

GET
/api/v2/subscriptions/{id}/registry/repositories/tags

Lists the tags of one repository (path below the registry's namespace), newest first, with each tag's digest, size and build time.

List Subscription Registry Tags › path Parameters

id
​string · uuid · required · style: simple

List Subscription Registry Tags › query Parameters

path
​string · required · style: form

The repository path below the registry's namespace, e.g. app/web.

The repository path below the registry's namespace, e.g. app/web.

List Subscription Registry Tags › Responses

OK

​RegistryTagView[] · required
truncated
​boolean · required

The repository has more tags than one listing describes; only the first ones (in the registry's order) are shown.


Delete Subscription Registry Tag

DELETE
/api/v2/subscriptions/{id}/registry/repositories/tags

Deletes one tag (tag) of a repository (path). The image stays reachable under its other tags; storage is reclaimed by garbage collection.

Delete Subscription Registry Tag › path Parameters

id
​string · uuid · required · style: simple

Delete Subscription Registry Tag › query Parameters

path
​string · required · style: form

The repository path below the registry's namespace, e.g. app/web.

The repository path below the registry's namespace, e.g. app/web.

tag
​string | null · style: form

The tag to delete.

The tag to delete.

Delete Subscription Registry Tag › Responses

Deleted

No data returned

List Bucket Objects

GET
/api/v2/subscriptions/{id}/bucket/objects

Lists the folders and objects directly under prefix (delimiter /), one page at a time (at most 200). Metadata only: listing is not billed.

List Bucket Objects › path Parameters

id
​string · uuid · required · style: simple

List Bucket Objects › query Parameters

prefix
​string | null · style: form

The folder to list: empty for the bucket root, else a path ending in / (a missing trailing slash is added).

The folder to list: empty for the bucket root, else a path ending in / (a missing trailing slash is added).

token
​string | null · style: form

next_token of the previous page.

next_token of the previous page.

limit
​integer | null · uint32 · min: 0 · style: form

Page size, 1 to 200 (default 100).

Page size, 1 to 200 (default 100).

List Bucket Objects › Responses

One page

​ObjectListingView · required

Sign Bucket Object Download

POST
/api/v2/subscriptions/{id}/bucket/objects/download

Signs a short-lived (15 minutes) GET for one object. The download goes straight to the object storage endpoint and is billed as egress exactly like any S3 client's: previews and thumbnails count too. Refused while the bucket is suspended.

Sign Bucket Object Download › path Parameters

id
​string · uuid · required · style: simple

Sign Bucket Object Download › Request Body

key
​string · required
save_as
​string | null

Makes the browser save the file under this name instead of showing it.

Sign Bucket Object Download › Responses

The signed URL

​PresignedUrlView · required

A GET. Billed egress, like any S3 download.


Start Bucket Object Upload

POST
/api/v2/subscriptions/{id}/bucket/objects/upload

Starts an upload of size_bytes. Up to 64 MiB: a signed PUT of exactly that many bytes. Larger (up to 625 GiB): a multipart upload. Uploads are free (ingress is not billed). Refused when the bucket is over its cap, the upload would take it over, or the bucket is suspended.

Start Bucket Object Upload › path Parameters

id
​string · uuid · required · style: simple

Start Bucket Object Upload › Request Body

key
​string · required
size_bytes
​integer · uint64 · min: 0 · required

The exact size in bytes. A single PUT is signed for exactly this many.

content_type
​string | null

Start Bucket Object Upload › Responses

How to upload

​StartUploadView · required

Sign Bucket Upload Parts

POST
/api/v2/subscriptions/{id}/bucket/objects/upload/parts

Signs PUT URLs for the given 1-based part numbers (at most 50 per call) of a multipart upload. Each part is signed for its exact length.

Sign Bucket Upload Parts › path Parameters

id
​string · uuid · required · style: simple

Sign Bucket Upload Parts › Request Body

key
​string · required
parts
​integer[] · required

1-based part numbers, at most 50 per request.

size_bytes
​integer · uint64 · min: 0 · required

The object's total size, as given to start the upload.

upload_id
​string · required

Sign Bucket Upload Parts › Responses

Part URLs

​PartUrlView[] · required

Complete Bucket Upload

POST
/api/v2/subscriptions/{id}/bucket/objects/upload/complete

Assembles the uploaded parts into the object.

Complete Bucket Upload › path Parameters

id
​string · uuid · required · style: simple

Complete Bucket Upload › Request Body

key
​string · required
​CompletedPartRequest[] · required
upload_id
​string · required

Complete Bucket Upload › Responses

Done

No data returned

Abort Bucket Upload

POST
/api/v2/subscriptions/{id}/bucket/objects/upload/abort

Abandons a multipart upload so its parts stop occupying space.

Abort Bucket Upload › path Parameters

id
​string · uuid · required · style: simple

Abort Bucket Upload › Request Body

key
​string · required
upload_id
​string · required

Abort Bucket Upload › Responses

Done

No data returned

Delete Bucket Objects

POST
/api/v2/subscriptions/{id}/bucket/objects/delete

Deletes up to 1000 objects by key. Allowed while the bucket is over its cap; refused while it is suspended.

Delete Bucket Objects › path Parameters

id
​string · uuid · required · style: simple

Delete Bucket Objects › Request Body

keys
​string[] · required

1 to 1000 object keys.

Delete Bucket Objects › Responses

What failed

​DeleteObjectsView · required

Create Bucket Folder

POST
/api/v2/subscriptions/{id}/bucket/objects/folders

Creates an empty folder (a zero-byte prefix/ object).

Create Bucket Folder › path Parameters

id
​string · uuid · required · style: simple

Create Bucket Folder › Request Body

prefix
​string · required

The folder's full path, e.g. photos/2026/.

Create Bucket Folder › Responses

Done

No data returned