Zaroz
Zaroz Cloud Kernel Open API

AddDomainRequest

Body for `POST /organizations/{id}/domains`.
domain
​string · required

The hostname the organization is proving it controls, for example shop.acme.com. Normalized (lowercased, trailing root dot stripped) and validated server-side; see the domain's own normalize_domain.

AddEndpointRequest

`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

AddOrganizationMemberRequest

Body of `POST /organizations/{id}/members`.
user_id
​string · uuid · required

The user to add to this organization.

​

Defaults to member, the least-privileged role: an omitted role must never widen access further than the caller asked for.

AddTicketMessageRequest

Body for `POST /api/v2/support/tickets/{id}/messages`.
body
​string · required

Files uploaded against this ticket that this message carries. Empty unless the caller uploaded something first; an attachment that is uploaded and never sent stays unreferenced and is not rendered anywhere.

AllocateUnitRequest

`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

AllocateWait

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
Properties for Variant 1:
Const value: delivered
Hold the answer until the unit's agent has the allocation's files, or 10 s.

AllocatedUnitResponse

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).

AllocationProtectionStateResponse

One allocation's live protection state, as reported by the Warden node serving it right now.
allocation_id
​string · uuid · required
protection_state
​string · required

normal, pressure, or attack. Never unknown: an allocation whose state was not reported is absent from the list instead, so a caller can tell "not reported" from "reported as normal".

AlterSharedDatabaseTableRequest

​AlterTableOperationRequest[] · required

AlterTableOperationRequest

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, column
type = object · requires: kind, name
type = object · requires: kind, from, to
type = object · requires: kind, name, column
type = object · requires: kind, to
Properties for Variant 1:
​ColumnDefinitionRequest · required
kind
​string · const · required
Const value: add_column
after_column
​string | null
Default: null

AnswerSetupStepResponse

`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.

ApiTokenAuthorization

A restricted token is deny-by-default. An empty grant list intentionally represents a token that can authenticate but cannot authorize any action. The one exception is the explicit [`Self::unrestricted`] form (V-0676): it resolves exactly as the pre-V-0625 "legacy" token did, acting as the full user. It is persisted as `{"unrestricted": true}`, can only be created by internal paths (kernel-bootstrap) and is refused by every minting route: the public wire form ignores it (rejecting `unrestricted: true`) and [`Self::is_available_to`] is false for it, which both token services check.
​ApiTokenGrant[] · required

ApiTokenCapability

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
accounting.view
accounting.manage
analytics.view
anti_ddos.manage
application_tokens.manage
audit_logs.view
captcha.manage
catalog.manage

ApiTokenGrant

​ApiTokenCapability · required

Closed, policy-owned action catalog. Capabilities are exact: there is deliberately no prefix or glob matching, and unknown wire values are rejected during deserialization. PartialOrd/Ord exist so a capability set can be a BTreeSet, which is what [crate::domain::auth::application_token::ApplicationToken] stores. The ordering is the declaration order and is not a wire contract: nothing serializes it, and as_str remains the only stable representation.

​ApiTokenGrantScope · required

Scope of one grant. Global means the capability is allowed regardless of resource. ResourceType is every resource in one namespace, while Resource names exactly one object.

ApiTokenGrantScope

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind
type = object · requires: kind, resource_type
type = object · requires: kind, resource_type, resource_id
type = object · requires: kind, resource_type, root_resource_id
Properties for Variant 1:
kind
​string · const · required
Const value: global

AppAdminApplyDto

string · enum
Enum values:
restart
install_only

Mirrors [AppAdminApply] by hand.

AppAdminDto

[`SetupStepId::AppAdmin`](kernel_core::domain::setup_steps::SetupStepId::AppAdmin)'s answers, shared between request and response: unlike `app_runtime`, nothing here is frozen or overwritten server-side -- an unasked `email`/`site_title` round-trips as `""` on both the way in and the way out, exactly as [`AppAdminAnswers::validate`] itself already treats it.
username
​string · required
email
​string

"" when this product's params never asked for an email.

Default:
site_title
​string

"" when this product's params never asked for a site title.

Default:

AppAdminParamsDto

[`SetupStepId::AppAdmin`](kernel_core::domain::setup_steps::SetupStepId::AppAdmin)'s declared params, mirroring [`AppAdminParams`] by hand on this module's own doc comment's discipline.
apply
​AppAdminApplyDto · enum · required

Mirrors [AppAdminApply] by hand.

Enum values:
restart
install_only
email
​boolean · required

Whether this product asks for an admin email at all.

​UsernameRuleDto · required

Mirrors [UsernameRule] by hand.

​

None when this product does not ask for a site title at all.

AppRuntimeParamsDto

[`SetupStepId::AppRuntime`](kernel_core::domain::setup_steps::SetupStepId::AppRuntime)'s declared params: the product's own runtime table (`docs/superpowers/specs/2026-09-25-setup-steps-shared-steps-plan.md` §3), mirroring [`AppRuntimeParams`] by hand.
​RuntimeOptionDto[] · required
start_command_max_length
​integer · uint16 · min: 0 · max: 65535 · required

AppRuntimeRequestDto

[`SetupStepId::AppRuntime`](kernel_core::domain::setup_steps::SetupStepId::AppRuntime)'s REQUEST shape: the three questions a customer answers (v1's DiscordBot flow's own three). `image`/`install_command` are absent on purpose -- [`AppRuntimeAnswers::validate`] overwrites both from the product's current params for the chosen runtime/variant, so a caller has nothing to set them through even if it tried (see [`AppRuntimeAnswers`]'s own doc comment on why that field is frozen server-side).
runtime
​string · required
start_command
​string · required
variant
​string · required

AppRuntimeResponseDto

[`SetupStepAnswersResponseDto::AppRuntime`]'s payload: the customer's chosen runtime/variant/start command, plus the `image`/`install_command` [`AppRuntimeAnswers::validate`] froze at submission -- never the same shape as [`AppRuntimeRequestDto`], on the same "request and response can differ" precedent [`ServerPasswordsRequestDto`]/[`ServerPasswordsResponseDto`] already set for this file.
image
​string · required
install_command
​string · required
runtime
​string · required
start_command
​string · required
variant
​string · required

ApplicationConsentPreviewResponse

`GET /api/v2/me/application-delegations/consent-preview/{application_id}`.
application_id
​string · uuid · required
application_name
​string · required

Presentable name, the same one MyApplicationDelegationResponse carries.

ApplicationUserProfile

`GET /api/v2/applications/users/{id}` and one element of the batch answer.
id
​string · uuid · required
name
​string · required

The user's display name, as shown to other users.

avatar_asset_id
​string | null · uuid

The profile picture, addressed through /api/v2/assets/{id}. None when they never set one.

deleted_at
​string | null · date-time

Set when the account was deleted. The profile is still answered so a listing can render "deleted user" rather than "unknown user".

ApplicationUsersLookupRequest

`POST /api/v2/applications/users/lookup`.
ids
​string[] · required

At most [MAX_LOOKUP_IDS]; duplicates are fine.

ApplicationUsersLookupResponse

Ids with no account are absent from `users`, not an error.
​ApplicationUserProfile[] · required

ApplyMode

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: live
Nothing to do: the new answer is identical to the stored one.

AttachCustomDomainRequest

Point one hostname at this service. One field, and no `is_primary`. A Traefik router matches a `Host()` rule and every attached name is matched equally, so a primary flag would be a field the platform stores and never acts on -- exactly the kind of promise `capabilities.rs::validate_parameters` refuses to let a product author make. If a canonical-host redirect is wanted later it is a real behaviour with a real field, not this one.
hostname
​string · required

The fully-qualified hostname, for example shop.acme.com.

Validated as a hostname before anything is persisted: trimmed, lowercased and stripped of a trailing dot, then refused if it is empty, longer than 253 characters, an IP address literal, a single label with no dot in it, or carries a label that is empty, longer than 63 characters, hyphen-terminated, or holds anything outside [a-z0-9-].

AttachEndpointDomainRequest

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

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

AttackSummaryReasonResponse

Why traffic was filtered: one of `structure`, `return_path`, `protocol`, `verdict`, `rate`. Never a gate name or threshold.
bytes
​integer · uint64 · min: 0 · required
key
​string · required
packets
​integer · uint64 · min: 0 · required

AttackSummaryResponse

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.

AttackSummaryServiceResponse

One service instance's share of the day's filtered traffic.
blocked_bytes
​integer · uint64 · min: 0 · required
name
​string · required
service_instance_id
​string · uuid · required

AttackSummaryTimelineEntryResponse

One attack episode on one of the caller's services.
blocked_bytes
​integer · uint64 · min: 0 · required
duration_seconds
​integer · int64 · required
started_at
​string · date-time · required
ended_at
​string | null · date-time

Absent while the episode is still open.

service_name
​string | null

AuditEntryResponse

One audit entry, as a customer sees it. Deliberately narrower than [`AuditLog`]. Two of its fields are platform correlation and are not serialized here: `sponsored_workload_id`, a platform-internal identity that has no place on a `/v2` wire contract, and `organization_id`, which is the scope of the request rather than data about the entry. (`order_id` was a third until ZC-0261 phase 9d deleted the column.) `sponsor_application_*` and `api_token_*` are kept: when something acted on a customer's behalf, hiding which token or application did it would make the log less useful for exactly the question an audit log exists to answer.
action
​string · required
created_at
​string · date-time · required
id
​string · uuid · required
metadata
​required

Arbitrary JSON.

principal_kind
​string · required

User, Application, or System.

api_token_id
​string | null · uuid
api_token_name
​string | null
principal_id
​string | null
principal_name
​string | null
project_id
​string | null · uuid
service_instance_id
​string | null · uuid

Which of the caller's services this entry is about (ZC-1277).

AdminAuditEntryResponse has always carried it, and its doc comment explains the omission here as these fields being "the scope of ITS request, not data about the entry". That is true of organization_id and was never true of this one: a customer read is scoped to an organization, and could narrow only as far as a project, so the service was never the scope of the request -- it was the one thing the customer could not learn.

sponsor_application_id
​string | null · uuid
sponsor_application_name
​string | null

AuthUserResponse

Minimal caller identity carried on a session payload. Deliberately not the full `/console/me` shape (`kernel_http_v2_identity::contracts::me::MeResponse`): `/v2` feature crates do not depend on one another (see `crates/http/CLAUDE.md`), so this is a small, independent projection sized for what a session response needs, not a shared type.
id
​string · uuid · required
is_onboarded
​boolean · required
name
​string · required
email
​string | null

Resolved through UserEmailService (checks oauth_accounts first, then password_credentials). None when neither source has an address for this user -- User itself carries no email field.

BackupResponse

One backup, as the customer who took it sees it.
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required

The label the customer gave it, or kernel's own timestamped default.

status
​string · required

creating, ready, failed or restoring.

completed_at
​string | null · date-time

Present once the snapshot has finished or failed.

restore_phase
​string | null

The step of an in-progress restore, or null when this backup is not currently being restored. One of stopping_workloads, detaching_pre, maint_attaching, reverting, detaching_post, csi_retaining_pv, csi_recreating_pvc, restarting_workloads.

restore_started_at
​string | null · date-time

When the current restore began, so a client can render "restoring for N minutes" without polling a second endpoint. null outside a restore.

size_bytes
​integer | null · int64

Present once the snapshot has finished (status: "ready" or later).

BackupsResponse

Every backup of one volume, newest first (`BackupRepository::find_by_provision`'s own order).
​BackupResponse[] · required
max_backups
​integer · uint32 · min: 0 · required

The ceiling POST .../backups is refused against, published here so a panel never holds it as a literal.

BannedPlayerResponse

name
​string · required
banned_at
​string | null · date-time
reason
​string | null
source
​string | null

BillingClassResponse

string · enum
Enum values:
included
extra

BucketKeyView

access_key_id
​string · required
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
scope
​string · required

read_only or read_write.

last_used_at
​string | null · date-time

BucketKeysResponse

​BucketKeyView[] · required

BucketResponse

​BucketView · required

BucketUsageResponse

​BucketUsageView · required

BucketUsageSampleView

egress_bytes
​integer · uint64 · min: 0 · required
ingress_bytes
​integer · uint64 · min: 0 · required
stored_bytes
​integer · uint64 · min: 0 · required
window_start
​string · date-time · required

BucketUsageView

egress_bytes
​integer · uint64 · min: 0 · required

Bytes sent out of the bucket in the window (billed).

from
​string · date-time · required
gap_hours
​integer · uint32 · min: 0 · required

Hours with no measurement, filled with the nearest earlier one.

​BucketUsageSampleView[] · required

Hourly measurements, oldest first.

storage_gib_months
​string · required

Average stored GiB over the whole window, as a decimal string.

to
​string · date-time · required

BucketView

access
​string · required

What the data plane allows right now: open, read_only (over the cap), suspended (unpaid or moderated) or deleted. Suspended buckets keep their data and can still be browsed through the console.

addressing_style
​string · required

Always path: use path-style addressing ({endpoint}/{bucket_name}/{key}); virtual-hosted style is not served.

bucket_name
​string · required

The bucket name to use with that endpoint.

cap_bytes
​integer · uint64 · min: 0 · required
created_at
​string · date-time · required
endpoint
​string · required

The S3 URL to point a client at.

id
​string · uuid · required
label
​string · required
public_read
​boolean · required
region_id
​integer · int32 · required
s3_region
​string · required

The region name S3 clients sign with. Not a place: the bucket lives in region_id.

subscription_id
​string · uuid · required
updated_at
​string · date-time · required
public_url
​string | null

Where anonymous readers fetch objects ({url}{key}); absent unless public_read is on.

purge_at
​string | null · date-time

When the bucket's data will be purged, once deleted.

stored_bytes
​integer | null · uint64 · min: 0

Latest measured stored bytes; absent before the first measurement (measured hourly).

ByOauthAccountResponse

Who a linked account belongs to.
display_name
​string · required
language_tag
​string · required

The user's language, for example en-US.

user_id
​string · uuid · required

CancelWithWalletCreditRequest

quote_id
​string · required

The quote_id of a quote issued for this subscription.

CancelWithWalletCreditResponse

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.

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

ChangePasswordRequest

Changes the password of a caller who already has one.
current_password
​string · required
new_password
​string · required
current_session_id
​string | null · uuid

The caller's own session, spared when the change signs every other session out. Omit to be signed out everywhere, including here.

CheckoutRequest

`POST /api/v2/console/checkout`'s request. A customer picks a **region** and a plan; which hardware profile inside that region runs the workload is internal placement, resolved by `CreateSubscriptionCheckoutServiceImpl::resolve_region_hardware_profile` (`crates/application/subscriptions/src/application/services/orders/create_subscription_checkout_service.rs`). This used to carry a `hardware_profile_id` instead, which no client could ever fill because nothing on `/v2` enumerated hardware profiles. `plan_id` and `region_id` are both `i32` (`impl_int_id!`), not UUIDs: the same ids `GET /catalog/products/{id}/plans` and `GET /catalog/regions` hand back as `PlanResponse.id` and `RegionResponse.id`.
name
​string · required

The customer's name for the service being bought.

The subscription is a purchase of a product and carries no name; this names the ServiceInstance the initial payment creates -- the VPS or container the customer actually refers to.

plan_id
​integer · int32 · required
product_id
​string · uuid · required
slug
​string · required

The URL segment addressing that service, unique within its project. Supplied rather than derived here: the console suggests one from the name as the customer types and lets them edit it.

cadence_months
​integer | null · int16 · min: -32768 · max: 32767

The billing term to buy, in months: 1, 3, 6 or 12 (V-0717).

Omitting it buys the monthly term, which is what every client written before term choice sends. The plan's offered terms, with each one's price and saving, are its terms on the product plan listing. A term the plan is not sold at is refused (400), never sold monthly instead; the subscription renews at the same term and Price.

Default: null
discount_code
​string | null

An optional discount code, exactly as the customer typed it.

A code that cannot be used is refused, not ignored: a customer who entered one and was charged full price anyway has been overcharged as far as they are concerned.

Public ports to open on the service once it runs. Omitting this (or sending none) buys exactly what every earlier request bought.

The customer names only the container port and the protocol(s); Zaroz assigns the public address and port. Each port is billed by the hour it is held, monthly in arrears: it is never part of the invoice this request creates, and the quote lists each one as a line of amount zero with the estimate in metered_estimate. A port that cannot be opened right after payment never fails the purchase: it is listed on the service as pending with the reason, and opened as soon as it can be. Refused for a product that offers none, past the plan's included ports when more are not priced, and above 10 per request.

hardware_profile_id
​integer | null · int32

The hardware tier inside region_id to run on, as RegionHardwareResponse.id from GET /api/v2/catalog/regions (decision-0347: a customer chooses between a region's tiers, each with its own Price). Omit it to let kernel place the service on the cheapest tier that fits. A tier that is not listed under region_id, or that the listing marks unavailable, is refused exactly as an unbuyable region is.

Default: null
operating_system_id
​string | null · uuid

The operating system to install, for a VPS purchase (ProductFamily::VirtualMachine). Required for that family, and refused if sent for any other -- nothing else on /v2 installs an operating system. GET /api/v2/catalog/products/{id}/operating-systems lists what can be named here.

Default: null
organization_id
​string | null · uuid

Which organization is buying, and therefore which one the subscription and its invoice bill to.

Optional, and omitting it means "my personal organization" -- the only thing /v2 checkout could bill before shared organizations became purchasable. Existing clients that send no organization_id keep the exact behaviour they had.

Sending one requires the caller to be entitled to spend that organization's money: an Owner or Admin membership, or its personal owner. A Member or Billing role is refused (403), never silently redirected to the caller's own organization. An organization the caller has no relationship to answers 404, the same as an unknown id, so ids cannot be enumerated by their refusal.

project_id
​string | null · uuid

Which project the purchased service belongs to (§34.1's Organization -> Project -> Subscription -> Resource).

Optional, and omitting it means the buying organization's default project -- created on first use if it does not exist yet, the same resolution /v1 order creation has always applied for an organization with no project concept of its own. This is what makes checkout reachable without first showing the caller an organization: §11's invisibility on organization_id is worthless if project_id still forces one into view.

When supplied, it must name a project the buying organization owns; one belonging to another organization is refused rather than ignored, because a subscription filed under someone else's project misattributes that spend in every read that joins through it.

region_id
​integer | null · int32

Where the service should run. Must be one of the regions GET /api/v2/console/catalog/regions returns for this product_id/plan_id: that read applies the same access and capacity rules checkout does, so anything it lists is buyable and anything it omits would be refused here.

Required for every purchasable product except a licence (ProductFamily::License), which is placed nowhere at all and is refused if this is sent. Omit it entirely for a licence purchase.

Default: null
​

The size to buy, when the customer sized it themselves.

Omitting this buys the plan exactly as authored, which is what every existing client does and what every existing request means. This is an override, not a replacement: plan_id is still required and still decides which hardware profile runs the workload and which Price rates it, because those are placement and money rather than size.

A plan is one convenient point inside the range products.minimum_*/maximum_* describes, and a customer sizing their own instance is choosing a different point in the same range. Anything outside it is refused by Product::admits rather than clamped to fit: silently resizing somebody's order and then charging them for the result is the worse of the two answers.

This is also what makes persistent storage opt-in. A product whose minimum_storage_gb is zero admits a shape with no disk, that shape prices at zero against the storage rate, and the provisioner builds no volume for it.

CheckoutResourcesRequest

A customer-chosen size, in the units the rest of `/v2` already quotes. `memory_gb` is fractional for the same reason every other `*_memory_gb` field is: memory is stored in mebibytes, so 0.5 is a real 512 MiB request rather than a rounding error. CPU has always been fractional. Storage stays whole gibibytes, because storage still is.
cpu
​number · float · required
memory_gb
​number · double · required

Memory in GiB, fractional (0.5 = 512 MiB)

storage_gb
​integer · uint64 · min: 0 · required

Storage in GiB. Zero buys no volume at all, where the product allows it.

CheckoutResponse

`/v2` DTOs are always named `*Request` / `*Response` (design doc `§43.4`). There is no `order` field anywhere on this type or its `subscription` nested value -- design constraint 1 of this checkout slice: V2 buys without orders, and no wire contract may name one even in spirit.
available_cents
​integer · int64 · required
​object · required

The establishing invoice, in the same shape GET /billing/invoices/{id} already returns (InvoiceResponse) -- a client that just checked out pays this invoice through the existing invoice-id-keyed payment endpoints (POST /api/v1/invoices/{id}/checkout/mollie or .../paypal, both of which already resolve by InvoiceId alone).

required_cents
​integer · int64 · required

ZC-1324 org trust S5 (§8.4). See [QuoteResponse::required_cents]'s own doc comment -- identical meaning, read AFTER this checkout's own effects (its reservation, if it wrote one) rather than before.

​CheckoutSubscriptionResponse · required
​

V-0449.04: postpaid disclosure, present only for the OWNER of an organization with a credit limit. See crate::contracts::standing::PostpaidDisclosureResponse.

​

CheckoutSubscriptionResponse

id
​string · uuid · required
organization_id
​string · uuid · required
status
​CheckoutSubscriptionStatusResponse · enum · required
Enum values:
pending

CheckoutSubscriptionStatusResponse

string · enum
Enum values:
pending

ClaimTrialRequest

`POST /api/v2/me/trials`'s request, and `POST /api/v2/me/trials/gaming`'s. Deliberately tiny next to `CheckoutRequest`: no organization (always the caller's own), no product or plan (the trial declares them), no size (the plan as authored), no discount code (there is nothing to discount).
name
​string · required

The customer's name for the service they will receive.

slug
​string · required

The URL segment addressing it, unique within its project. Same rules as checkout's: 3-63 characters of lowercase letters, numbers and single hyphens.

trial_price_id
​string · uuid · required

The id from GET /api/v2/me/trials.

project_id
​string | null · uuid

Which project to file it under. Omit for the personal organization's default project, created on first use. Must be omitted on POST /api/v2/me/trials/gaming, which always files into the games project.

region_id
​integer | null · int32

Where to run it.

Ignored when the trial reports region_is_fixed, and omitting it otherwise means "anywhere that fits, cheapest first" -- resolved server-side, never by the client.

ClaimTrialResponse

What a claim produced: a running trial and the zero invoice that records it.
​object · required

The zero-amount establishing invoice, in the same shape GET /billing/invoices/{id} returns. Already paid; it is returned so the customer's invoice list and this response cannot describe the same row differently.

​ClaimedTrialSubscriptionResponse · required

ClaimableTrialResponse

One trial the caller's storefront offers. `claimable` and `refusal` move together: a row the caller may take has `claimable: true` and no reason. Refused trials are still listed, so a dashboard renders them disabled with the reason rather than making a product's trial vanish the moment it has been used -- which would leave the customer wondering whether the product ever had one.
claimable
​boolean · required
duration_hours
​integer · uint32 · min: 0 · required

How long the free period lasts, in hours, as declared.

id
​string · uuid · required

The trial Price's id (decision-0353: a trial is a Price), to send back as trial_price_id to POST /api/v2/me/trials.

plan_id
​integer · int32 · required

The plan this trial grants.

product_id
​string · uuid · required
product_name
​string · required

The product's display name, resolved server-side so a list needs no second request per row.

region_is_fixed
​boolean · required

Whether this trial pins its own hardware or region. When true, any region_id sent to the claim is ignored, so a client must not offer a region picker for this row.

​

Why not, when claimable is false. A stable machine value plus a sentence; clients switch on code and never on message.

region_id
​integer | null · int32

The region the trial runs in when it fixes one.

ClaimableTrialsResponse

​ClaimableTrialResponse[] · required

ClaimedTrialSubscriptionResponse

auto_renew
​boolean · required

Always false on a fresh claim, and stated on the wire rather than assumed: this is the field that makes the trial free, and turning it on through PUT /api/v2/subscriptions/{id}/renewal is the whole of what converting a trial means. A client that hides it leaves the customer no way to keep their service.

id
​string · uuid · required
organization_id
​string · uuid · required
project_id
​string · uuid · required
status
​string · required

active for a claim that provisioned, pending for the narrow window where the zero invoice settled but the row had not been re-read. Carried as the real status rather than a one-variant enum like CheckoutSubscriptionStatusResponse, because a claim -- unlike a checkout -- activates inside the same request.

trial_ends_at
​string | null · date-time

When the free period stops. After this the subscription either starts billing (if the customer turned renewal on) or is suspended.

ColumnDefinitionRequest

​LogicalColumnTypeRequest · required
name
​string · required
​
nullable
​boolean
Default: false
primary_key
​boolean
Default: false

CommandConsoleSessionTicketResponse

`POST /api/v2/console/service-instances/{id}/resources/{resource_id}/command-console/session-ticket`.
expires_at
​string · date-time · required

When the gateway stops accepting this ticket.

This is a redemption deadline, not a session deadline. cluster-agent checks exp once, when the socket is upgraded (gateway/edge_token.rs:41, called from gateway/terminal.rs:35); an attached session then runs until one side disconnects. So a client redeems the ticket immediately and never re-mints to stay connected: it mints again only to attach again.

url
​string · required

The gateway URL to open a WebSocket against, carrying the signed ticket in its query string.

Binary frames both ways, and no subprotocol: cluster-agent's /terminal upgrade negotiates none (cluster-agent/src/gateway/mod.rs:88), unlike its /vnc and /ssh upgrades. Anything that is not a binary frame is dropped by the gateway rather than refused (gateway/terminal.rs:107), so there is no control channel to speak into.

CompleteOAuthLoginRequest

Redeems the single-use completion key the callback handed the client. A body rather than `/v1`'s `?key=` query string, and the operation is a POST rather than a GET, because the key is a bearer credential for a whole session: a query string is written into browser history, into `Referer` on the next navigation, and into every proxy and access log on the path. The same reasoning `CUSTOMER_FACING_V2_WRITES` records for the console family's ticket-minting POSTs applies here, and more sharply -- this one mints the session itself.
key
​string · required

ConditionFallbackResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: skip
Treat an unanswerable check as a veto. The safe default.

ConfigureHttpDomainRequest

Publish an HTTP domain in front of one TCP-capable allocation.
domain
​string · required

ContainerStateResponse

string · enum
Enum values:
running
waiting
terminated

ContainerStatusResponse

Live per-container status inside one pod. Field naming mirrors [`CrashLoopInfoResponse`] deliberately, so a consumer needs one vocabulary for the same kubelet data rather than two.
name
​string · required
ready
​boolean · required
restart_count
​integer · int32 · required
state
​ContainerStateResponse · enum · required
Enum values:
running
waiting
terminated
last_exit_code
​integer | null · int32
last_terminated_at
​string | null · date-time
last_terminated_reason
​string | null
message
​string | null

Detail for the container's current state: the waiting reason's message while waiting, the terminated message while terminated, absent while running.

ControlledUnitResponse

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.

CrashLoopInfoResponse

restart_count
​integer · int32 · required
last_exit_code
​integer | null · int32

1 is an application error, 137 a SIGKILL and usually the OOM killer, 143 an ordinary SIGTERM.

last_terminated_at
​string | null · date-time
last_terminated_reason
​string | null
message
​string | null
next_retry_at
​string | null · date-time

Best-effort estimate of the next restart attempt. Kubernetes does not publish the kubelet's schedule, so this is reconstructed from the last termination plus the standard backoff.

CreateBackupRequest

name
​string | null

Optional label. Kernel fills in a timestamped default ("Backup 2026-09-10 14:35") when this is absent, null, or blank.

Default: null

CreateBucketKeyRequest

scope
​string · required

read_only or read_write.

name
​string | null

Display name. Blank or absent becomes "Key".

CreateBucketRequest

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.

CreateCredentialRequest

`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

CreateDirectoryRequest

Where to create a directory. The path travels in the body rather than the query string only because this is the one route with no other reason to have a body, and a `POST` whose entire input is a query parameter reads as a `PUT` that forgot its payload.
path
​string · required

CreateFirewallRuleRequest

action
​FirewallAction · enum · required
Enum values:
allow
block
direction
​FirewallDirection · enum · required
Enum values:
ingress
egress
priority
​integer · uint16 · min: 0 · max: 65535 · required

Lower runs first. Ties are broken by the datapath, not here, so two rules sharing a priority have no defined order between them.

protocol
​FirewallProtocol · enum · required
Enum values:
tcp
udp
icmp
any
description
​string | null
Default: null
cidr
​string | null

The source range this rule matches, IPv4 only and no broader than /8. Absent, null, or empty all mean "any source".

Default: null
enabled
​boolean

Defaults to true: a rule created disabled is almost always a mistake in the client rather than an intention, and /v1 defaults the same way.

Default: true
port_from
​integer | null · uint16 · min: 0 · max: 65535

Required for tcp and udp, refused for icmp and any. The domain enforces that (FirewallRule::create), so an omitted port on a TCP rule is a validation refusal rather than a rule that matches every port.

Default: null
port_to
​integer | null · uint16 · min: 0 · max: 65535
Default: null

CreateMyApiTokenRequest

Body of `POST /me/api-tokens`.
​object · required

The grants the token holds. Every capability must be one the caller may grant themselves (GET /me/api-token-capabilities).

name
​string · required

1-64 characters after trimming.

admin_authorized
​boolean

Must be true exactly when the authorization contains an admin-only capability, and then requires an elevated session.

Default: false
expires_at
​string | null · date-time

Omit for a token that never expires on its own. Must be in the future.

Default: null

CreateMyApplicationDelegationRequest

Grants spec `§2.6` (Path B), simplified: this is what a consent page the caller is looking at, after they approve, would submit with their own kernel session. Discovering that there is something to approve (a one-time consent-request URL shown to the caller out of band, grants spec `§2.6` steps 2-3) is `Q78`, unresolved, and not built here -- see the ZC-1321 report.
application_id
​string · uuid · required
​ApiTokenCapability[] · required

Non-empty: a consent event that grants nothing is refused.

CreateMyReferralLinkRequest

`POST /api/v2/me/referral-links`.
slug
​string · required

1-64 characters of letters, numbers, hyphens, or underscores. Lowercased and trimmed before it is checked for uniqueness -- see kernel_organizations::referral_link_service::normalize_slug.

CreateMyWalletTopUpRequest

Start a top-up of the caller's own wallet.
amount_cents
​integer · int64 · required

Integer euro cents, and one of the fixed presets: 2500, 5000, 10000, 25000 or 100000. Any other amount is refused with invalid-field-value; top-ups are always EUR.

provider
​TopUpProviderRequest · enum · required

A provider a top-up can be started with. Mollie is deliberately absent: a Mollie top-up has no checkout and no settlement path in the kernel.

Enum values:
paypal
nowpayments

CreateOrganizationApiTokenRequest

Body of `POST /organizations/{id}/api-tokens`.
name
​string · required
​ApiTokenCapability[]

What the token may do, scoped to this organization. An empty list mints a token that authorizes nothing: for an organization credential, absence denies rather than permits.

Default: []
expires_at
​string | null · date-time

Omit for a token that never expires on its own. Must be in the future.

Default: null
​

Omitting this yields membership_tied, the safer shape: the token is revoked when the creator's membership of this organization ends. Pass organization_owned only when the credential is meant to outlive whoever set it up.

Default: null

CreateOrganizationInvitationRequest

Body of `POST /organizations/{id}/invitations`. `role` reuses [`OrganizationRoleRequest`] as-is, including its `Owner` variant, rather than a narrower enum without it: the "an invitation cannot grant the owner role" rule is enforced by `OrganizationInvitation::create` (`OrganizationInvitationValidationError::OwnerRoleNotInvitable`) at the domain layer. Narrowing the wire type here would only make this request's shape quietly diverge from `AddOrganizationMemberRequest`'s for no enforcement benefit. Unlike `AddOrganizationMemberRequest`, there is no default role: an invite sent with no role specified is a validation error at the application layer, not a silent grant of the least-privileged one. Inviting is a rarer, more deliberate action than adding an already-known member, so silently guessing on the caller's behalf is the wrong failure mode here.
email
​string · required
role
​OrganizationRoleRequest · enum · required

Distinct from OrganizationRoleResponse because request and response enums are separate types on /v2 (design doc §43.4), even where they currently carry the same variants.

Enum values:
owner
admin
billing
member

CreateOrganizationRequest

Body of `POST /organizations`.
name
​string · required
slug
​string · required

URL-safe identifier, unique platform-wide (lowercase ascii letters, digits, and single internal hyphens).

CreateProjectRequest

Body of `POST /organizations/{id}/projects`.
name
​string · required
slug
​string · required

URL-safe identifier, unique within the owning organization (lowercase ascii letters, digits, and single internal hyphens).

description
​string | null
Default: null

CreateRegistryCredentialRequest

scope
​string · required

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

name
​string | null

Display name. Blank or absent becomes "Credential".

CreateRegistryRequest

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.

CreateScheduleRequest

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.

Default: []
paused
​boolean
Default: false

CreateServiceShareRequest

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".

CreateSessionApplicationDelegationRequest

Grants spec `§2.5` (Path A): the calling application already holds a session the kernel issued to a user (typically because that application runs the kernel's own login flow, e.g. FOP). `session_token` is DATA, not authentication -- the request is authenticated by the application's own bearer token, in `Authorization`, exactly like every other application call. The kernel verifies `session_token` itself and mints (or replaces) the delegation for whichever user it names; the calling application never gets to assert an identity the kernel has not independently checked.
​ApiTokenCapability[] · required

Non-empty: a delegation that grants nothing is refused.

session_token
​string · required

CreateSharedDatabaseTableRequest

​ColumnDefinitionRequest[] · required
name
​string · required

CreateSubscriptionSharedDatabaseRequest

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.

CreateTicketRequest

Body for `POST /api/v2/organizations/{id}/tickets`.
body
​string · required
subject
​string · required

CreateTicketResponse

A freshly opened ticket together with its opening message. Returned by `createTicket` rather than the bare `TicketResponse`: the caller sent a message body along with the subject, and it would otherwise disappear from the response until the next `getMyTicket` read.
​TicketMessageResponse · required

One message in TicketDetailResponse::messages.

Soft-deleted messages never appear here at all -- this endpoint reads SupportMessageRepository::find_by_ticket_id_visible, which already excludes them, rather than /v1's find_by_ticket_id (every message, with the body blanked for a deleted one at render time). A /v2 read surface has no reason to reconstruct a "[deleted]" placeholder in the thread; see this crate's report.

A staff author's identity is never exposed: author_is_staff is true and author_name reads "Support Team". A non-staff message's author_name is that message's own author's display name, resolved per message by the caller of from_message -- not necessarily the caller of the request. A ticket's whole thread is now visible to every member of its organization (design doc §44.2), so a non-staff message may have been authored by the ticket's owner, another organization member, or the current caller; there is no single name that is correct for all of them.

​TicketResponse · required

One ticket visible to the caller, in the detail view (paired with its messages in TicketDetailResponse) -- any ticket belonging to any organization the caller may see (design doc §44.2), not only ones the caller personally opened. Same no-staff-identity rule as TicketSummaryResponse.

CreateUnitRequest

`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

CreateUnitWebhookRequest

`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: []

CreatedApplicationDelegationResponse

What the calling application needs back: the id of the delegation it now holds, and the user it was minted for. Nothing else -- this is not the user's own account view (`GET /me/application-delegations`), it is the application's confirmation that its own mint call succeeded.
id
​string · uuid · required
user_id
​string · uuid · required

CreatedCredentialResponse

`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>.

CreatedMyApiTokenResponse

The only response that ever carries the secret.
secret
​string · required

The full credential, shown exactly once. Only a digest of its secret half is stored.

​MyApiTokenResponse · required

One of the caller's own personal API tokens (ZC-0512).

Never carries the secret, or even its digest. Only a digest of the secret half is stored on token_hash, and this response omits that field entirely: the full credential is returned exactly once, at creation (POST /me/api-tokens), and no later read -- this one included -- can reproduce it.

Unlike [crate::contracts::organization_api_tokens::OrganizationApiTokenResponse], capabilities here is not filtered to resource-scoped grants: a personal token may hold a global or resource-type-scoped grant as well as a resource-scoped one, so every capability the token's authorization names is reported, regardless of scope shape. A token created before restricted authorization existed carries no authorization at all (a legacy unrestricted token); it reports an empty list rather than "every capability there is", since there is no finite list of everything an unrestricted token can do to render here.

CreatedOrganizationApiTokenResponse

The only response that ever carries the secret.
secret
​string · required

The full credential, shown exactly once. Only a digest of its secret half is stored, so no later request can return this value again.

​OrganizationApiTokenResponse · required

One organization API token. Never carries the secret. The secret exists in exactly one response, [CreatedOrganizationApiTokenResponse], and only its digest is stored, so nothing can put it back here later.

CredentialResponse

An external credential, without its secret.
created_at
​string · date-time · required
expires_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
created_by
​string | null · uuid

The person who made the credential. null once that account is deleted.

created_by_name
​string | null

Their display name, null when created_by is or the account cannot be read.

last_used_at
​string | null · date-time

CurrentSessionResponse

The caller's own identity, returned only when the bearer token names a valid, current session -- so a successful call doubles as "am I signed in".
​AuthUserResponse · required

Minimal caller identity carried on a session payload.

Deliberately not the full /console/me shape (kernel_http_v2_identity::contracts::me::MeResponse): /v2 feature crates do not depend on one another (see crates/http/CLAUDE.md), so this is a small, independent projection sized for what a session response needs, not a shared type.

CursorExpiredResponse

The body of `410 cursor_expired`: where to resume.
description
​string · required
code
​string · required
oldest_retained_seq
​integer · int64 · required

The oldest seq still kept.

resume_after
​integer · int64 · required

Send this as after to read from the oldest kept event. Events before it were swept: re-read the unit list to resynchronise.

CustomDomainAvailabilityResponse

Whether a hostname could be attached right now (v1's `/custom-domain/availability`).
status
​string · required

available, taken, invalid, platform_zone or not_verified. A string on the wire, like status above, so a new state does not break a generated client.

CustomDomainResponse

One hostname attached to this service.
hostname
​string · required

Normalized: lowercase, no trailing dot.

id
​string · uuid · required

The id DELETE /service-instances/{id}/custom-domains/{route_id} names. It is the ingress resource's own id, so detaching addresses the exact row that was created rather than the hostname string, which a customer could otherwise re-send in a different case or with a trailing dot and miss.

status
​string · required

pending, active or failed.

pending is not a synonym for broken. It is the ordinary state between accepting the name and cert-manager completing an HTTP-01 challenge, and that challenge cannot complete until the customer's own DNS points here -- so a client should render it as "waiting on your DNS", never as an error.

url
​string · required

https://{hostname}, assembled here so every client renders the same link rather than each concatenating its own.

CustomDomainsResponse

Everything the domains panel renders for one service.
​CustomDomainResponse[] · required
limit
​integer · uint · min: 0 · required

How many hostnames this service may hold at once, so a panel can disable its own add control instead of discovering the ceiling at a refusal.

platform_host
​string | null

The free platform hostname this service always answers on, or null when it publishes no HTTP ingress at all.

Also null when the instance's cluster could not be resolved, which is a different cause with the same rendering on purpose: the listing of attached domains is correct either way, and failing the whole read because a placement lookup faulted would blind a customer to the names they have already attached.

platform_url
​string | null

https://{platform_host}, present exactly when platform_host is. The shared wildcard certificate covers every platform host, so unlike a CustomDomainResponse.url this one is never waiting on a certificate to issue -- a client can render it as a ready link (copy and open-in-new-tab) the moment platform_host is non-null.

DatabaseEngineDto

string · enum
Enum values:
postgres
mariadb

[DatabaseEngine] on the wire.

DatabaseNameDto

[`SetupStepId::DatabaseName`](kernel_core::domain::setup_steps::SetupStepId::DatabaseName)'s answer (V-0888), shared between request and response. Blank means the product's default name.
name
​string
Default:

DatabaseNameParamsDto

[`SetupStepId::DatabaseName`](kernel_core::domain::setup_steps::SetupStepId::DatabaseName)'s declared params, mirroring [`DatabaseNameParams`] by hand, plus the engine's longest legal name so a form can bound its input.
engine
​DatabaseEngineDto · enum · required

[DatabaseEngine] on the wire.

Enum values:
postgres
mariadb
max_length
​integer · uint · min: 0 · required
default
​string | null

DatabaseSqlResultResponse

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

DatabaseTableResponse

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

DatabaseTableRowsResponse

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

DatabaseTablesResponse

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

DatabaseUsageResponse

​DatabaseUsageView · required

How full a database is.

database
​string · required

DatabaseUsageView

How full a database is.
data_length
​integer · uint64 · min: 0 · required
index_length
​integer · uint64 · min: 0 · required
used_bytes
​integer · uint64 · min: 0 · required

Data plus index bytes, as the engine reports them. Cached for up to a minute and refreshed by any write through this API.

over_quota_since
​string | null · date-time

Set while the quota enforcement task has the database locked read-only for being over its quota.

quota_bytes
​integer | null · uint64 · min: 0

The storage quota in bytes, or null for a database that has none (a dedicated database is sized by its own volume).

DatabaseWriteResultResponse

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

DedicatedIpNetworkProtectionModeResponse

The dedicated IP assignment's protection mode after a change. Names the assignment rather than the instance because one instance's exclusive IP is one assignment row, and the id is what a subsequent read of the assignment keys on.
assignment_id
​string · uuid · required
network_protection_mode
​string · required

DeleteSharedDatabaseRowRequest

primary_key_value
​required

Arbitrary JSON.

DeploymentActor

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, user_id
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
Somebody acting through the customer-facing console, identified by their user id. Safe to name: the console surface is reachable only by a caller the capability guard already proved may manage this instance, so this id belongs to the customer's own organization. It is what lets a panel say which teammate deployed.
kind
​string · const · required
Const value: customer
user_id
​string · uuid · required

DeploymentCapabilitiesResponse

What this deployment runs. A partner hosting their own kernel may leave any of these unconfigured; a `false` means the capability is absent here: its routes answer `501 capability-not-available`, its products are not listed, and a client must not offer its UI.
container_registry
​boolean · required

Customer container registries. Registry products are not listed when false.

domain_connect
​boolean · required

Domain Connect one-click DNS setup.

mail
​boolean · required

Transactional email. Without it no mail is ever sent (verification, password reset and notifications included).

mollie
​boolean · required

Mollie card checkout for invoices.

nowpayments
​boolean · required

NOWPayments crypto checkout.

object_storage_buckets
​boolean · required

Customer object-storage buckets. Bucket products are not listed when false.

paypal
​boolean · required

PayPal checkout (wallet top-ups and invoice payment).

unit_log_store
​boolean · required

Stored unit log history. Live tail is unaffected.

unit_metrics_store
​boolean · required

Stored unit metrics history.

warden
​boolean · required

The Warden edge protection listener.

web_push
​boolean · required

Web Push notifications.

DeploymentFailureReason

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: not_configured
The service has not been told what to run yet, or the configuration that reached the rollout was not usable as one. `ProvisionOrderError::ServiceConfigMissing`. The customer-facing path already refuses this before a lease is even taken (`start_deployment_handler`'s `awaiting_source` check), so this arm is reachable only if the two checks ever disagree -- kept rather than folded into [`Unknown`](Self::Unknown) because, if it does fire, the cause is still exactly nameable.

DeploymentOutcome

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: succeeded
The rollout was applied. Not a claim that the pods are healthy now: that is a live question, and `ListDeploymentsResponse::workloads` answers it.

DeploymentResponse

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.

DeploymentStatus

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: running
Still holding its lease. The request that started it has not returned, or the process that made it is renewing.

DeploymentTrigger

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind
type = object · requires: kind, summary
Properties for Variant 1:
A person asked for what is currently saved to be put into service. The actor is on the [`Deployment`] rather than in here, because it is the same question for every arm that has one.
kind
​string · const · required
Const value: manual

DesiredStateResponse

string · enum
Enum values:
running
stopped

DesiredUnitsPointResponse

How many units the platform wanted at the end of one bucket.
at
​string · date-time · required

The bucket's start.

units
​integer | null · int32

null when the service's history does not say.

DirectoryListingResponse

One directory.
​FileEntryResponse[] · required

Unordered. Sorting is the panel's, per FilesTransport.

path
​string · required

The normalized path that was listed, not the one that was typed, so a breadcrumb reflects what kernel actually read. A customer pasting an SFTP-shaped path (/server/config) gets back the mount-relative one (/config).

truncated
​boolean · required

True when the directory holds more entries than one response carries. A flag rather than a cursor: there is no stable ordering to page over (the sidecar returns whatever read_dir yields), so a cursor would promise a consistency the backend cannot keep.

DiscoverDomainConnectRequest

domain
​string · required

The customer's registrable domain, e.g. example.com.

host
​string · required

The subdomain label (e.g. play), or empty to point the apex at the server.

DnsInstructionResponse

A DNS record to create at the customer's DNS provider.
name
​string · required

The domain the record is created on.

record_type
​string · required

CNAME for a subdomain; ALIAS for a bare domain (example.com), which cannot hold a CNAME: use the provider's ALIAS, ANAME or CNAME-flattening record to the same target.

target
​string · required

What the record points at: the endpoint's free host.

DomainConnectLinkResponse

One Domain Connect link.
created_at
​string · date-time · required
domain
​string · required

The customer's registrable domain, e.g. example.com.

host
​string · required

The subdomain label, or empty for the apex.

hostname
​string · required

What players connect to: {host}.{domain}, or {domain} for the apex.

id
​string · uuid · required
status
​string · required

pending, active or failed. pending is the ordinary state until the customer approves the change at their DNS provider and the records propagate; render it as waiting, not as an error.

apply_url
​string | null

The browser URL that applies the template at the customer's DNS provider, or null when Domain Connect is not configured on this platform.

dns_provider_display_name
​string | null

The detected DNS provider's display name, when it publishes one.

last_error
​string | null
last_verified_at
​string | null · date-time
template_confirmed
​boolean | null

Whether the DNS provider recognizes our template: true, false, or null when the provider did not answer consistently.

DomainConnectResponse

​DomainConnectLinkResponse[] · required
supported
​boolean · required

Whether this service's product offers Domain Connect. A client hides the section when false.

DomainFailureResponse

Why a domain failed.
code
​string · required
message
​string · required

DomainVerificationResponse

What the organization must publish, and the state of that proof so far. Returned by `GET /organizations/{id}/domains`, `POST /organizations/{id}/domains` (creating or re-reading the challenge), and `POST .../domains/{domain}/check` (after running it). Deliberately carries no `id`: the resource this API exposes is keyed by `(organization, domain)`, never by a bare row id a caller could try against a different organization's scope. Mirrors the fields the design doc's Domains-tab table needs (Domain, Status, Detail, Last checked) plus the record itself, plus the three fields the four-state machine added that no consumer could render at all before this: `first_verified_at` and `lost_since` are what state 6 ("Lost, inside grace" -- "will stop being served on 18 September") is computed from, and `grandfathered_until` is the date the "four live production domains" migration note says the tab must show its affected customers. `grandfathered_until` is populated only by that one migration and is genuinely a customer-facing deadline (not an internal implementation detail), which is why it is here rather than withheld: withholding it would leave console unable to render the exact screen the design section that created it requires. There is no `created_at`/`updated_at` here: a freshly issued challenge cannot honestly report either.
domain
​string · required
record_name
​string · required
record_type
​string · required

Always TXT. Present so a client never has to hardcode the record type it renders next to record_name/record_value.

record_value
​string · required
status
​string · required

pending, verified, lost, or failed.

first_verified_at
​string | null · date-time

The first time this row was ever proven, set once and never moved again even through a later Lost/Verified cycle. What a "Verified on 1 March" message (design doc state 4/5) reads from; distinct from verified_at, which keeps advancing on every successful recheck.

grandfathered_until
​string | null · date-time

A dated exemption for one of the handful of production domains that were serving before this feature existed (design doc, "The four live production domains"). Some only for those rows, and only until the date itself: after it, an unproven domain in this state stops being served. Absent for every row created through the ordinary start flow.

last_checked_at
​string | null · date-time
last_error
​string | null

Kernel's own sentence for the most recent refusal, verbatim, per the design doc's "what kernel returns" (kernel writes the sentence, console renders it as-is).

lost_since
​string | null · date-time

When a Verified row's proof was most recently found gone or replaced. Some only while status is lost. Combined with the domain's fourteen-day grace period, this is what lets a client compute the exact date state 6 names ("will stop being served on lost_since + 14 days").

verified_at
​string | null · date-time

DownloadFormatParam

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: text
One line of text per log line.

DownloadUrlRequest

Which file to mint a signed download URL for. A body, not `?path=`, for the same reason `CreateDirectoryRequest` is: this `POST` has no other reason to carry one.
path
​string · required

DownloadUrlResponse

A signed hand-off to the region's own cluster edge gateway for the one file that was asked for (ZC-1225). This used to be `{ url, expires_at }`, a path on kernel's own `/api/v2/public` mount that kernel itself redeemed -- see `controllers/capabilities/files.rs`'s module header for why that hairpinned every download through kernel's own process regardless of which region the customer or the service were actually in. `base_url` is the cluster edge gateway's own address (e.g. `https://edge.mad.zaroz.cloud`) and `token` is the signed `edge_token::Resource::Sidecar` ticket, confined by its `root` to this exact file; the client opens the download directly against the gateway, and kernel's own process never touches the bytes. `expires_at` is the same instant the signature itself expires at, not a second, independently-computed deadline (`edge_token::sign_with_expiry` returns the one value used for both).
base_url
​string · required
expires_at
​string · date-time · required
token
​string · required

EditTicketMessageRequest

Body for `PATCH /api/v2/support/tickets/{id}/messages/{message_id}`.
body
​string · required

EffectiveRoutingResponse

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=.

ElevationMethod

string · enum
Enum values:
pin
webauthn
totp

Which admin-elevation module a credential belongs to, or a verification attempt is checked against. AdminElevationModule gives each method (PIN, WebAuthn, TOTP) a home behind this same enum without touching every caller that only cares about routing by name. Serializable so callers of /auth/sudo/* can pick a method by name instead of the HTTP layer assuming PIN.

EndpointDomainResponse

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.

EndpointKind

string · enum
Enum values:
port
https
unit

What an endpoint is (decision-0308). A Container offers two, and a customer chooses by what they run: a website or API, or anything else.

  • port: a raw TCP/UDP public port through Warden. Priced, metered, backed by a port allocation.
  • https: one container port behind Traefik, on a free platform host plus the customer's verified domains, with automatic TLS. Free, never metered, holds no allocation. Carries TCP only (WebSockets included); TLS ends at Traefik.
  • unit: a raw port like port, but one per unit slot, reaching exactly that unit's pod (Units phase 1, S13). One allocation per slot, held while the slot exists, so the address survives pod restarts and is released on scale-down. Priced and metered per port exactly as port is.

EndpointOriginResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: purchase
Asked for when the service was bought.

EndpointProtocols

string · enum
Enum values:
tcp
udp
tcp_udp

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

EndpointRequestBody

One endpoint a purchase asks for: a raw public port (the default) or an HTTPS endpoint.
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 once the service runs. Each must be one the organization has verified or a subdomain of one, exactly as POST .../endpoints requires; refused here with invalid_domain, platform_domain or domain_not_verified. At most 10 across the whole purchase (domain_limit).

Default: []
kind
​string · enum

port (the default, so a request written before kinds existed means what it always did) is a raw TCP/UDP public port: a shared address and an assigned public port, billed by the hour held. https is a website or API behind Traefik: a free platform host, the customer's own verified domains (added once the service runs), and automatic TLS, free and never metered; the app serves plain HTTP.

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; refused with invalid_host_label otherwise. Omitted, the host is named after the endpoint's id. Same rule as POST .../endpoints.

Default: null
​

For port: tcp, udp, or tcp_udp for one public number on both; required. For https: omit it (it is always TCP); tcp is accepted.

Default: null

EndpointResponse

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.

EndpointStateResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: pending
Asked for at purchase and not open yet; `pending_reason` says why. Nothing is billed while a port is pending.

EnvironmentApplies

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: immediately
The running process sees the new value with no further act.

EnvironmentResponse

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.

EnvironmentVariableResponse

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.

ExecPtySessionTicketResponse

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

The container the shell will open in.

expires_at
​string · date-time · required

When the gateway stops accepting this ticket. A redemption deadline: the ticket is single use and the open session is bounded by the gateway's own idle and maximum-length timeouts, not by this.

shells
​string[] · required

The shells the gateway tries, in order, until one starts.

url
​string · required

The gateway URL to open a WebSocket against, carrying the signed ticket in its query string. Negotiate the zaroz-pty-v1 subprotocol and append columns and rows.

ExecuteSharedDatabaseSqlRequest

sql
​string · required

ExternalAccessLiveResponse

Live protection state for a service instance's protection-managed allocations. Deliberately partial. Every entry is a state a node reported; a protection-managed allocation with no entry has no current state, and the client renders that as unavailable rather than as normal. `degraded` says a node serving this instance did not answer in time or could not be resolved, which is expected during an attack on the serving relay and is not an error.
​AllocationProtectionStateResponse[] · required
degraded
​boolean · required

ExternalAccessResponse

​PortAllocationResponse[] · required
extra_slots
​integer · int32 · required

Extra slots staff granted this service instance on top of free_slots (0 without a grant). free_slots + extra_slots is the ceiling a new port request on a shared pool is refused at.

free_slots
​integer · int32 · required
http_ingress_ips
​string[] · required
is_dedicated_ip
​boolean · required

True when this instance's ports are served from an exclusive (dedicated) IP, where the requested port becomes the public port. On a shared pool the external port is drawn at random and cannot be chosen.

used
​integer · uint · min: 0 · required

FileContentResponse

One file's text.
content
​string · required

UTF-8 text. A file that is not valid UTF-8 is refused rather than lossily converted, because the transport's write takes a string and a lossy read followed by a save would rewrite the customer's binary as replacement characters.

path
​string · required
size_bytes
​integer · uint64 · min: 0 · required

Bytes of content, which is what was actually read rather than what the directory entry claimed a moment earlier.

FileEntryResponse

One entry in a listing.
kind
​FileKindResponse · enum · required

What an entry is. Two values, closed: a filesystem entry the browser can draw is a file or a directory, and the sidecar refuses to describe anything else (entry_for_path answers invalid_argument for a socket or a device).

Enum values:
file
directory
name
​string · required
path
​string · required

Absolute within the mount, always starting with /. This is the value to hand back to any other route on this capability: it is the sidecar's own answer about where the entry lives rather than a path the client rebuilt from name, so following it addresses the file kernel listed.

modified_at
​string | null · date-time

null when the backend reported none. Rendered, never parsed for meaning.

size_bytes
​integer | null · uint64 · min: 0

null for a directory. The sidecar reports zero for one, and a rendered zero reads as "this folder is empty", which is a claim kernel never made.

FileKindResponse

string · enum
Enum values:
file
directory

What an entry is. Two values, closed: a filesystem entry the browser can draw is a file or a directory, and the sidecar refuses to describe anything else (entry_for_path answers invalid_argument for a socket or a device).

FilesSessionTicketResponse

A signed hand-off to the region's own cluster edge gateway for a browse session across the whole volume (ZC-1225). The `/v2` re-keying of `/v1`'s `fs/edge-url` (`crates/http/workloads/src/controllers/fs.rs` on `dev`): `base_url` plus `token` is the identical two-field shape that route returned, so a client that already knows how to call cluster-agent's `/fs/*` surface with a `{base_url, token}` pair needs no new response parser. Unlike the download ticket above, this one's `token` is confined by `root` to the whole volume, not one file, because a browse session makes many `/fs/*` calls against the one ticket it was handed.
base_url
​string · required
expires_at
​string · date-time · required
token
​string · required

FirewallAction

string · enum
Enum values:
allow
block

FirewallDirection

string · enum
Enum values:
ingress
egress

FirewallProtocol

string · enum
Enum values:
tcp
udp
icmp
any

FirewallRuleResponse

One saved rule.
action
​FirewallAction · enum · required
Enum values:
allow
block
created_at
​string · date-time · required
direction
​FirewallDirection · enum · required
Enum values:
ingress
egress
enabled
​boolean · required
id
​string · uuid · required
priority
​integer · uint16 · min: 0 · max: 65535 · required
protocol
​FirewallProtocol · enum · required
Enum values:
tcp
udp
icmp
any
resource_id
​string · uuid · required

The machine this rule filters traffic to. /v1 calls this provision_id; see this module's header.

updated_at
​string · date-time · required
description
​string | null
cidr
​string | null

Rendered back as it is stored, canonicalised by ipnetwork, so a caller who wrote 10.0.0.5/8 reads 10.0.0.0/8 and can see what the rule will actually match.

port_from
​integer | null · uint16 · min: 0 · max: 65535
port_to
​integer | null · uint16 · min: 0 · max: 65535

FirewallRulesResponse

The whole ruleset for one machine, in the order it is evaluated. A wrapper object rather than a bare array, unlike `/v1`'s listing. A top-level array cannot grow a field without breaking every generated client, and this one already has something to say beyond the rows: `max_rules_per_direction` is the limit a `POST` will refuse against, and a panel that does not know it can only discover the ceiling by hitting it.
max_rules_per_direction
​integer · uint32 · min: 0 · required

How many rules one direction may hold. Kernel's own constant, published rather than duplicated as a frontend literal.

​FirewallRuleResponse[] · required

FivemArtifactChannelDto

string · enum
Enum values:
recommended
optional
latest
custom

Mirrors [FiveMArtifactChannel] by hand, on this module's own discipline (the domain type carries no JsonSchema).

FivemLiveResponse

​FivemPlayerResponse[] · required

Empty unless status is ok.

​FivemLiveStatus · required
​

Present only when status is ok.

FivemLiveStatus

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
ok

FivemPlayerResponse

One connected player. Never carries the player's IP or identifiers.
id
​integer · uint32 · min: 0 · required
name
​string · required
ping
​integer · uint32 · min: 0 · required

Milliseconds; 0 means the server did not report one.

FivemServerDto

[`SetupStepId::FivemServer`](kernel_core::domain::setup_steps::SetupStepId::FivemServer)'s answers, shared between request and response (nothing secret, nothing frozen server-side beyond normalization of the custom build id).
artifact_channel
​FivemArtifactChannelDto · enum · required

Mirrors [FiveMArtifactChannel] by hand, on this module's own discipline (the domain type carries no JsonSchema).

Enum values:
recommended
optional
latest
custom
default_chat_enabled
​boolean · required
custom_artifact_version
​string | null

Exact {build}-{40 hex} id; required for custom, dropped otherwise.

Default: null

FivemServerInfoResponse

What `dynamic.json` says, colour codes already stripped.
clients
​integer · uint32 · min: 0 · required

Players connected now.

gametype
​string · required
hostname
​string · required
mapname
​string · required
max_clients
​integer | null · uint32 · min: 0

The server's slot count, when it publishes one.

FivemServerParamsDto

[`SetupStepId::FivemServer`](kernel_core::domain::setup_steps::SetupStepId::FivemServer)'s declared params, mirroring [`FivemServerParams`] by hand.
artifact_channel
​boolean · required

Whether this product offers a channel / custom build choice. false for FiveM Enhanced, whose build is pinned in the image.

ForgotPasswordRequest

Requests a password reset link for an address the caller claims to own. See `controllers::password_recovery::forgot_handler` for the refusal rule: this operation answers identically for a registered and an unregistered address, which is the whole reason it takes an address in a body and returns no information about it.
email
​string · required
origin
​string | null

The origin the emailed reset link should point back at (ZC-1425). Same shape and validation as [PasswordSignupRequest::origin], including the optional app mount path (https://zaroz.cloud/gaming). Unlike the address above, a disallowed origin here is NOT hidden behind the identical-response refusal: it is not account-existence information, so forgot_handler refuses it with a real 4xx.

FramebufferHoldRequest

Body of `POST /api/v2/console/service-instances/{id}/resources/{resource_id}/framebuffer/lock`.
session_id
​string · required

The session_id the ticket response minted. A session that no longer holds the screen gets held: false rather than an error.

FramebufferHoldResponse

`POST /api/v2/console/service-instances/{id}/resources/{resource_id}/framebuffer/lock`. A keep-alive, so losing the screen is a normal 200 carrying `held: false` rather than an error. `zaroz-ui`'s `FramebufferHold` is `{ held: true } | { held: false; holder?: string }` for the same reason: a panel that has been taken over surrenders the screen, it does not report a failure to the customer who is no longer using it.
held
​boolean · required
expires_at
​string | null · date-time

When this hold lapses unless refreshed again. Absent when the hold was lost, because there is nothing left to refresh.

holder_name
​string | null

Who holds it instead, when the hold was lost. Absent when it was kept, and absent when nothing holds it at all.

FramebufferReleaseResponse

`DELETE /api/v2/console/service-instances/{id}/resources/{resource_id}/framebuffer/lock`.
released
​boolean · required

False when the session named no longer held the screen, which is the ordinary outcome of unmounting after being taken over. Not an error: a page being torn down is not a place to report one.

FramebufferSessionConflictResponse

The `409` body when somebody else holds the screen.
description
​string · required
code
​string · required

Matches the shape of the shared error envelope so a generic client error handler still reads it.

holder_name
​string · required

Rendered as given. zaroz-ui puts this straight on screen and does not word a sentence around it.

holder_user_id
​string · uuid · required
updated_at
​string · date-time · required

When the holder last refreshed. A client can use it to say how stale the hold is before offering to take over.

FramebufferSessionTicketResponse

`POST /api/v2/console/service-instances/{id}/resources/{resource_id}/framebuffer/session-ticket`.
expires_at
​string · date-time · required

When the gateway stops accepting this ticket.

A redemption deadline and not a session deadline: cluster-agent verifies exp once at upgrade (gateway/vnc.rs:12) and never again, so an attached screen outlives its ticket.

hold_expires_at
​string · date-time · required

When the exclusive hold lapses unless refreshed.

protocols
​string[] · required

Subprotocols the socket must request. See [FRAMEBUFFER_SUBPROTOCOL].

session_id
​string · required

The handle for this exclusive session.

Minted here rather than supplied by the client, unlike /v1's sessionId query parameter. The client cannot then name somebody else's session, and taking a screen over is spelled force rather than spelled "guess the holder's identifier and refresh it".

Pass it to the hold and release routes. It is not a credential: holding this screen still requires passing the same authorization the mint did.

url
​string · required

The gateway URL to open a WebSocket against, carrying the signed ticket in its query string.

FriendlyError

description
​string · required
code
​string · required
details
​string | null

GameServerDto

[`SetupStepId::GameServer`](kernel_core::domain::setup_steps::SetupStepId::GameServer)'s answers (V-0703), shared between request and response. `null` (or absent) for a setting the product does not ask, or to take its default.
description
​string | null
Default: null
crossplay
​boolean | null
Default: null
disable_anti_cheat
​boolean | null
Default: null
expansion
​boolean | null
Default: null
fps_limit
​integer | null · uint32 · min: 0
Default: null
map
​string | null
Default: null
max_players
​integer | null · uint32 · min: 0
Default: null
mode
​string | null
Default: null
public
​boolean | null
Default: null
seed
​integer | null · uint32 · min: 0
Default: null
server_name
​string | null
Default: null
update_mods_on_start
​boolean | null
Default: null
update_on_restart
​boolean | null
Default: null
world_name
​string | null
Default: null
world_size
​integer | null · uint32 · min: 0
Default: null

GameServerParamsDto

[`SetupStepId::GameServer`](kernel_core::domain::setup_steps::SetupStepId::GameServer)'s declared params (V-0703), mirroring [`GameServerParams`] by hand. A `null` setting is one the product does not ask.
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​

GameSubdomainAvailabilityResponse

status
​GameSubdomainAvailabilityStatus · enum · required
Enum values:
available
taken
invalid
zone_unavailable
​

Set when status is invalid.

GameSubdomainAvailabilityStatus

string · enum
Enum values:
available
taken
invalid
zone_unavailable

GameSubdomainInvalidReason

string · enum
Enum values:
empty
too_short
too_long
invalid_chars
reserved

GameSubdomainRequest

Create a subdomain, or rename one.
parent_domain_id
​string · uuid · required
subdomain
​string · required

3-32 characters: a-z, 0-9 and inner hyphens. Normalized to lowercase.

GameSubdomainResponse

A game subdomain: `<subdomain>.<zone>` pointing at this service's public address.
created_at
​string · date-time · required
has_srv_record
​boolean · required

Whether an SRV record carries the port, so players can connect with the hostname alone. false means they must add :record_port.

hostname
​string · required

<subdomain>.<parent_domain_name>, what players connect to.

id
​string · uuid · required

Changes on a rename: a rename replaces the subdomain.

parent_domain_id
​string · uuid · required
parent_domain_name
​string · required
​GameSubdomainStatusResponse · required

Where a game subdomain's DNS records are.

subdomain
​string · required
last_synced_at
​string | null · date-time
record_ip
​string | null

The address the records point at, once they were pushed.

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

The port players need when there is no SRV record carrying it.

GameSubdomainStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: pending
Accepted; the records are being created.

GameSubdomainZoneResponse

A platform zone a customer may take a game subdomain under.
id
​string · uuid · required
name
​string · required

GameSubdomainsResponse

Everything the game-subdomain section renders for one service.
limit
​integer · uint · min: 0 · required

How many subdomains one service may hold (1), so a client can hide its add control.

​GameSubdomainResponse[] · required
​GameSubdomainZoneResponse[] · required

The zones a new subdomain may be taken under. Empty when none is enabled.

GrantCapabilityDto

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: units.read
Read the target's units (and, later, its event stream).

GrantDto

One grant: a service of the same project and what the principal may do to it.
​GrantCapabilityDto · required

What a grant lets the principal do to its target service. Mirrors the domain's closed vocabulary; deliberately not an API token capability.

target_service_instance_id
​string · uuid · required

GrantableOrganizationApiTokenCapabilityResponse

One capability the caller may put in a token of this organization.
​ApiTokenCapability · required

Closed, policy-owned action catalog. Capabilities are exact: there is deliberately no prefix or glob matching, and unknown wire values are rejected during deserialization. PartialOrd/Ord exist so a capability set can be a BTreeSet, which is what [crate::domain::auth::application_token::ApplicationToken] stores. The ordering is the declaration order and is not a wire contract: nothing serializes it, and as_str remains the only stable representation.

GrantableServiceResponse

One service the person may grant the principal access to.
name
​string · required

The service's name, as the project lists it.

service_instance_id
​string · uuid · required

GuestReadinessKindResponse

string · enum
Enum values:
windows_vps

GuestReadinessResponse

kind
​GuestReadinessKindResponse · enum · required
Enum values:
windows_vps
ready
​boolean · required
status
​string · required

The ticket's own state machine, as a string. Diagnostic rather than something to branch on: ready is the decision.

completed_at
​string | null · date-time

HostStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: pending
Accepted and applied; the certificate (for a customer domain) is not issued yet, so a browser reaching the name may get a TLS error for a while.

HttpExposureResponse

domain
​string · required
status
​string · required
url
​string · required

HttpsDetailsResponse

Where an https endpoint answers.
​EndpointDomainResponse[] · required

The customer's own domains on this endpoint, each with its certificate state.

​HostStatusResponse · required

How far a host or a domain is.

host
​string | null

The free platform host, <label>.zone.<zone>.zaroz.cloud, covered by the platform's wildcard certificate. Absent once the endpoint is released.

url
​string | null

https:// plus host.

IdleAction

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: stop
Drain and stop: the unit keeps its key, labels and user data.

IfNone

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: fail
Refuse with `no_warm_unit`.

ImportSharedDatabaseTransferRequest

`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

InFlightOperationResponse

One in-flight `service_operation_leases` row, projected for a status consumer. See [`kernel_core::domain::service_operations::in_flight_operation`] for the selection rule, including how an expired-but-unreclaimed lease (the holder crashed and nothing has reclaimed the row) is excluded rather than read as a permanent "still deploying", and how two concurrent leases (only possible for two backups today) are resolved to one.
kind
​string · required

The operation's stable snake_case spelling (deploy, restart, reprovision, ...its ServiceOperationKind::to_string()). Plain text rather than a dedicated wire enum, matching v2-admin's ServiceOperationLockResponse::operation for the same lease, so an operator comparing the two never reads two different vocabularies for one fact. observe never appears here: it takes no lease.

started_at
​string · date-time · required

When kernel acquired the lease. Not an estimate of when it will finish -- nothing here knows how long an operation of this kind usually takes -- but enough for a consumer to render an elapsed timer ("deploying, 40s") instead of a bare "deploying".

InsertSharedDatabaseRowRequest

​object · required

InvoiceCardCheckoutResponse

Where to send the customer's browser to pay. One shape for both providers. `/v1` answered Mollie with `checkout_url` and PayPal with `approve_url` for the same idea, so every client carried a branch that existed only to read a differently named field.
checkout_url
​string · required

The provider-hosted page the customer completes the payment on. Open it in the browser; the invoice is paid when the provider settles, not when this URL is issued.

InvoiceKindResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
initial
resize
renewal
admin_resize
trial_purchase

InvoiceLineKindResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
base_subscription
carried_proration
resize_proration
force_migrate_proration

InvoiceLineResponse

One line of a multi-line invoice (Program A2-S10, `invoice_line_items`). This is the persisted charge line -- what the invoice actually billed, each with its own snapshotted tax and service period (design doc `§10.2`/ T4: no allocation from an invoice-level total, each line resolves its own tax at issue time). It is a different thing from a catalog `PriceLine` (`dimension` / `unit` / `measurement` / `tiers`), which is the pricing *rule* a Price is built from, not a persisted invoice record -- `invoice_line_items` carries no such columns, so this response type does not invent them. See this crate's report.
description
​string · required
amount_cents
​integer · int64 · required
currency
​string · required
id
​string · uuid · required
​InvoiceLineKindResponse · required

Mirrors InvoiceLineItemKind 1:1, as its own /v2 response type so the wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here (same convention as kernel_http_v2_catalog::contracts::products::ProductKindResponse).

service_ends_at
​string · date-time · required
service_starts_at
​string · date-time · required
tax_amount_cents
​integer · int64 · required
project_id
​string | null · uuid

The project this line was billed for, resolved server-side through the line's subscription (subscriptions.project_id is NOT NULL).

Joined once for the whole invoice rather than left to the client: a client holding only subscription_id would need a call per line to recover it, turning one invoice read into N.

null -- together with project_name -- for a line with no subscription_id, and for the (schema-permitted, expected-impossible) case of a line whose subscription belongs to another organization, which the join refuses to resolve rather than naming a project the caller may not see. Both are absences, not errors: the line still carries its description, amount, tax and service period.

project_name
​string | null

The project's current name, read live at response time rather than snapshotted onto the line. Renaming a project moves every one of its invoice lines with it -- this is navigation, not the frozen invoice content bill_to_* is (Program G §2.2).

Always null exactly when project_id is.

resource_id
​string | null · uuid
subscription_id
​string | null · uuid

InvoicePaymentResponse

What a successful payment produces: the ledger entry the money moved through, so a client can link straight to it rather than paging the wallet to find what it just did.
entry_id
​string · uuid · required

InvoiceResponse

One invoice with its line items and its frozen billing-identity snapshot. Program A2-S10 made invoices genuinely multi-line and organization-owned; `/v1`'s `InvoiceView` has never had a line-items field and, per the `/v1` freeze, never will (design doc `2026-08-26-v1-api-surface-changes.md` `§7`). This is that route. The `bill_to_*` fields are Program G `§2.2`'s frozen recipient snapshot, taken once at issue and never re-derived from a live billing identity -- last year's invoice keeps showing last year's address even after the organization's billing profile changes. All four are `None` for a `Simplified`-series invoice (which must structurally carry no personal data, see `Invoice::validate`) and for every invoice issued before Program G.
amount_cents
​integer · int64 · required
created_at
​string · date-time · required
currency
​string · required
id
​string · uuid · required
is_synthetic
​boolean · required
​InvoiceKindResponse · required
​InvoiceLineResponse[] · required
status
​InvoiceStatusResponse · enum · required

An invoice's status on the /v2 wire.

Deserialize as well as Serialize, because this is also the closed vocabulary of listOrganizationInvoices's status filter. Deliberately one type rather than a separate *Filter enum: the filter's accepted values and the values a row can come back carrying must be the same set, and a second enum is a second place for them to disagree. A generated client sees one schema for both.

Enum values:
pending
paid
failed
refunded
cancelled
tax_amount_cents
​integer · int64 · required
total_amount_cents
​integer · int64 · required
bill_to_address
​string | null
bill_to_country
​string | null
bill_to_name
​string | null
bill_to_tax_id
​string | null
due_at
​string | null · date-time

When an unpaid invoice must be paid by. Identical in meaning and derivation to InvoiceSummaryResponse::due_at -- see that field, including its postpaid settlement exception.

failed_at
​string | null · date-time
invoice_number
​string | null
​
paid_at
​string | null · date-time
refunded_at
​string | null · date-time
tax_label
​string | null
tax_rate
​number | null · double

The tax rate applied at issue, e.g. 0.21 for 21%. None when no tax rate was resolved for this invoice. Exactly the stored rate (numeric(6,4)), never a blurred one: a rate the JSON number cannot carry exactly fails the response instead (V-0699).

InvoiceSeriesResponse

string · enum
Enum values:
simplified
full

Program G §1: the two invoice series, SIMP (simplified, structurally carries no personal data) and FULL (identity-bearing). See InvoiceResponse::bill_to_name and friends for the snapshot this series gates.

InvoiceStatusResponse

string · enum
Enum values:
pending
paid
failed
refunded
cancelled

An invoice's status on the /v2 wire.

Deserialize as well as Serialize, because this is also the closed vocabulary of listOrganizationInvoices's status filter. Deliberately one type rather than a separate *Filter enum: the filter's accepted values and the values a row can come back carrying must be the same set, and a second enum is a second place for them to disagree. A generated client sees one schema for both.

InvoiceSummaryResponse

One invoice, without its line items -- used by the list endpoint so a page of invoices does not pay for a lines query per row. See `InvoiceResponse` for the single-invoice shape that includes lines.
amount_cents
​integer · int64 · required

Money is integer cents on every /v2 DTO, never a float or a formatted string -- /v1's InvoiceView renders format!("{:.2}", amount) decimal strings instead, which this deliberately does not carry forward (see this crate's report).

created_at
​string · date-time · required
currency
​string · required
id
​string · uuid · required
is_synthetic
​boolean · required
​InvoiceKindResponse · required
organization_id
​string · uuid · required

The organization this invoice belongs to.

Carried even when the caller filtered to a single organization, because a row that cannot say who it belongs to forces the client to infer it from the request it happened to send. A client rendering an unfiltered listing has no other way to group or label rows at all, which is the gap that made /{org}/billing/invoices show every organization's invoices under one organization's heading.

status
​InvoiceStatusResponse · enum · required

An invoice's status on the /v2 wire.

Deserialize as well as Serialize, because this is also the closed vocabulary of listOrganizationInvoices's status filter. Deliberately one type rather than a separate *Filter enum: the filter's accepted values and the values a row can come back carrying must be the same set, and a second enum is a second place for them to disagree. A generated client sees one schema for both.

Enum values:
pending
paid
failed
refunded
cancelled
tax_amount_cents
​integer · int64 · required
total_amount_cents
​integer · int64 · required
due_at
​string | null · date-time

When an unpaid invoice must be paid by.

Derived, not stored: the invoice's created_at plus the grace period of the subscription it bills (subscriptions.grace_period_hours). That is the same deadline the invoice_due email quotes as its suspension date and the same one the billing task suspends on, so it is not a new promise -- it is the existing one, said on the wire.

null when the invoice bills no subscription at all (an admin or synthetic invoice), because nothing about such an invoice says when it is due and a default would be an invention. An invoice billing several subscriptions takes the earliest of their deadlines.

A postpaid settlement invoice (kind == "postpaid_settlement") is the exception: its due_at is the payment-terms date fixed when it was issued (issued + postpaid_payment_terms_days, V-0484), stored and never re-derived, so a later change of the terms does not move it. It is also the instant the debt ladder starts counting the invoice from.

With this present, status == "pending" and due_at in the past is readable as overdue. status == "pending" alone still is not.

failed_at
​string | null · date-time
invoice_number
​string | null

Program G §1: the display-ready number (e.g. FULL-2026-000001). None for every invoice issued before that programme -- never backfilled, see Invoice::invoice_number's own doc comment.

​
paid_at
​string | null · date-time
refunded_at
​string | null · date-time
tax_label
​string | null

The tax's name as printed on the invoice (e.g. VAT); None when none was resolved.

tax_rate
​number | null · double

The tax rate applied at issue, e.g. 0.21 for 21%; None when none was resolved. Same meaning and exactness as InvoiceResponse::tax_rate (V-0699).

IssueSshSessionRequest

`POST /api/v2/service-instances/{id}/resources/{resource_id}/ssh/sessions`. Every field is optional and none changes what is issued: the terminal size and correlation id are accepted so the dashboard's existing client can send what it already sends, and are bounded so they cannot be used to push arbitrary data through.
columns
​integer | null · uint32 · min: 0
Default: null
correlation_id
​string | null
Default: null
rows
​integer | null · uint32 · min: 0
Default: null

IssuedBucketKeyResponse

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

IssuedRegistryCredentialResponse

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

The password to log in with.

JavaRuntimeDto

[`SetupStepId::JavaRuntime`](kernel_core::domain::setup_steps::SetupStepId::JavaRuntime)'s answers.
use_optimization_flags
​boolean · required
​

null means "derive it from the Minecraft version" -- see [kernel_core::domain::setup_steps::StepAnswersDocument::template_answers].

jvm_options
​string | null
jvm_xx_options
​string | null

JavaVersionDto

string · enum
Enum values:
8
11
16
17
21
25

[JavaMajor], spelled the way minecraft.rs's java_major_export writes it ("8"|"11"|"16"|"17"|"21"|"25") -- the template-export vocabulary, not JavaMajor's own undecorated Serialize output ("J21").

LadderStopReasonResponse

string · enum
Enum values:
own_debt
restricted

own_debt: the service's own organization has an unpaid balance. restricted: the service is frozen because of a debt elsewhere; the reader should contact the organization's owner.

LadderStopResponse

Why the debt ladder stopped a service (V-0482). Carried by the service-instance reads as `ladder_stop`, present only while the ladder holds the service stopped. Never names the debtor organization or episode (Q121): `restricted` is the cascade case, where the debt is an owner's on ANOTHER organization, and the reader may not learn whose.
reason
​LadderStopReasonResponse · enum · required

own_debt: the service's own organization has an unpaid balance. restricted: the service is frozen because of a debt elsewhere; the reader should contact the organization's owner.

Enum values:
own_debt
restricted
since
​string · date-time · required

When the ladder stopped it.

LifecycleVerb

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: start
Bring it up.

LimitLevel

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: ok
Comfortably below.

LimitUsageResponse

One figure of the organization against its limit.
current
​integer · int64 · required

What the organization uses now.

​required

ok, near (80 percent of the limit or more) or at_limit (growth is refused).

limit
​integer · int64 · required

What staff allow it.

ListAuditResponse

​AuditEntryResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListCredentialsResponse

`GET .../principal/credentials`: the live (not revoked) external credentials, newest first, expired ones included.
​CredentialResponse[] · required

ListDeploymentsResponse

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.

ListEndpointsResponse

`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.

ListGrantableOrganizationApiTokenCapabilitiesResponse

max_lifetime_days
​integer | null · uint32 · min: 0

Longest lifetime kernel accepts for an organization token, in days. null means kernel enforces no maximum: only that expires_at is in the future, and omitting it means the token never expires.

ListGrantableServicesResponse

`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`.
​GrantableServiceResponse[] · required

ListInvoicesResponse

`/v2` DTOs are always named `*Request` / `*Response` (design doc `§43.4`) -- there is no `InvoiceView` here on purpose (`/v1`'s equivalent, `kernel_http_billing::controllers::invoices::InvoiceView`).
​InvoiceSummaryResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListMyApiTokenCapabilitiesResponse

​MyApiTokenCapabilityResponse[] · required

ListMyApiTokensResponse

​MyApiTokenResponse[] · required

ListMyApplicationDelegationsResponse

ListMyNotificationsResponse

​NotificationResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

unread_count
​integer · uint64 · min: 0 · required

The caller's TOTAL unread count, across every page -- not merely how many of the notifications in data are unread. The bell icon's badge needs the whole number regardless of which page happens to be open.

ListMyReferralLinksResponse

​MyReferralLinkResponse[] · required

ListMyServiceInstancesResponse

​PaginationMeta · required

Pagination metadata included in paginated responses

ListMySessionsResponse

​MySessionResponse[] · required

ListMyTicketsResponse

​TicketSummaryResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListOrganizationApiTokensResponse

​OrganizationApiTokenResponse[] · required

ListOrganizationWalletEntriesResponse

One page of one organization's ledger. The same `{ data, pagination }` envelope `ListInvoicesResponse` uses, and for the same reason: `/v1`'s `/me/wallet/entries` answers with a bare array paged on a timestamp cursor, with no total and no next cursor, so a client can render kernel's default page and has nothing to build a pager out of. **No row carries a running balance, and that is a decision, not an oversight.** `balance_after` is well-defined only against a total order over the whole ledger, folded from one end. This page is a window: it can be the second page, it can be narrowed to one currency, and a wallet holding two currencies has two independent balances that a mixed page interleaves. Any value computed from the rows in hand would be a running total of *this page*, which agrees with the wallet only when the page is the entire ledger -- so it would read as right and be wrong. Kernel could serve a true one (the order in `WalletRepository::list_entries_page` is total, which is the hard half), but it needs a window-function query and a ruling on what the value means on a filtered page. Until then no balance per row is served, and a client must not add the movements up itself.
​WalletEntryResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListProductPlansResponse

​PlanResponse[] · required

ListProductsResponse

`/v2` DTOs are always named `*Request` / `*Response` (design doc `§43.4`) -- there is no `CatalogItemView` here on purpose (`/v1`'s equivalent, `foundation/src/contracts/catalog.rs`, deleted as dead code in V-0692).
​ProductResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListPublicProductPlansResponse

​PublicPlanResponse[] · required

ListPublicProductsResponse

​PublicProductResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListRegionsResponse

`/v2` DTOs are always named `*Request` / `*Response` (design doc `§43.4`).
​RegionResponse[] · required

ListRunsResponse

The run history. Newest first, matching `ServiceRunRepository::list_by_subscription`'s own ordering.
​RunResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListScalingDecisionsResponse

​ScalingDecisionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListScannerSubscriptionsResponse

​ScannerSubscriptionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListSchedulesResponse

​ScheduleResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListServiceInstanceActivityResponse

​ServiceInstanceActivityEntry[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListServiceInstanceResourcesResponse

`GET /api/v2/service-instances/{id}/resources`: the service's live resources, for owners (organization doors) and per-service share holders alike.
primary_role_is_inconsistent
​boolean · required

true when two live resources both satisfied the primary rule (a data fault); then no resource is marked is_declared_primary.

ListServiceSharesResponse

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

How many live shares one service may carry.

ListSharedServiceInstancesResponse

​SharedServiceInstanceResponse[] · required

ListSubscriptionResourcesResponse

`GET /api/v2/console/subscriptions/{id}/resources`. Not paginated, unlike `ListSubscriptionsResponse` -- one subscription's resource set is small and bounded (design doc `§9.1`: "the modal shape today is already 2-3 Deployments per order"), the same reasoning `kernel_http_v2_catalog::contracts::products::ListProductPlansResponse` already applies to one product's plan list. **`data` is normal when empty.** Design doc `§8.1`: a `Subscription` has zero, one, or many `Resource`s -- a per-seat license (Zac, ZarozScanner) provisions no resource at all. Validation case `V2` was un-ignored specifically to establish that this is not an error state; see this crate's report for where that is exercised.
​ResourceResponse[] · required
primary_role_is_inconsistent
​boolean · required

true when two live resources both satisfy the primary rule, which A2-S9b made a hard error because it is never a legitimate state (design doc §9.1).

When set, every is_declared_primary in data is false -- the resolver refuses to guess between them -- and a consumer should say so rather than render "no main resource", which is indistinguishable from a subscription that legitimately has none.

It is a field rather than an error because this is a read-only display surface and failing the whole response helps nobody. It is a field rather than a log line because a silent degrade would make the screen most likely to reveal the corruption the screen that hides it (design doc §50).

​object · required

The running service these resources belong to, carrying the name and slug the customer refers to it by. Included here rather than fetched separately because this read already resolves the instance to find the resources at all.

ListSubscriptionsResponse

​SubscriptionResponse[] · required
​PaginationMeta · required

Pagination metadata included in paginated responses

ListUnitEventsResponse

`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.

ListUnitPodsResponse

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

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

ListUnitTerminationsResponse

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

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

ListUnitWebhookDeliveriesResponse

​UnitWebhookDeliveryResponse[] · required

ListUnitWebhooksResponse

​UnitWebhookResponse[] · required

ListUnitsResponse

`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.

ListVpsOperatingSystemsResponse

`GET /api/v2/catalog/products/{id}/operating-systems`'s response (ZC-0954/ZC-0960/ZC-0983).

LogCoverageNoteResponse

One fact the coverage sentence states, as `{code, params, text}`.
code
​string · required
​object · required
text
​string · required

LogDirectionParam

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: backward
Newest first: the first page of a panel, and scrolling back.

LogLineResponse

One line of output. No `stream` and no `level`: see this module's own documentation for why neither can be answered, and why inventing them would be worse than omitting them.
at
​string · date-time · required

When the container emitted the line, as Kubernetes recorded it.

id
​string · required

Stable across overlapping reads of the same output: the line's instant plus its position within the run of lines sharing that instant. A client resuming from a cursor can dedupe on this without comparing text.

message
​string · required

LogMarkerKindResponse

string · enum
Enum values:
pod_started
pod_replaced
pod_stopped
pod_lost
pod_torn_down
container_restarted
overflow

LogStreamResponse

string · enum
Enum values:
stdout
stderr

LogStreamTicketResponse

`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.

LogicalColumnTypeRequest

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, size
type = object · requires: kind, precision, scale
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
kind
​string · const · required
Const value: integer
size
​LogicalIntegerSizeRequest · enum · required
Enum values:
small
regular
big
unsigned
​boolean
Default: false

LogicalDefaultValueRequest

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind
type = object · requires: kind, value
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
kind
​string · const · required
Const value: null

LogicalIntegerSizeRequest

string · enum
Enum values:
small
regular
big

LoginRequest

Credentials for a password login.
email
​string · required
password
​string · required

LoginResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: status, challenge_id
Properties for Variant 1:
The password was correct. Submit `challenge_id` and the emailed code to `POST /auth/login/verify` to open a session.
challenge_id
​string · uuid · required
status
​string · const · required
Const value: mfa_required

LogoutRequest

refresh_token
​string · required

MapLabelDto

string · enum
Enum values:
map
world_type

Mirrors [MapLabel] by hand.

MapSettingDto

The `map` setting: `options` is `null` for free text.
default
​string · required
label
​MapLabelDto · enum · required

Mirrors [MapLabel] by hand.

Enum values:
map
world_type
​array | null

MarkDiscoverySeenRequest

`POST /api/v2/me/discovery/seen`'s request body. `deny_unknown_fields`, the same convention every `/v2` request type in this crate follows. The 20-key batch cap and the `<kind>:<path>` key format are NOT re-validated here -- `DiscoveryLedger::mark_seen` is the single source of truth for both, so a request that violates either gets exactly one 400, from the domain layer, naming the actual problem.
keys
​string[] · required

MaskedInvitationEmailResponse

The 403 body shown to an authenticated caller whose verified email does not match the invitation they asked for. Carries the masked address and nothing else about the organization -- deliberately the ONLY field on this type. Do not add an organization name, icon, role or any other invitation detail here: the whole point of masking is that a caller who is not the recipient learns nothing beyond "an invitation exists for an address shaped like this one".
email
​string · required

Already masked by the caller (e.g. j***@example.com) before this type is built; masking is not this DTO's job. This is deliberately the only field on this response.

MeResponse

The caller's own identity, as seen on the `/v2` console surface.
created_at
​string · date-time · required
discord_notifications_enabled
​boolean · required

Whether kernel-generated notifications are relayed to this user as Discord DMs, on top of the in-app center they always get. Read back so the toggle PATCH /me writes has somewhere to read its own current state from.

id
​string · uuid · required
is_onboarded
​boolean · required
language
​string · required

The caller's own display language, as an unvalidated BCP-47-ish tag (LanguageCode is a newtype over String and validates nothing).

Published because the onboarding screen and the account page both need to render the value the account already holds as their default. console/app/onboarding/page.tsx records the gap this closes: it had to start the language and billing-country selects empty because nothing on /v2 reported either field, which meant re-answering onboarding silently guessed instead of showing what was already stored.

name
​string · required
system_permissions
​string[] · required

The caller's own platform permissions, as [kernel_core::domain::permissions::SystemPermission]'s wire strings (view_all_orders, manage_users, ...). Empty for an ordinary customer, which is nearly every caller.

Holding one of these IS being staff. The kernel has no separate staff flag, so a non-empty list is the whole of "this caller may open the admin app", and each value decides one gated section inside it.

Field-for-field the same answer /v1's CurrentUserView::system_permissions gave (deleted with /v1 in 88cb24c1; it rendered User::permissions through the same Display). Nothing on /v2 published it, so admin recovered it by intersecting GET /api/v2/admin/users/{id}'s roles with GET /api/v2/admin/ permissions' granted_by_roles: two extra round trips per render, growing with the role count, and both of those routes sit under /admin/ and so behind require_elevated_session -- meaning an un-elevated staff session could not learn what it held at all (ZC-0410, and ZC-0411 which this closes with it). /api/v2/me has no admin path segment, so it is not elevation-gated and does not reintroduce that trap.

Empty for a restricted API token, never the owning user's list. A restricted token's authority is its grants, which domain::policies::api_token_intersect intersects with these permissions once per operation; there is no mapping from a SystemPermission back to the ApiTokenCapability a token would also have to carry, so the owner's list is not an answer to "what may this token do", and publishing it would tell a leaked token only that its owner is staff. /v1 never had to decide this: its current-user route refused a token caller outright.

avatar_asset_id
​string | null · uuid

The caller's own avatar (ZC-1306), addressed through /api/v2/assets/{id}. None when the account has never set one (no OAuth provider avatar and nothing uploaded). An asset id, not a URL -- see OrganizationResponse::icon_asset_id's own doc comment for the /v2 convention this follows: the id itself changes on every replace, uploaded or provider-synced alike, so a consumer never has to append a cache-busting query parameter.

billing_country_code
​string | null

The ISO 3166-1 alpha-2 country the account is billed from, None until onboarding answers it.

Write-once in practice: UserManagementService::update_user_profile refuses to change it once is_onboarded is true and a value is already set, so a client rendering an edit control for it after onboarding is rendering a control that cannot succeed.

email
​string | null

Resolved through UserEmailService (checks oauth_accounts first, then password_credentials). None when neither source has an address for this user -- User itself carries no email field, so this cannot be filled in by a pure User conversion; see get_me_handler.

MeasurementResponse

string · enum
Enum values:
flat
allocated
gauge
counter
quantity

MemoryPressureLevelResponse

string · enum
Enum values:
high
recent

MemoryPressureResponse

last_high_at
​string · date-time · required
level
​string · enum · required

high while pressure is sustained right now, recent for an episode that has passed but is still inside the retention window.

Enum values:
high
recent
peak_psi_some_avg60
​number · double · required

Worst percentage of wall time spent stalled on memory reclaim.

peak_working_set_ratio
​number · double · required

Worst working set during the episode, as a fraction (0-1) of the limit.

MeteredEstimateResponse

The monthly worst case of the metered items a purchase asks for.
basis
​string · required

How this is billed, in kernel's words.

included_ports
​integer · uint32 · min: 0 · required

Ports the plan includes at no charge.

monthly_cents
​integer · int64 · required

Every priced port held for a whole month. Zero when the plan includes them all.

per_port_monthly_cents
​integer · int64 · required

What one priced port costs held a whole month, in cents. monthly_cents is this times priced_ports; a client shows it rather than dividing.

priced_ports
​integer · uint32 · min: 0 · required

Ports billed per month held.

MetricLineResponse

One line of a chart. A bucket with no sample is `null`, which a stopped unit and an idle one need to look different.
​array[] · required

[bucket start in epoch milliseconds, value], 120 of them.

limit
​number | null · double

The ceiling this line is measured against, or null when it is unmetered or the unit has no limit set.

​

Latest, mean and max of the buckets with a value; absent when there is none.

MetricSeriesResponse

One measured quantity over the window.
id
​string · required

Stable identifier: cpu, memory, net_in, net_out. A consumer keys its unit formatter off this, never off label.

label
​string · required

Rendered as written. Naming a series is kernel's job; converting its unit for display (bytes to MiB) is the client's, which is the same line R2 already draws for prices.

​array[] · required
unit
​string · required

The unit points, limit and summary are all expressed in: cores, bytes, or bytes_per_second. Every number on this series shares it, so a client never has to know which field carries which scale.

limit
​number | null · double

The ceiling this series is measured against, or null when the series is unmetered and a line would invent a bound that does not exist (15-metrics.md §4).

Attached to the series rather than to the instance for two reasons: it is per series (network has none), and a ceiling that arrives with the points cannot disagree with them.

​

Absent when the window holds no samples. The panel then shows no figures rather than inventing them.

MetricSummaryResponse

Latest, mean and max over the **requested window**. **Returned, never reduced by a consumer.** `latest`, `mean` and `max` are four lines of JavaScript each, which is exactly why every consumer would write them and why the copies would eventually disagree about whether a gap counts as a zero. `R2` (`docs/V2Plan/00-program.md` §5) exists to stop that, and here it costs kernel one pass over at most a few hundred rows. **Computed over the raw samples, not over the buckets above.** The max of the means is not the max, and the difference is exactly the spike a customer opened the page to find: a one-sample burst inside a thirty-second bucket whose mean is low still reports as `max` here.
latest
​number · double · required

The newest sample in the window. The whole summary is absent when the window holds no samples at all, so this is never a stand-in for one.

max
​number · double · required
mean
​number · double · required

MinecraftDifficultyDto

string · enum
Enum values:
peaceful
easy
normal
hard
hardcore

MinecraftGameplayDto

[`SetupStepId::MinecraftGameplay`](kernel_core::domain::setup_steps::SetupStepId::MinecraftGameplay)'s answers.
difficulty
​MinecraftDifficultyDto · enum · required
Enum values:
peaceful
easy
normal
hard
hardcore
online_mode
​boolean · required

MinecraftInventoryItemResponse

One jar currently in the folder.
file_name
​string · required

The bare file name, split off path so a panel need not parse it.

path
​string · required

Absolute within the mount, the same spelling the Files capability uses -- follow it there to open the jar in the file browser.

size
​integer · uint64 · min: 0 · required
​MinecraftJarStateResponse · required

What kernel currently knows about one jar's identity.

reason
​string | null

Present exactly when state is unresolved.

​

Present exactly when state is resolved.

MinecraftInventoryKindResponse

string · enum
Enum values:
mods
plugins

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).

MinecraftInventoryResponse

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

MinecraftJarStateResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
resolved
unresolved

MinecraftLoaderDto

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: type, version
type = object · requires: type, version
type = object · requires: type, version, loader_version
type = object · requires: type, version, loader_version
type = object · requires: type, version
type = object · requires: type, version
type = object · requires: type, project_id
type = object · requires: type, pack_id
type = object · requires: type, path
type = object · requires: type, url
Properties for Variant 1:
type
​string · const · required
Const value: vanilla
version
​string · required

MinecraftPlayersResponse

The roster: online players, operators and bans, fused server-side (`fuse_snapshot`) exactly as `ZC-0463-game-surfaces-design.md` §1's "1." describes -- never a client-side join of three payloads.
​BannedPlayerResponse[] · required

Empty unless status is ok.

banned_unavailable
​boolean · required

Same meaning as ops_unavailable, for banned-players.json.

​OnlinePlayerResponse[] · required

Empty unless status is ok.

​OperatorPlayerResponse[] · required

Empty unless status is ok.

ops_unavailable
​boolean · required

True when ops.json could not be read or parsed. ops is then empty, but that does not mean nobody is an operator -- it means this answer cannot say.

partial_list_format
​boolean · required

True when /list's reply did not match the vanilla format and had to be parsed permissively (parser::parse_list's tier 2). The roster is still usable; this is a hint that the counts may be approximate on a heavily modded server.

​MinecraftPlayersStatus · required

Every outcome a read or a write can answer with, folded into one status so a client renders a match rather than branching on HTTP status codes that are all 200. Ok is the only variant the roster fields or the action actually succeeding are meaningful under.

max_players
​integer | null · uint32 · min: 0

MinecraftPlayersStatus

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
ok
server_offline
auth_failed
unreachable
timeout
protocol_error

MinecraftSoftwareDto

[`SetupStepId::MinecraftSoftware`](kernel_core::domain::setup_steps::SetupStepId::MinecraftSoftware)'s answers.
​MinecraftLoaderDto · required

Which Minecraft server software runs, and where it comes from. The ten spellings below are exactly minecraft.rs's own minecraft_software.loader export vocabulary (vanilla|paper|forge|neoforge|fabric|quilt|modrinth|feed_the_beast| custom_file|custom_url), not [MinecraftLoader]'s own serde tag.

ModeLabelDto

string · enum
Enum values:
game_mode
preset
difficulty
auth_mode

Mirrors [ModeLabel] by hand.

ModeSettingDto

The `mode` setting: `options` is `null` for free text.
default
​string · required
label
​ModeLabelDto · enum · required

Mirrors [ModeLabel] by hand.

Enum values:
game_mode
preset
difficulty
auth_mode
​array | null

MyApiTokenCapabilityResponse

One capability the caller may put in a token they mint.
admin_only
​boolean · required

Minting it needs admin_authorized and an elevated session.

​ApiTokenCapability · required

Closed, policy-owned action catalog. Capabilities are exact: there is deliberately no prefix or glob matching, and unknown wire values are rejected during deserialization. PartialOrd/Ord exist so a capability set can be a BTreeSet, which is what [crate::domain::auth::application_token::ApplicationToken] stores. The ordering is the declaration order and is not a wire contract: nothing serializes it, and as_str remains the only stable representation.

MyApiTokenResponse

One of the caller's own personal API tokens (ZC-0512). **Never carries the secret, or even its digest.** Only a digest of the secret half is stored on `token_hash`, and this response omits that field entirely: the full credential is returned exactly once, at creation (`POST /me/api-tokens`), and no later read -- this one included -- can reproduce it. Unlike [`crate::contracts::organization_api_tokens::OrganizationApiTokenResponse`], `capabilities` here is not filtered to resource-scoped grants: a personal token may hold a global or resource-type-scoped grant as well as a resource-scoped one, so every capability the token's authorization names is reported, regardless of scope shape. A token created before restricted authorization existed carries no `authorization` at all (a legacy unrestricted token); it reports an empty list rather than "every capability there is", since there is no finite list of everything an unrestricted token can do to render here.
​ApiTokenCapability[] · required
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
expires_at
​string | null · date-time
last_used_at
​string | null · date-time

MyApplicationDelegationResponse

One application holding a live-or-revoked delegation over the caller's own account (ZC-1321). Never names another user: `application_delegations`' own `user_id` is always the caller's here, so this response omits it rather than echo back a field that can only ever equal the caller's own id.
application_id
​string · uuid · required
application_name
​string · required

Presentable name, resolved by the kernel so this reads "the Discord bot" rather than a bare id -- the same reason the consent page and the management page must read from one source (grants spec §3.1/§4.1).

​ApiTokenCapability[] · required

What the caller approved this application to do.

created_at
​string · date-time · required
id
​string · uuid · required
origin
​string · required

session_delegation or explicit_consent.

last_used_at
​string | null · date-time
revoked_at
​string | null · date-time

MyDiscoveryResponse

`GET /api/v2/me/discovery`'s response: the caller's own `{key: seen_at}` map. An account with no stored `user_discovery` row answers `{"seen": {}}`, never a 404 -- see `DiscoveryLedger::empty`.
​object · required

MyInvitationStatusFilter

string · enum
Enum values:
pending
declined

Only pending and declined are listable this way, matching the design's route table: accepted, revoked and expired invitations do not belong in a recipient's actionable inbox.

MyReferralLinkResponse

One of the caller's own referral links. `use_count` is a CLICK count -- `conversion_count` is the number of real signups, and the two can differ (a link is clicked without converting). See the admin surface's identical distinction on `kernel_http_v2_admin::contracts::referral_links::ReferralLinkResponse`.
conversion_count
​integer · int64 · required
created_at
​string · date-time · required
id
​string · uuid · required
slug
​string · required
updated_at
​string · date-time · required
use_count
​integer · int32 · required

MyServiceInstanceResourcesResponse

The purchased sizing, in the same units `ResizeResourcesRequest` uses -- see that type's own doc comment. `memory_gb` is fractional (sub-GiB workloads are real since the 2026-09-04 migration -- `kernel_http_foundation::contracts::memory_units`'s own doc comment); `storage_gb` is whole GiB, because storage never went sub-GiB.
cpu
​number · float · required
memory_gb
​number · double · required

Memory in GiB, fractional (0.5 = 512 MiB)

storage_gb
​integer · uint64 · min: 0 · required

Storage in GiB.

MyServiceInstanceSummaryResponse

One row of the caller's own service-instance list (`GET /api/v2/service-instances`, `ZC-1199`). Deliberately does not carry an `order_id`: `/v2` has none (Ruling Zero) -- a client resolves everything through `subscription_id` and `id`, never through an order.
availability
​string · enum · required

cluster_unavailable means the platform cannot currently resolve a cluster for this service's hardware profile -- the same profile-to-cluster resolution the status service itself uses to decide whether to attempt a live read. It does NOT mean a live reachability probe against the cluster failed; no such probe is made for a list of many rows. A row never fails the whole list: an unresolvable cluster for one row answers cluster_unavailable on that row alone.

Enum values:
available
cluster_unavailable
created_at
​string · date-time · required
has_pending_invoice
​boolean · required

Whether the owning subscription has at least one unpaid, pending invoice right now.

id
​string · uuid · required
name
​string · required
product_id
​string · uuid · required
project_id
​string · uuid · required
​MyServiceInstanceResourcesResponse · required

The purchased sizing, in the same units ResizeResourcesRequest uses -- see that type's own doc comment. memory_gb is fractional (sub-GiB workloads are real since the 2026-09-04 migration -- kernel_http_foundation::contracts::memory_units's own doc comment); storage_gb is whole GiB, because storage never went sub-GiB.

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

The owning subscription's scheduled end, or null if none is scheduled. Mirrors SubscriptionResponse::cancels_at.

​

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.

parent_subscription_id
​string | null · uuid

ZC-0984: the bundle this instance's subscription is a member of, or null for an ordinary, standalone subscription.

​

The service's aggregate run state, folded from its own resources' recorded statuses -- the same fold the live status route (GET .../service-instances/{id}/status/stream) uses, read here from the database rather than a live cluster call so listing many services costs one query, not one Kubernetes call per row. null only if the instance's resources could not be read at all; in practice every row answers not_provisioned at worst, never null.

​

The authored storefront shelf of this row's product, resolved through its display profile -- the same value ProductResponse.storefront_category publishes, repeated here so a client listing many services does not have to read a product per row to learn what kind of thing each one is (ZC-1270). null when nobody has categorised the display profile yet, which is a real state and not an error. This exists because the route returns EVERYTHING the caller owns, and a frontend that sells only part of the catalogue had no authored fact to filter on -- the gaming app was listing the business app's products. It is deliberately the authored shelf rather than a derived category: ProductResponse.category's own documentation warns that its fallthrough files databases, object storage and static sites all as game servers.

MySessionResponse

One of the caller's own signed-in sessions (ZC-0512, ZC-0482). A session is a refresh-token **family**, not a single row: `id` is the family id, stable across every rotation the family goes through, and the same value `DELETE /me/sessions/{id}` (and `RefreshTokenService:: revoke_session`) addresses. The underlying row *is* replaced on every rotation (`RefreshTokenServiceImpl::issue_in_family` revokes the old row and inserts a new one in the same family), which is why `created_at` and `last_used_at` below describe the family's most recent rotation rather than when the person originally signed in: nothing in `refresh_tokens` preserves that earlier timestamp once the first rotation happens. Stated here rather than silently rendered as "signed in at", which it is not. Deliberately has no `current: bool`. The access token backing a request to this route carries no family/session claim (`JwtClaims` in `kernel-auth` has `user_id`, `expires_at`, `is_onboarded` and `elevated_until` only), so nothing on the request lets this handler match the caller's bearer token back to one row in this list. Marking a row "current" would mean guessing, and a wrong guess here -- telling someone their own active session is some *other* device -- is worse than the field being absent. Recorded as a gap for whoever picks this up: closing it means adding a family/session claim to the access token, which is a token-format change wider than this route, not a column to add.
created_at
​string · date-time · required

When this session's current credential was issued -- the most recent rotation, not necessarily the original sign-in. See the type's own doc comment.

expires_at
​string · date-time · required
id
​string · uuid · required
last_used_at
​string · date-time · required

Same caveat as created_at: refreshed on rotation, so it tracks "last time this session's access token was renewed" rather than every individual API call.

ip_address
​string | null

Captured from the request that most recently refreshed this session. None when no client address was resolved.

user_agent
​string | null

Captured from the request that most recently refreshed this session. None when the client sent no User-Agent header.

MyWalletPreferencesResponse

autopay_enabled
​boolean · required

Whether invoices are collected from the wallet automatically when they fall due.

preferred_currency
​string · required

ISO 4217. Read-only: wallets are EUR-only, so there is nothing to choose.

MyWalletTopUpResponse

One of the caller's own top-ups.
amount_cents
​integer · int64 · required

Integer cents of currency.

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

ISO 4217. Always EUR today.

expires_at
​string · date-time · required

After this the checkout lapses and the top-up becomes expired.

id
​string · uuid · required
provider
​string · required

paypal or nowpayments. Older rows may say mollie.

​TopUpStatusResponse · required

Where a top-up is. Only credited means money reached the wallet.

checkout_url
​string | null

The provider's hosted page to send the customer to. Present once the checkout exists; absent while creating and on a failed start.

NetworkProtectionModesResponse

The protection modes a service may choose from (v1's `/network-protection-modes`).
default_mode
​string · required

The mode a new allocation starts in.

game
​string[] · required

Modes accepted for a game port allocation.

vps
​string[] · required

Modes accepted for a VPS's dedicated IP.

NotificationCreatedEventResponse

One frame of `GET /api/v2/me/notifications/stream`'s `notification_created` event. Deliberately its OWN type rather than a reuse of `NotificationResponse`: `DomainEvent::NotificationCreated` (what this is built from) carries no `organization_id` and no persisted `created_at` -- inventing either would either misreport scoping the event never asserted or claim a persistence timestamp the frame does not actually carry, and this surface must never say more, or something DIFFERENT, than a `GET` of the same notification would (the rule `contracts::ticket_events`'s own module doc states for the identical reason). `read` is also absent: every notification this event announces was, by construction, just created unread, so a client does not need to be told.
title
​string · required
body
​string · required
created_at
​string · date-time · required

The moment this frame was assembled, not a persisted column -- see this type's own doc comment.

id
​string · uuid · required
kind
​NotificationKindResponse · enum · required

The wire spelling of NotificationKind, converted explicitly at the transport boundary rather than deriving JsonSchema on the domain type directly (domain crates do not take a schemars dependency -- see crates/http/CLAUDE.md's "Transport boundary").

Subscription stays spelled order on the wire. See NotificationKind::Subscription's own doc comment: it was renamed from Order in the domain (ZC-0261 phase 9f), but notifications.kind is a text column written since /v1 and dashboard's notification feed (lib/notifications.ts) already reads the literal string "order" -- changing the wire value would be a breaking, silent rename of every existing row's meaning.

Enum values:
order
invoice
server
system
href
​string | null

NotificationKindResponse

string · enum
Enum values:
order
invoice
server
system

The wire spelling of NotificationKind, converted explicitly at the transport boundary rather than deriving JsonSchema on the domain type directly (domain crates do not take a schemars dependency -- see crates/http/CLAUDE.md's "Transport boundary").

Subscription stays spelled order on the wire. See NotificationKind::Subscription's own doc comment: it was renamed from Order in the domain (ZC-0261 phase 9f), but notifications.kind is a text column written since /v1 and dashboard's notification feed (lib/notifications.ts) already reads the literal string "order" -- changing the wire value would be a breaking, silent rename of every existing row's meaning.

NotificationResponse

One notification, in the shape both the list read and the single mark-read write answer with -- matching `dashboard`'s own `Notification` interface (`lib/notifications.ts`) field for field where the domain carries the same information: `id`, `kind`, `title`, `body`, `read`, `href`, `created_at`. `organization_id` is additional, carried because the entity already has it and a future multi-organization UI can use it to group the feed; it is never `null` when the notification is genuinely organization-scoped and never fabricated when it is not (see `Notification::organization_id`'s own doc comment on the domain type).
title
​string · required
body
​string · required
created_at
​string · date-time · required
id
​string · uuid · required
kind
​NotificationKindResponse · enum · required

The wire spelling of NotificationKind, converted explicitly at the transport boundary rather than deriving JsonSchema on the domain type directly (domain crates do not take a schemars dependency -- see crates/http/CLAUDE.md's "Transport boundary").

Subscription stays spelled order on the wire. See NotificationKind::Subscription's own doc comment: it was renamed from Order in the domain (ZC-0261 phase 9f), but notifications.kind is a text column written since /v1 and dashboard's notification feed (lib/notifications.ts) already reads the literal string "order" -- changing the wire value would be a breaking, silent rename of every existing row's meaning.

Enum values:
order
invoice
server
system
read
​boolean · required
href
​string | null
organization_id
​string | null · uuid

OAuthAccountResponse

A provider account linked to the caller's own user. Carries no token. `OAuthAccount` stores the provider access and refresh tokens it obtained, and neither is a thing a client has any use for; a response that included them would hand every reader of one page a credential for the customer's Discord or Google account.
has_avatar
​boolean · required

Whether this provider has a stored avatar that could be activated. The URL itself is not published: it is a provider-side resource the kernel imports through AvatarImportService, not something a client should be fetching directly.

provider_id
​string · required
email
​string | null

The address the provider reports for this account, when it reports one. Only ever the caller's own.

last_used_at
​string | null · date-time

OAuthProviderResponse

One provider a visitor may sign in with, as the sign-in page renders it. Carries the id and a display name and nothing else. It deliberately does not say which providers any particular account uses -- that is `GET /auth/oauth/accounts`, which answers only about the caller's own account and needs a session to do it.
id
​string · required
display_name
​string | null

A reseller-chosen label. None means the client should render the provider's own conventional name from the id.

OAuthRedirectResponse

Where to send the visitor next.
redirect_to
​string · required

OnExit

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: restart
The pod is replaced and the unit goes on.

OnlinePlayerResponse

is_op
​boolean · required
name
​string · required
gamemode
​string | null

survival, creative, adventure or spectator. Present only when include=gamemode was asked for and the server answered for this player.

​

Present only when include=position was asked for and the server answered for this player.

OpenInvoicesSummaryResponse

What the caller still owes, without the invoices themselves (V-0579). The read behind the open-invoice attention dot on the Billing nav: a sidebar needs one number and one amount, not a page of rows. "Open" and "overdue" are decided in the domain (`OpenInvoicesSummary`); this is its wire shape.
count
​integer · uint64 · min: 0 · required

Invoices issued and not yet paid, including a postpaid invoice before its due date. 0 means nothing is owed.

overdue_count
​integer · uint64 · min: 0 · required

How many of count are past their due_at.

currency
​string | null

ISO 4217 code of total_cents; null exactly when total_cents is.

total_cents
​integer | null · int64

Sum of the open invoices' tax-inclusive totals, in cents. null when nothing is open, and also when the open invoices span more than one currency (an amount that adds two currencies is withheld, not guessed).

OperationStatusResponse

string · enum
Enum values:
pending
processing
completed
dead_letter

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.

OperatorPlayerResponse

level
​integer · uint8 · min: 0 · max: 255 · required
name
​string · required
online
​boolean · required

OrganizationApiTokenLifetimeResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: membership_tied
Revoked automatically when the creator's membership of this organization ends.

OrganizationApiTokenResponse

One organization API token. **Never carries the secret.** The secret exists in exactly one response, [`CreatedOrganizationApiTokenResponse`], and only its digest is stored, so nothing can put it back here later.
​ApiTokenCapability[] · required
created_at
​string · date-time · required
id
​string · uuid · required
​OrganizationApiTokenLifetimeResponse · required

The lifetime shape of an organization API token, on the wire.

MembershipTied is the default. A create request that omits lifetime gets it, and that is the safer of the two: the token dies when the person who minted it stops being a member. OrganizationOwned is the one that keeps working afterwards, and asking for it is always explicit.

name
​string · required
created_by_user_id
​string | null · uuid

The member who minted this token. Null when that account has since been deleted; an organization-owned token deliberately survives that.

expires_at
​string | null · date-time
last_used_at
​string | null · date-time

OrganizationInvitationResponse

One invitation row, for `GET /organizations/{id}/invitations` (an organization's own pending invites) and for the response to `POST /organizations/{id}/invitations` (the one just sent). There is deliberately no `From<OrganizationInvitation>`, matching `OrganizationMemberResponse`'s own reasoning in `contracts::organizations`: the inviter's name and picture cannot be filled from the invitation row alone, so a bare `From` would be an invitation to serialize a blank inviter without anyone noticing.
created_at
​string · date-time · required
email
​string · required
expires_at
​string · date-time · required
id
​string · uuid · required
invited_by_name
​string · required
organization_id
​string · uuid · required
organization_name
​string · required

Denormalized onto every row (also present, redundantly, on an organization-scoped listing) so GET /me/invitations does not need a second round trip per organization to render a usable list.

role
​OrganizationRoleResponse · enum · required
Enum values:
owner
admin
billing
member
status
​string · enum · required

The invitation's effective status: a stale pending or declined row past its own expiry reads as expired here even before any sweep has rewritten the stored column. See OrganizationInvitation::effective_status.

Enum values:
pending
accepted
declined
revoked
expired
invited_by_avatar_asset_id
​string | null · uuid
organization_icon_asset_id
​string | null · uuid
responded_at
​string | null · date-time

OrganizationInvitationReviewResponse

The page a recipient opens for `GET /invitations/{id}`: enough to decide whether to accept, nothing more. No `email` field -- the recipient already knows their own address, and this response is only ever served once the caller's verified email has already been matched against the invitation (an email mismatch is refused with [`MaskedInvitationEmailResponse`] instead, which shows the organization nothing about itself in return).
created_at
​string · date-time · required
expires_at
​string · date-time · required
id
​string · uuid · required
inviter_name
​string · required
organization_id
​string · uuid · required

Where Accept navigates once it succeeds.

organization_name
​string · required
role
​OrganizationRoleResponse · enum · required
Enum values:
owner
admin
billing
member
status
​string · enum · required

The invitation's effective status, not necessarily the raw stored column.

Enum values:
pending
accepted
declined
revoked
expired
inviter_avatar_asset_id
​string | null · uuid
organization_icon_asset_id
​string | null · uuid

OrganizationInvitationStatusResponse

string · enum
Enum values:
pending
accepted
declined
revoked
expired

Mirrors [OrganizationInvitationStatus] on the wire. A separate type rather than reusing the domain enum's own Serialize impl, matching every other /v2 status/role pair in this crate (see OrganizationRoleResponse next to OrganizationRole in contracts::organizations): the wire representation and the domain representation are allowed to diverge without a compile error forcing them apart later.

OrganizationLimitsResponse

The organization's unit fuse (decision 0306): live units across all its services and pod starts in the last hour, each against its limit.
nearing
​boolean · required

Any figure is near or at its limit: the console shows its notice and points to support.

​object · required

The month's metered spend against the organization-wide spend cap.

​LimitUsageResponse · required

One figure of the organization against its limit.

​LimitUsageResponse · required

One figure of the organization against its limit.

OrganizationMemberResponse

One membership row, carrying enough of the member's identity that the person reading it can tell **who** it is. This used to be `organization_id`, `user_id`, `role` and `created_at` alone, which meant console's members table could only render a raw uuid -- an organization owner literally could not tell who their own collaborators were (`console/components/members/member-cell.tsx`, and `console/openapi/PENDING.md`, both said so). Owner ruling 2026-09-09 item 8: name, email **and** profile picture, all three, visible to fellow members. Three shape decisions, each with a precedent in this workspace: - **Denormalized onto the membership row, not a separate identity lookup.** A lookup route would have to answer for a `user_id` in isolation, which is a user-enumeration oracle by construction: it cannot know the caller shares an organization with the subject without being handed the organization, at which point it is this route with extra steps. Resolving at response time is what `kernel_http_v2_admin::contracts::organizations` already does. - **`email` is `Option`**, following `UserSummaryResponse`: an account with no reachable address is a real state (see [`UserEmailService`](kernel_core::application::services::users::user_email_service::UserEmailService)), not a failed lookup, and a member list must not 500 because one member signed up through a provider that returned no address. - **`avatar_asset_id`, not an `avatar_url`.** The `/v2` rule is asset ids on the wire, addressed through `/api/v2/assets/{id}` by the consumer (`kernel_http_v2_reseller::contracts::reseller`, `kernel_http_v2_catalog::contracts::products`'s `icon_asset_id`). `/v1` baked `/api/v1/assets/{id}?v={updated_at}` into responses and pinned the wire to a path `/v2` deletes. It also matters here for a second reason: an id is an opaque kernel uuid, so no part of this field is derived from the member's email address the way a Gravatar-style hash or an OAuth provider's CDN URL would be. The provider's avatar is downloaded into our own object storage at login (`AvatarImportService`), so a member's picture never sends a viewer to a third party.
created_at
​string · date-time · required
name
​string · required

The member's display name.

organization_id
​string · uuid · required
role
​OrganizationRoleResponse · enum · required
Enum values:
owner
admin
billing
member
user_id
​string · uuid · required
avatar_asset_id
​string | null · uuid

The member's profile picture, addressed through /api/v2/assets/{id}. None when they never set one.

email
​string | null

None when the account has no reachable address.

OrganizationResponse

One organization as seen by its own member, on the `/v2` console surface. `/v2` DTOs are always named `*Request` / `*Response` (design doc §43.4) -- there is no `OrganizationView` here on purpose.
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
slug
​string · required

URL-safe identifier, unique platform-wide. Console routes are /{slug}/...

billing_country_code
​string | null

Two-letter country the organization is taxed under. Null until set; a purchase in a shared organization needs it (PUT /organizations/{id}/billing-country).

icon_asset_id
​string | null · uuid

The organization's uploaded icon (ZC-1305), addressed through /api/v2/assets/{id}. None when no icon has been uploaded. An asset id, not a URL -- the /v2 convention every other asset-backed field on this surface already follows (see OrganizationMemberResponse::avatar_asset_id's own doc comment): the id itself changes on every replace, so a consumer building the URL from it never has to append a cache-busting query parameter to avoid serving a stale image.

​

The caller's role in this organization, when resolved.

OrganizationRoleRequest

string · enum
Enum values:
owner
admin
billing
member

Distinct from OrganizationRoleResponse because request and response enums are separate types on /v2 (design doc §43.4), even where they currently carry the same variants.

OrganizationRoleResponse

string · enum
Enum values:
owner
admin
billing
member

OrganizationStandingResponse

`GET /api/v2/organizations/{organization_id}/standing`'s response (`Q121`, decided 2026-09-26): the organization's own owner (and staff, in admin) sees [`StandingResponse::detail`] in full; every other member sees only [`Self::message`], the same fixed sentence for every restriction reason -- naming which fact failed, or which other organization is involved, is exactly the detail `Q121` says is wrong for a co-member to learn. Both fields are `None` for a caller who is not restricted and not the owner: there is nothing to disclose either way.
restricted
​boolean · required
​
message
​string | null

OrganizationWalletResponse

One organization's wallet. The balance lives here rather than on `OrganizationResponse` on purpose. Console's scope layout reads the organization on every request, under the sidebar and every page below it; a running total folded out of the ledger on that path would put a `SUM` over `wallet_entries` in front of every navigation. A balance is one operation, asked for when a page actually shows money.
​WalletBalanceResponse[] · required

One row per currency the organization holds, ordered by currency code.

A currency with no money in it has no row. The balance is summed from the ledger and only non-zero sums come back, so an organization that has never been credited answers with an empty list, and so does one whose movements happen to net to exactly zero. Treat a missing currency as zero; do not treat an empty list as "no wallet".

organization_id
​string · uuid · required

The organization whose wallet this is -- echoed back so a client that fans several of these out cannot mis-file an answer.

OverflowRangeResponse

The span and size of log lines the rate cap moved to the archive.
count
​integer · uint64 · min: 0 · required
from
​string · date-time · required
to
​string · date-time · required

OverlapPolicyResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: skip
A fire that would overlap a still-running previous run is not started; a run row is written with `outcome: skipped_overlap`.

OwnPasswordResetRequest

Optional body of `POST /auth/password/forgot-current` (V-0853).
origin
​string | null

The app URL the emailed reset link should point back at, e.g. https://zaroz.cloud/console. Same shape and validation as [ForgotPasswordRequest::origin]; a disallowed or malformed value is a real 4xx refusal. Omitted, the link points at the platform dashboard.

PaginationMeta

Pagination metadata included in paginated responses
has_next_page
​boolean · required

Whether there is a next page

has_previous_page
​boolean · required

Whether there is a previous page

page
​integer · uint32 · min: 0 · required

Current page number (1-indexed)

page_size
​integer · uint32 · min: 0 · required

Number of items per page

total_items
​integer · uint64 · min: 0 · required

Total number of items across all pages

total_pages
​integer · uint32 · min: 0 · required

Total number of pages

PasswordAcceptedResponse

The answer to every request that was accepted for processing and has nothing further to report. Shared by forgot, reset, change, set and forgot-current rather than given a per-operation shape, because a per-operation shape is how an oracle gets built by accident: the moment one of these carries a field that varies with the account, the response starts describing an account the caller may not own. Modelled on [`PasswordSignupResponse`], which exists for the same reason.
accepted
​boolean · required

Always true. A real typed field rather than an empty body so a generated client has a success type to name.

PasswordRequestDto

anyOf
At least one variant must match. Multiple variants may match simultaneously.

Decision Table

VariantMatching Criteria
type = object · requires: generate
type = object · requires: value
type = object · requires: clear
Properties for Variant 1:
generate
​boolean · required

PasswordSignupRequest

Starts an email and password registration.
email
​string · required
password
​string · required
origin
​string | null

The app base the emailed verification link should point back at (ZC-1425, V-0664): https plus a host, optionally followed by exactly one app mount path -- https://zaroz.cloud/gaming, https://zaroz.cloud/console, https://zaroz.cloud/admin, or a reseller's https://<host>/gaming. A bare origin keeps working and links to the host root. No other path, no query, no fragment. The link is <origin><path>/verify-email?token=.... Checked against the same allowlist OAuth's own redirect_to uses; a disallowed or malformed origin refuses the whole request with a 4xx, never a silent fallback. Omit to keep the default: the dashboard.

referral_code
​string | null

A referral link's slug, if the visitor arrived through one (ZC-1032, owner ruling 2026-09-17: attribution happens at signup). Never validated here and never blocks or slows down signup, nor changes this route's response, whether the code is real, expired, self-owned, or absent -- see signup_handler's own doc comment for why.

PasswordSignupResponse

The outcome of `POST /auth/password/signup`, deliberately identical whether or not the address already has an account. `/v1`'s equivalent (`PasswordSignupResponse` in `crates/http/identity/src/contracts/password_auth.rs`) is a tagged union with an `AlreadyRegistered { has_password }` variant, returned verbatim whenever `PasswordAuthService::sign_up` resolves to `SignupOutcome::AlreadyRegistered`. That makes a signup attempt an account-existence oracle: posting any address and reading the response alone tells the caller whether it is registered. V2 does not carry that forward -- `controllers::password::signup_handler` collapses both `SignupOutcome` variants into this one response, so there is no second variant here to return by mistake.
accepted
​boolean · required

Always true. Kept as a real typed field, rather than an empty Success(()), so a generated client gets an actual success body; there is nothing more to report here without reopening the oracle described above.

PasswordSpecDto

One declared password ([`PasswordSpec`]), on the same wire as [`ServerPasswordsParamsDto`]. Never carries a value -- this is what a product ASKS for, not what a customer answered ([`ServerPasswordsResponseDto`] is that).
env
​string · required
generated
​boolean · required
id
​string · required
optional
​boolean · required

V-0703. See [PasswordSpec::optional]: the customer may leave this password unset, or clear it later.

revealable
​boolean · required

ZC-1379 slice S4. See [PasswordSpec::revealable]'s own doc comment; the dashboard uses this to grey out the reveal action rather than let a customer hit the 409 the reveal route otherwise answers with.

rotatable
​boolean · required

ZC-1379 slice S4. See [PasswordSpec::rotatable]'s own doc comment.

min_length
​integer | null · uint8 · min: 0 · max: 255

PasswordStatusDto

One declared password's status: whether it has a stored answer at all.
env
​string · required
id
​string · required
set
​boolean · required

PasswordStatusResponse

The email and password state of the signed-in caller's own account, for the security settings screen.
email_verified
​boolean · required
has_password
​boolean · required
email
​string | null

The address password login uses, or the provider address a first password would be attached to. None when neither exists.

linkable_email
​string | null

The provider-proven address POST /auth/password/set would attach a first password to. None once the account already has one.

PathRouteRequest

One path route: requests whose path is `path` or lies under it go to `target`.
path
​string · required

/api, /api/v1: a slash, then segments of letters, digits and . _ ~ -; no trailing slash, never / itself (the domain's own endpoint is the default route).

target_endpoint_id
​string · uuid · required

An open https endpoint of that service.

target_service_instance_id
​string · uuid · required

The service that answers, in the same project.

strip_prefix
​boolean

Remove the path before the request reaches the service (/api/users arrives as /users). Off by default.

Default: false

PendingDownsizeResponse

A reduction waiting for the end of its billing period. Never billed, never reserved; `applies_at` is a stored date, not recomputed.
applies_at
​string · date-time · required
​PendingResizeTargetResponse · required

The target shape a pending resize would leave the service at, in the same units ResizeResourcesRequest uses.

PendingGrowthResponse

A growth still on its way to the running service. Pay `invoice_id` through the existing invoice-id-keyed payment endpoints if `invoice_status` is still `"pending"` -- once it is `"paid"`, the growth can no longer be cancelled, and it is only a matter of the platform catching up before this stops appearing.
invoice_id
​string · uuid · required
invoice_status
​string · required
​PendingResizeTargetResponse · required

The target shape a pending resize would leave the service at, in the same units ResizeResourcesRequest uses.

capacity_held_until
​string | null · date-time

When the capacity held for this growth expires, or null if nothing is currently held (already settled and waiting for the platform to apply it, or a lapsed hold not yet re-attempted).

PendingReasonResponse

Why a requested port is not open yet: a stable `code` to branch on and a sentence to show.
code
​string · required
message
​string · required

PendingResizeResponse

A subscription's pending resize (`ZC-1332`, V-0794), published on the single service-instance read (`ServiceInstanceResponse::pending_resize`) -- `null` when there is none. A growth and a scheduled reduction can be pending at once (one request may grow memory and shrink cpu); `POST .../resize/cancel` cancels both. See `PendingResizeService`'s own doc comment for exactly what "pending" means.
​
​

PendingResizeTargetResponse

The target shape a pending resize would leave the service at, in the same units `ResizeResourcesRequest` uses.
cpu
​number · float · required
memory_gb
​number · double · required

Memory in GiB, fractional (0.5 = 512 MiB)

storage_gb
​integer · uint64 · min: 0 · required

Storage in GiB.

PlanResponse

description
​string · required
available
​boolean · required

Kernel's verdict on buying this plan in the region named by region_id, and the only one -- nothing is to be derived beside it. True when no region was named, because nothing has been judged and so nothing is refused.

cpu
​number · float · required
id
​integer · int32 · required
memory_gb
​number · double · required
name
​string · required
product_id
​string · uuid · required
sort_order
​integer · int32 · required
storage_gb
​integer · uint32 · min: 0 · required
tax_behavior
​TaxBehaviorResponse · enum · required
Enum values:
exclusive
inclusive
​PlanTermResponse[] · required

The billing terms this plan is sold at (V-0717), monthly first: one entry per term (1, 3, 6 or 12 months) a live Price exists for, each with what one whole term costs and what it saves against buying the same months monthly. Send the chosen entry's cadence_months to POST /checkout and POST /checkout/quote. Empty when the plan has no resolvable price; a single monthly entry means no longer term is sold.

icon
​string | null
price
​number | null · double

This plan's resolved monthly price, already adjusted for the caller's reseller price multiplier if any. Null when no live Price resolves for this plan's (product, hardware profile) combination -- a broken or ambiguous catalog price hides this one plan's price rather than failing the whole listing; see this crate's report for the full rationale.

unavailable_reason
​string | null

Why this plan cannot be bought in the region named by region_id, in kernel's own words, to be rendered exactly as written and never composed, templated or interpolated by a client. Carries no capacity figure of any kind -- no count, percentage, node or cluster. Null exactly when available is true.

PlanTermResponse

One billing term of a plan, as checkout would charge it before codes and tax. Every figure is computed by kernel (`pricing::terms::term_offers`); a client renders them and never derives one from another.
cadence_months
​integer · int16 · min: -32768 · max: 32767 · required

1, 3, 6 or 12.

monthly_equivalent
​number · double · required

price spread over the term's months, rounded to the cent. Display only: nothing is charged at it.

price
​number · double · required

What one whole term costs, already adjusted for the caller's reseller price multiplier; tax per the plan's tax_behavior.

saving
​number | null · double

What this term saves per term against the monthly price times the term's months. Null for the monthly term and when there is no monthly term to compare with. Negative when the longer term costs more.

saving_percent
​number | null · double

saving as a percentage of the undiscounted figure, one decimal.

PlayerActionRequest

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, name
type = object · requires: kind, name
type = object · requires: kind, name
type = object · requires: kind, name
type = object · requires: kind, name
type = object · requires: kind, name, mode
type = object · requires: kind, name, message
type = object · requires: kind, name, item +1 more
type = object · requires: kind, message
Properties for Variant 1:
kind
​string · const · required
Const value: kick
name
​string · required
reason
​string | null
Default: null

PlayerActionResponse

The outcome of one action.
​MinecraftPlayersStatus · required

Every outcome a read or a write can answer with, folded into one status so a client renders a match rather than branching on HTTP status codes that are all 200. Ok is the only variant the roster fields or the action actually succeeding are meaningful under.

PlayerPositionResponse

Where an online player stands.
x
​number · double · required
y
​number · double · required
z
​number · double · required
dimension
​string | null

Namespaced dimension id such as minecraft:overworld; absent when the server did not say.

PodLeaveReason

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: replaced
The kernel deleted it to create the next incarnation (config change or restart).

PortAllocationResponse

allocated_at
​string · date-time · required
external_ip
​string · required
external_port
​integer · uint16 · min: 0 · max: 65535 · required
id
​string · uuid · required
internal_port
​integer · uint16 · min: 0 · max: 65535 · required
is_primary
​boolean · required
is_system
​boolean · required
network_protection_mode
​string · required
protection_managed
​boolean · required

True when protection is auto-managed by the edge (Warden) and manual modes do not apply. Static, comes from the pool this allocation sits on, and is what a panel's "Protected" flag reads.

protection_state
​string · required

Always unknown for a protection-managed allocation on this route, and not_managed otherwise. The live detector value (normal / pressure / attack) is served separately by GET /service-instances/{id}/external-access/live, so this listing never waits on a Warden node.

protocol
​string · required
display
​string | null
​

PortProtocol

string · enum
Enum values:
Tcp
Udp
Both

PostpaidDisclosureResponse

What the OWNER of a trusted organization is told at checkout about postpaid billing (V-0449.04). Present only when the caller owns the organization and it has a credit limit; a non-owner gets [`RestrictionResponse::ContactOwner`] instead and none of these figures. **The first period is not postpaid.** A checkout raises a Pending invoice that is paid up front, because the service starts when it is paid. What is billed afterwards is the RENEWAL of each period, hence the field name.
credit_limit_cents
​integer · int64 · required
headroom_cents
​integer · int64 · required

credit_limit - open_receivable - pending_settlement, never below 0.

open_receivable_cents
​integer · int64 · required

What the organization owes today (ledger receivables, never negative).

pending_settlement_cents
​integer · int64 · required

Renewal periods already running on credit and not yet invoiced.

renewals_billed_postpaid
​boolean · required

true when this purchase's renewals will be billed after each period instead of in advance, as things stand now.

​

Why not, when the organization has a limit and they will still be prepaid.

PowerActionResponse

string · enum
Enum values:
start
stop
restart

What a power task does to the service.

PrepaidReasonResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, requested_cents
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
The renewal would pass the credit limit (owed, plus already granted and not yet invoiced, plus this renewal).
kind
​string · const · required
Const value: over_credit_limit
requested_cents
​integer · int64 · required

PreviousRunRequirementResponse

string · enum
Enum values:
succeeded
failed

What a previous_run guard requires the schedule's own last run to have been.

PriceLineResponse

dimension
​string · required
measurement
​MeasurementResponse · enum · required
Enum values:
flat
allocated
gauge
counter
quantity
recurrence
​RecurrenceResponse · enum · required
Enum values:
recurring
one_off
sort_order
​integer · int32 · required
​PriceTierResponse[] · required
unit
​string · required

PriceSummaryResponse

The pinned `Price` a subscription renews at, rendered verbatim from `price_lines` -- see `SubscriptionResponse::price`'s own doc comment for why nothing here is computed.
cadence_months
​integer · int16 · min: -32768 · max: 32767 · required

The period this Price's rates are quoted per (1 | 3 | 6 | 12).

currency
​string · required
id
​string · uuid · required
​PriceLineResponse[] · required
name
​string · required
term_months
​integer | null · int16 · min: -32768 · max: 32767

PriceTierResponse

flat
​number · double · required
per_unit
​number · double · required
up_to
​number | null · double

PrincipalCopyResponse

One copy (unit) of the service and whether it already carries the mounted key.
carries_key
​boolean · required

true once the copy runs the current pod template, mount included. A copy that has not restarted since API access changed reads false.

unit_key
​string · required

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

ProcessInfoResponse

command
​string · required
cpu_percent
​number · double · required
memory_rss_bytes
​integer · uint64 · min: 0 · required
name
​string · required
pid
​integer · uint32 · min: 0 · required
ppid
​integer · uint32 · min: 0 · required
state
​ProcessStateResponse · enum · required
Enum values:
unknown
running
sleeping
disk_sleep
stopped
zombie
idle
uptime_secs
​integer · uint64 · min: 0 · required

ProcessStateResponse

string · enum
Enum values:
unknown
running
sleeping
disk_sleep
stopped
zombie
idle

ProcessTreeResponse

​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

ProcessTreeStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
ok

ProductCategoryResponse

string · enum
Enum values:
game_server
fleet
application
virtual_machine
license

ProductFamilyView

string · enum
Enum values:
dedicated_workload
pooled_allocation
virtual_machine
license
composite

Mirrors the domain [ProductFamily] 1:1, as its own /v2 response type -- never ProductFamily itself -- so a domain rename or reorder must be a deliberate, matching edit to this From impl rather than a silent wire-format drift. The retired ProductKindResponse (deleted A2-S13c alongside CatalogServiceKind) existed for the same reason.

ProductPublicPortsResponse

What a product offers in public ports.
free_port_slots
​integer · int32 · required

Ports the plan includes at no charge. Zero on a product whose every port is priced.

https_included
​boolean · required

The product's own template already opens its public HTTPS front door (a Static Site, WordPress): the purchase asks no question about internet access and sends no HTTPS endpoint for it. Derived from the template's front_door route, never authored separately.

https_offered
​boolean · required

An HTTPS endpoint can be opened on this product (decision-0308): a free platform host, the customer's own verified domains and automatic TLS, never metered. True for a workload that publishes a site (its template owns a front door, or it declares the custom_domain capability), whatever it says about raw ports above: it needs no free slot and no port price. False for a Worker, a Cron Job and the databases.

offered
​boolean · required

A customer can ask for public ports on this product at all.

priced
​boolean · required

Ports beyond the free ones are allowed and billed by the hour held. A quote with ports requested says what they cost.

ProductResizeDimensionResponse

string · enum
Enum values:
cpu
memory
storage

Mirrors the domain [ResizeDimension], same rule as [ResizePolicyKindResponse] above. Named Product* rather than the bare ResizeDimensionResponse its admin (contracts::product_definitions) and subscriptions (kernel_http_v2_subscriptions::contracts::resize) siblings might suggest: schemars keys /v2's OpenAPI schema components by Rust type name across the whole document, and kernel_http_v2_subscriptions::contracts::resize::ResizeDimensionResponse (a resize QUOTE's dimension: current/requested/growing/available) already held that name (no_v2_schema_component_name_collides, crates/http/src/controllers/mod.rs). This one is a narrower, declarative fact -- which dimensions a PRODUCT'S resize policy grows for free -- so it takes the more specific name rather than leaving both in the tree under one auto-disambiguated name.

ProductResponse

One product as a storefront shows it. `storefront_category` is the domain [`StorefrontCategory`] itself rather than a `/v2`-local mirror, deliberately breaking this module's usual rule (see [`ProductFamilyView`], which exists precisely so a domain rename cannot silently change the wire format). The rule does not apply here, because the wire spelling is not this crate's to choose: each value is the name of a console route directory, and a mirror would add a third place for those three lists to disagree instead of removing one. The domain type carries that contract in its own doc comment, and reaches the generated client as a named string enum.
available_for_order
​boolean · required

Whether this product can currently start a new order

category
​string · enum · required

The category derived from this product's service kind. Kept for existing clients; prefer storefront_category, which is authored per display profile rather than derived from a closed enum whose fallthrough files databases, object storage and static sites all as game servers.

Enum values:
game_server
fleet
application
virtual_machine
license
created_at
​string · date-time · required

When this product was created. There is no separate published_at; console uses this as the product's introduced date for discovery dots (ZC-1302).

enabled
​boolean · required
family
​string · enum · required

The provisioning shape this product deploys as (design doc §4.2/§4.5: A2 matches ProductFamily exhaustively at the one dispatch site that still needs it). Not a mirror of kind or category: several kind values can share one family, and this is the axis future dispatch actually branches on.

Enum values:
dedicated_workload
pooled_allocation
virtual_machine
license
composite
growable_dimensions
​ProductResizeDimensionResponse[] · required

The resource dimensions resize_policy_kind grows for free, if any. Always empty for catalog_plans, and may be empty for plan_change (a product offering only a plan change, no free growth).

Enum values:
cpu
memory
storage
has_free_trial
​boolean · required

Whether this product currently has an enabled, non-expired free-trial configuration. Per-account eligibility is not checked here.

id
​string · uuid · required
minimum_cpu
​number · float · required
minimum_memory_gb
​number · double · required

Fractional gibibytes (2026-09-04 mebibyte migration): 0.5 is 512 MiB.

minimum_storage_gb
​integer · uint64 · min: 0 · required
name
​string · required
product_slug
​string · required

This product's own unique machine name (design doc §4.2 A2: /v2 exposes products.slug instead of the frozen /v1 service-kind enum). Distinct from slug above: slug is the shared addressable segment of the display profile this product presents under, and several products can share one display profile and therefore one slug. product_slug never collides across products.

​object · required

Whether this product sells public ports and how many its plans include (V-0546). Decided by the rule checkout refuses by, so a product shown here as offering ports is never refused them for that reason.

purchase_steps
​PurchaseStepResponse[] · required

The questions buying this product asks, in the order a checkout asks them, always ending in review. Derived here from this product's own bounds, its plan list and the regions this caller's storefront may order into -- never authored, and never a capacity signal: a step is present because the product offers a choice, not because a region happens to have room. A product sold in one region on one plan with a fixed shape is ["review"], which is the whole flow and not a flow with four screens missing.

Enum values:
region
plan
sizing
storage
review
resize_policy_kind
​string · enum · required

How this product permits a resize once bought (ZC-1481): whether it offers a self-service change to another of its own catalog plans (plan_change), free growth on growable_dimensions within its current plan (free_grow), or neither (catalog_plans). Read from this product's own product_definitions row; catalog_plans also when the product has no such row at all, since there is no kind-level fallback to inherit and no row means no self-service resize either way. Exists so a client can tell whether to offer a plan-change control without probing a quote for PlanChangeNotOffered first.

Enum values:
free_grow
catalog_plans
plan_change
slug
​string · required

URL-safe segment addressing this product, from its display profile. Use it instead of the UUID id when building a console link. Several products sharing one display profile share this slug, as they share the name and icon beside it.

​

The display profile's authored teaching copy (ZC-1161): what this product is, how it works, and what people use it for. Null when nobody has authored one yet, in which case the client should fall back to its own equivalent of console's lib/nav/product-about.ts fixture table. Resolved to the platform's one shipped locale; see DisplayProfileAbout's own module doc for how a second locale is meant to be introduced without a wire-format change.

​object | null

about for EVERY authored locale, keyed by IETF locale tag (V-0870): pick the viewer's locale, then en-US, then any entry. Null when nobody has authored one.

icon
​string | null

An icon TOKEN from this product's display profile: a brand slug or a Lucide name, resolved by the client through @zaroz/ui's ResolvedIcon. Distinct from icon_asset_id, which is an operator-uploaded image and wins outright when both are present. Null when nobody authored one, in which case the client falls back to a monogram. PlanResponse.icon is the same field one table over, and is why this one exists: a product had the token on its display profile and this response was simply not carrying it (ZC-1125).

icon_asset_id
​string | null · uuid
maximum_cpu
​number | null · float
maximum_memory_gb
​number | null · double
maximum_storage_gb
​integer | null · uint64 · min: 0
​object | null

Per-locale names keyed by IETF locale tag (V-0870), e.g. {"es-ES": "Almacenamiento de objetos"}. name stays the canonical English name and the fallback: a viewer whose locale has no entry shows name. Null when nobody has translated this product.

starting_price
​number | null · double

Cheapest monthly price across this product's plans, in the platform's single supported currency (design doc §44.3, EUR only for now), already adjusted for the caller's reseller price multiplier if any. Null when the product has no priceable plan -- see this crate's report for why a resolver refusal surfaces as null here rather than an error.

​
​

The authored storefront shelf, from this product's display profile. Every value is a console route directory, so console links this product as /{org}/{project}/{storefront_category}/{slug}. Null when nobody has categorised the display profile yet, in which case fall back to category.

ProfileAboutResponse

A [`ResolvedDisplayProfileAbout`], mirrored explicitly rather than reused, on the same discipline `ProductFamilyView` documents: a domain rename must be a deliberate matching edit here, never a silent wire change.
definition
​string · required
how_it_works
​string · required
​ProfileAboutUseCaseResponse[] · required

ProfileAboutUseCaseResponse

title
​string · required
body
​string · required

ProjectCycleSpendResponse

One currency's worth of [`ProjectStatsResponse::cycle_spend`]. **Which period.** `/v2` has no project-wide or organization-wide billing cycle: every subscription anchors independently. So "the cycle" is resolved per subscription, from money already recorded -- each invoice line states the service window it paid for, and a line counts here exactly when that window covers the instant of the request. A monthly and an annual subscription in the same project both contribute the line covering today. **Which rows.** Invoice lines naming one of the project's live subscriptions, on an invoice that is neither `cancelled` nor `refunded`. A `pending` or `failed` invoice still counts: the service is being delivered and the money is owed, and excluding it would make a project read as free precisely while its payment is failing. **Recorded, never re-derived.** These are the figures on the invoice lines as issued -- no Price is re-read, no discount or reseller multiplier re-applied -- so this can never show an amount that was not charged.
amount_cents
​integer · int64 · required

Money is integer cents on every /v2 DTO, never a float or a formatted string -- same convention as InvoiceSummaryResponse. Pre-tax.

currency
​string · required

ISO 4217, taken from the invoice lines themselves.

tax_amount_cents
​integer · int64 · required

The tax on those same lines. Each line snapshots its own tax at issue time, so this is a sum of stated figures rather than a rate applied to a total.

total_amount_cents
​integer · int64 · required

amount_cents + tax_amount_cents, carried so a caller rendering one number does not have to decide which of the two it wanted.

ProjectResponse

One project as seen by a member of its organization, on the `/v2` console surface (design doc §34.1). `/v2` DTOs are always named `*Request` / `*Response` -- there is no `ProjectView` here on purpose.
created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
organization_id
​string · uuid · required
slug
​string · required

URL-safe identifier, unique within the owning organization. Console routes are /{org}/{slug}.

updated_at
​string · date-time · required
description
​string | null

ProjectStatsResponse

The aggregates behind console's project card badges and project overview tiles, for one project. **A separate operation, not fields on [`ProjectResponse`].** A field cannot be streamed apart from the object carrying it: `resource_count` on `ProjectResponse` would make `GET /projects/{id}` -- and every row of `GET /projects` -- wait on an aggregation over `subscriptions` and `invoice_line_items` before the project's own name could be rendered. Split out, console paints the project immediately and fills these in behind its own suspense boundary.
​ProjectCycleSpendResponse[] · required

What the project is being charged for the service it is receiving right now, one entry per currency. Empty when nothing in the project is under a live service window -- which is the honest answer for a project with no subscriptions and for one whose subscriptions have all lapsed, never an error. In practice this holds exactly one entry: the platform transacts in EUR only.

project_id
​string · uuid · required
resource_count
​integer · int64 · required

How many live subscriptions this project holds. A "resource" here is a subscription -- design doc §34.1 puts Project directly above Subscription, so one purchase is one row -- not a resources row (which would count a single VPS as its VM plus each of its volumes) and not a service instance (which would drop a paid subscription still awaiting activation). "Live" is deleted_at IS NULL and nothing more, the same rule that decides whether the project can be deleted and the same rule GET /console/subscriptions?project_id= pages with, so this number is exactly how many rows the caller sees on clicking through.

PublicCaptchaConfigResponse

`GET /api/v2/public/captcha/config/{hostname}`, ZC-1192. Unauthenticated, so it carries strictly less than `CaptchaConfigResponse` above: no `secret_key_set` (a bare "yes/no" about a secret's presence has no reason to exist on a route nobody signed in can act on), no `updated_at`, and `site_key` is `None` rather than an empty string when nothing is configured. Built from `CaptchaRequirements` (`crates/domain/system/src/domain/captcha/mod.rs`), which is itself the secret-free projection every scope's config collapses to -- there is no field on this type a caller could not already learn by trying to load the login page and inspecting the widget it renders.
enabled
​boolean · required

Whether any action on this host requires a solved challenge at all. false for a missing config, a disabled one, or one missing a key -- see CaptchaConfig::is_active.

protected_actions
​string[] · required

Stable snake_case action names the widget is required for. Empty when enabled is false.

provider
​string · required

Provider wire value, e.g. turnstile. Always present, even when enabled is false, so a client can wire up the right widget component ahead of a provider ever being turned on.

site_key
​string | null

Safe to render in a browser; this is the half of the key pair that is meant to be public. None exactly when enabled is false.

PublicIncidentResponse

An ongoing platform incident, as published to anyone with no account at all. Carries only what an outage banner has to render.
title
​string · required

The headline to show.

id
​string · uuid · required

A stable key for deduplicating a poll or remembering a dismissal.

started_at
​string · date-time · required

When the incident began, so the banner can date itself.

message
​string | null

Further detail, if the operator published any.

PublicMaintenanceResponse

An upcoming maintenance window, as published to anyone with no account at all. Carries only what a maintenance banner has to render.
title
​string · required

The headline to show.

id
​string · uuid · required

A stable key for deduplicating a poll or remembering a dismissal.

scheduled_at
​string · date-time · required

When the maintenance is scheduled to begin.

expected_duration_minutes
​integer | null · int32

How long it is expected to last, if staff said.

PublicPlanResponse

One plan as a logged-out storefront visitor sees it. Field-identical to `PlanResponse`, and that is the reviewed answer rather than an oversight: a plan row is already display-only. It carries no hardware-profile id, no cost basis, no reseller identifier, and no per-account state -- the price is the single resolved figure the pricing code already computed for display, not the `Price` rows behind it. There is nothing on it a signed-in customer may see that a shopper may not. It stays a separate type anyway so that a future console-only plan field cannot become world-readable by being added in one place.
description
​string · required
cpu
​number · float · required
id
​integer · int32 · required
memory_gb
​number · double · required
name
​string · required
product_id
​string · uuid · required
sort_order
​integer · int32 · required
storage_gb
​integer · uint32 · min: 0 · required
tax_behavior
​TaxBehaviorResponse · enum · required
Enum values:
exclusive
inclusive
icon
​string | null
price
​number | null · double

This plan's resolved monthly price, already adjusted for the requested reseller storefront's price multiplier if one was named. Null when no live Price resolves for this plan's (product, hardware profile) combination -- a broken or ambiguous catalog price hides this one plan's price rather than failing the whole listing.

PublicProductResponse

One product as a logged-out storefront visitor sees it. Deliberately narrower than `ProductResponse`. Three groups of fields are dropped rather than forwarded, each for its own reason: - `enabled`: this mount never serves a disabled product at all (the listing's repository query filters it in SQL, and the two single-product reads 404 through `is_publicly_listable`), so the flag would be the constant `true`. Serving it would be an invitation for a future change to start listing disabled products and let the client decide, which is exactly how an unreleased product leaks from a storefront. - `minimum_cpu` / `minimum_memory_gb` / `minimum_storage_gb`: the provisioning floor a resource is created with. No public caller reads them (`landing/src/lib/kernel.ts` never fetched them, and `landing/src/types/catalog.ts` records their absence as deliberate), and they describe how the platform builds a workload, not what the shop sells. - `maximum_cpu` / `maximum_memory_gb` / `maximum_storage_gb`: the same, plus these describe the per-node capacity envelope the fleet is willing to shape a workload into. That is infrastructure sizing information, not storefront copy. **`bundle_discount_tiers` was a fourth, and is now gone for a different reason: the thing it described no longer exists.** A term is its own Price (`migrations/2026-09-05-000003_price_per_term`), so "a longer term is cheaper" is not a percentage a storefront applies to `starting_price` -- it is a second Price with its own rate and its own `cadence_months`. Serving a percentage now would be serving a figure no checkout would honour, which is worse than serving nothing. **What replaces it is a listing, not a field here.** The terms on offer are the product's live Prices grouped by `cadence_months`, and kernel already writes the label for each one -- `cadence_label`, in `create_subscription_checkout_service`, which the checkout quote serves as `QuoteResponse::cadence_label`. Nothing on this mount enumerates a product's Prices yet, and `POST /api/v2/console/checkout` still quotes `cadence_months: 1` unconditionally, so a term selector has no route to be built from on `/v2` at all today. That is a gap this contract records rather than papers over with a number. Everything kept is information a shop window must show: what the product is, what it is called, its icon, whether it can be bought right now, its "from" price with the right tax treatment, and whether a trial is on offer.
available_for_order
​boolean · required

Whether this product can currently start a new order

category
​ProductCategoryResponse · enum · required
Enum values:
game_server
fleet
application
virtual_machine
license
family
​string · enum · required

The provisioning shape this product deploys as. See ProductResponse's own family field (the /v2/console mount) for the full rationale; kept here too because it is shop-window information, not an account fact.

Enum values:
dedicated_workload
pooled_allocation
virtual_machine
license
composite
has_free_trial
​boolean · required

Whether this product currently has an enabled, non-expired free-trial configuration on this storefront. Per-account eligibility is not checked here, and cannot be: this caller has no account.

id
​string · uuid · required
name
​string · required
product_slug
​string · required

This product's own unique machine name. Not the display profile's shared slug (this mount does not expose that one at all today) -- see ProductResponse::product_slug (the /v2/console mount) for the distinction between the two.

​

The display profile's authored teaching copy (ZC-1161): what this product is, how it works, and what people use it for. Null when nobody has authored one yet. This is shop-window information a logged-out visitor needs at least as much as a signed-in customer does -- it is the copy an empty product page teaches with -- so it is carried onto this narrower response rather than dropped with the account-scoped fields above.

​object | null

about for EVERY authored locale, keyed by IETF locale tag (V-0870): pick the viewer's locale, then en-US, then any entry. Null when nobody has authored one.

icon
​string | null

An icon TOKEN from this product's display profile: a brand slug or a Lucide name, resolved by the client through @zaroz/ui's ResolvedIcon. Distinct from icon_asset_id, which is an operator-uploaded image served from GET /assets/{id} on this same public mount and wins outright when both are present. Null when nobody authored one, in which case the client falls back to a monogram. Same field as ProductResponse.icon (the /v2/console mount) and PublicPlanResponse.icon below -- this response carried icon_asset_id without it, which was the buy-page half of ZC-1144.

icon_asset_id
​string | null · uuid
​object | null

Per-locale names keyed by IETF locale tag (V-0870), e.g. {"es-ES": "Almacenamiento de objetos"}. name stays the canonical English name and the fallback: a viewer whose locale has no entry shows name. Null when nobody has translated this product.

starting_price
​number | null · double

Cheapest monthly price across this product's plans, in the platform's single supported currency (design doc §44.3, EUR only for now), already adjusted for the requested reseller storefront's price multiplier if one was named. Null when the product has no priceable plan.

​

PublicResellerResponse

A reseller as an anonymous visitor's browser sees it, resolved from the hostname it was served on. Branding only: everything a storefront needs to paint itself before anybody has logged in, and nothing else.
brand_color
​string · required
custom_plan_selector_enabled
​boolean · required
hostname
​string · required
id
​string · uuid · required
name
​string · required
og_image_mode
​string · required

One of platform, logo, custom, disabled.

discord_invite_url
​string | null
icon_asset_id
​string | null · uuid
resolved_og_image_asset_id
​string | null · uuid

See [ResellerResponse::resolved_og_image_asset_id].

PurchaseStepResponse

string · enum
Enum values:
region
plan
sizing
storage
review

Mirrors the domain [PurchaseStep] 1:1, as its own /v2 response type -- never [PurchaseStep] itself -- so a domain rename or reorder must be a deliberate, matching edit to this From impl rather than a silent wire-format drift. Same rule and same reason as [ProductFamilyView].

The five spellings match console's StepId union exactly (console/components/purchase/registry.ts), because that registry is a Record<StepId, PurchaseStep> and a value it has no entry for is a type error there rather than an empty screen.

QuoteDiscountResponse

The applied discount code, priced against this basket. This is also the discount *lookup*: a client that wants to know whether a code is usable sends it with the configuration and reads this back, rather than asking a second endpoint that cannot see the basket and therefore cannot judge `min_applicable_price`.
description
​string · required

What the code does, in kernel's own words, e.g. "20% off for your first 3 billing cycles". Composed by DiscountCode::describe in the domain so the quote, the invoice and the admin preview cannot drift into three sentences for one code.

amount_cents
​integer · int64 · required

Negative, so the ticket is a sum and the panel renders it with the sign it arrived with rather than subtracting anything.

code
​string · required

The code as the catalog stores it, not as the customer typed it.

QuoteLineResponse

One priced thing on the ticket.
description
​string · required

Composed by kernel, and identical to what the invoice line will say -- the same CreateSubscriptionCheckoutServiceImpl::base_line_description writes both. A customer reconciling a quote against an invoice compares the words as well as the money.

amount_cents
​integer · int64 · required

Before any discount code.

QuoteRequest

`POST /api/v2/console/checkout/quote`'s request. [`CheckoutRequest`] minus the two fields that only describe a purchase that is actually happening: `name` and `slug`. A customer moving a sizing control has not named anything yet, and requiring a name before showing a price would put the price last -- after the decision it is supposed to inform. Every remaining field is here because it changes the number or decides which number applies, and each one means exactly what it means on `CheckoutRequest`. Send the same body to both endpoints (plus a name and slug) and the total quoted is the total invoiced.
plan_id
​integer · int32 · required
product_id
​string · uuid · required
cadence_months
​integer | null · int16 · min: -32768 · max: 32767

The billing term to quote. See [CheckoutRequest::cadence_months]: omitted is the monthly term, and the quote's total is one whole term.

Default: null
discount_code
​string | null

An optional discount code, exactly as the customer typed it. A code that cannot be used is refused here exactly as it would be at checkout, rather than quietly quoting the undiscounted price.

Public ports to price. See CheckoutRequest::endpoints: the same rules refuse the same requests here. Each comes back as an informational line of amount zero, and metered_estimate carries the worst monthly cost. total_cents never includes them.

hardware_profile_id
​integer | null · int32

See CheckoutRequest::hardware_profile_id's own doc comment.

Default: null
operating_system_id
​string | null · uuid

See CheckoutRequest::operating_system_id's own doc comment.

Default: null
organization_id
​string | null · uuid

Which organization would be buying. Decides the reseller price multiplier and the tax jurisdiction, so it is quote input rather than bookkeeping.

project_id
​string | null · uuid

Which project the purchase would be filed under.

Optional, mirroring [CheckoutRequest::project_id]. When supplied it is still checked against the buying organization -- nothing is written, but a quote is an answer about a purchase this caller could make, so naming somebody else's project is refused here too. When absent, nothing is checked and nothing is resolved: unlike checkout, a quote never creates the buying organization's default project, because a quote is documented to write nothing at all.

region_id
​integer | null · int32

Required for every family except a licence. See CheckoutRequest::region_id's own doc comment.

Default: null
​

The size to price. Omitting it quotes the plan as authored.

QuoteResourcesResponse

The priced shape, in the same units [`CheckoutResourcesRequest`] accepts.
cpu
​number · float · required
memory_gb
​number · double · required

Memory in GiB, fractional (0.5 = 512 MiB)

storage_gb
​number · double · required

Storage in GiB. Zero means no volume at all.

QuoteResponse

What a purchase would cost, in cents, itemized. Cents and `i64`, matching `InvoiceResponse` exactly (see that type's own note on why `/v2` does not put decimal strings on the wire): the figures a customer compares are the quote and the invoice, so they are quoted in the same unit and arrive as the same kind of number. **Every figure here is a field, and none is derived from another.** A console that had to work out what a code took off, what tax was charged, or what the line under the total was for would be reimplementing the pricing rules in TypeScript, and its answer would start disagreeing with the invoice the first time kernel prorates, clamps a discount or prices a region differently. The breakdown exists so nobody is ever tempted. The customer-facing ticket adds up, in this order and no other: ```text subtotal_cents (== the sum of `lines`) + discount.amount_cents (negative, or the field is absent) + tax.amount_cents (only when `tax.included_in_lines` is false) = total_cents ``` On a plan authored `tax_behavior: inclusive`, `tax.included_in_lines` is true, every line above already contains the tax, and the chain collapses to `subtotal_cents + discount.amount_cents = total_cents` with nothing added for tax. The tax figure is then a statement about what is inside the price ("of which VAT"), not something to add. That is the only thing that flag changes: the sentence, never this arithmetic. `amount_cents` is not a term of that chain. It is always the establishing invoice's `amount` -- the net, pre-tax figure `TaxBehavior::split` derives -- regardless of `tax_behavior`. On an `Exclusive` plan the two happen to coincide (`subtotal_cents + discount.amount_cents == amount_cents`, because every figure above is already net), but on an `Inclusive` one `amount_cents` is less than `total_cents` by the tax the advertised price already contains, precisely because the lines, subtotal and discount above are gross there and `amount_cents` is not.
amount_cents
​integer · int64 · required

After the discount, in the invoice's own terms: always the net, pre-tax figure, never the plan's advertised (possibly tax-inclusive) price. This is the establishing invoice's amount, and the figure the two can be reconciled on, for both tax behaviours. See this struct's own doc comment for why it is not a term of the customer-facing additive chain on an Inclusive plan.

available_cents
​integer · int64 · required

balance - required_cents. Compared against required_cents (not against this purchase's own total) by the caller: kernel does not know here whether this purchase itself will reserve anything, only what the organization's standing already looks like.

cadence_label
​string · required

How to read the figures aloud, e.g. "per month, billed monthly". Kernel's own words: it is a claim about a billing term, and a client assembling one from a month count would be deciding what that count means.

cadence_months
​integer · int16 · min: -32768 · max: 32767 · required

The billing term quoted, in months (1, 3, 6 or 12): the one asked for, or 1 when none was. Every figure below is one whole term (V-0717).

currency
​string · required

The currency every figure below is in.

due_now_cents
​integer · int64 · required

What the customer pays now, which is not always total_cents: a setup fee, a term paid up front or a prorated first period all move it. Rendered, never derived. Equal to total_cents for every purchase this endpoint can price today, and a field anyway, because the day it stops being equal must not be the day a console starts lying.

​QuoteLineResponse[] · required

What is being bought, described the way the invoice will describe it.

Exactly one line today, because a /v2 checkout issues exactly one invoice line (InvoiceLineItemKind::BaseSubscription). It is a list rather than a single field so that a setup fee, a term bundle or a second priced component does not have to reshape the contract, and so a client renders whatever arrives instead of a fixed row.

required_cents
​integer · int64 · required

ZC-1324 org trust S5 (§8.4): max(organization_floor, already-reserved) -- the buying organization's own standing, not this purchase's price. Present on every quote, not only a Measurable one: an unmeasurable product still explains the organization's floor this way (§8.2's closing paragraph).

​object · required

The shape that was priced -- the customer's when they sent one, the plan's when they did not. Echoed so a console showing "2 vCPU, 4 GiB, no disk -- EUR 12.40" is reading both halves from the same answer rather than pairing a server price with its own local idea of the size.

subtotal_cents
​integer · int64 · required

The sum of lines, before any discount code.

​QuoteTaxResponse · required

Tax, as data rather than as something to work out.

The client must never derive any of this from a billing country: which country applies, whether a business is reverse-charged, and what the rate is on the day are all kernel's decisions, taken by TaxResolutionService::resolve.

total_cents
​integer · int64 · required

What the customer pays for one billing cycle.

​

The code that was applied, what it does, and what it took off. None when no code was sent; a code that was sent and cannot be used is a refusal, never a silently absent discount.

​

What the requested public ports can cost, present only when the request named some. Ports are billed by the hour held, monthly in arrears, so this is a worst case (every priced port held a whole month), shown beside the ticket and never added to total_cents or due_now_cents.

​

V-0449.04: postpaid disclosure, present only for the OWNER of an organization with a credit limit. See crate::contracts::standing::PostpaidDisclosureResponse.

​

The organization's own current restriction, if any -- None when its standing is Good. Kernel supplies the reason and the numbers; the frontend supplies the words (§8.4).

QuoteTaxResponse

Tax, as data rather than as something to work out. The client must never derive any of this from a billing country: which country applies, whether a business is reverse-charged, and what the rate is on the day are all kernel's decisions, taken by `TaxResolutionService::resolve`.
amount_cents
​integer · int64 · required
included_in_lines
​boolean · required

True when lines already contain this tax (a plan authored tax_behavior: inclusive). Changes the sentence, never the maths -- see [QuoteResponse].

rate_basis_points
​integer · int32 · required

The rate as an integer, e.g. 2100 for 21%. For rendering only: amount_cents is the authority, and multiplying by this would be computing a price.

label
​string | null

Kernel's word for it -- "IVA", "VAT", "Reverse charge". None when no rate resolved at all, which is not the same as a zero rate.

note
​string | null

A sentence that has to appear beside the figures when one applies, such as the intra-community reverse-charge notice. Written by kernel because it is a legal statement about this specific transaction.

RangeSettingDto

Mirrors [`RangeSetting`] by hand.
default
​integer · uint32 · min: 0 · required
max
​integer · uint32 · min: 0 · required
min
​integer · uint32 · min: 0 · required

RecurrenceResponse

string · enum
Enum values:
recurring
one_off

RedeemMyWalletCodeRequest

code
​string · required

The code as the customer typed it. Case and separators are normalised server-side.

RedeemMyWalletCodeResponse

The credit a redeemed code produced.
amount_cents
​integer · int64 · required
currency
​string · required
entry_id
​string · uuid · required

The wallet entry the value landed in.

RefreshTokenRequest

refresh_token
​string · required

RefreshTokenResponse

A rotated access/refresh token pair. `token` is bare, same as [`SessionResponse::token`].
refresh_token
​string · required
session_id
​string · uuid · required
token
​string · required

RegionHardwareResponse

One hardware tier inside a region: how the hardware is described to a customer, and kernel's can-or-cannot verdict on it for this product. Held to the region row's own rule: no capacity, stock or rate figure of any kind -- nothing a percentage could be computed from -- and nothing about placement internals (node selectors, storage classes).
available
​boolean · required

true when a purchase on this tier would find room and a live price right now.

disk_type
​string · required

The disk technology, e.g. nvme.

id
​integer · int32 · required

What a client sends back as CheckoutRequest.hardware_profile_id.

name
​string · required
description
​string | null
cpu_model
​string | null

The processor model, when the operator named one.

unavailable_reason
​string | null

Why not, in kernel's own words; null exactly when available.

RegionResponse

One region this product is sold in, in the order the catalog sorts them, carrying kernel's verdict on whether it can be bought into right now. **This row deliberately carries no capacity, stock or pricing figure**, unlike `/v1`'s `PublicRegionView`. `purchase/21-steps.md` §3 states the rule in its operative form: *do not add a field here that a percentage could be computed from*, because the failure mode is not somebody adding `utilisation: 0.87`, it is somebody adding two numbers whose ratio is one. That has already happened once -- `dashboard/components/region-selector.tsx` renders a stock bar summing every disk beside a "Full" badge counting only schedulable nodes, so one dead node reads as Full at 28 % -- and the two figures disagree because they were computed twice. `available` plus `unavailable_reason` is therefore the whole of what a client learns about capacity: can-or-cannot, and a sentence. The hardware tiers under `hardware` are described (name, processor, disk) so a customer can choose between them, as `/v1` let them (decision-0347), and carry the same can-or-cannot verdict -- never a capacity figure or a placement internal. **Refused regions are returned, not dropped.** This listing used to make availability the membership rule for the list, which meant a region that went full silently vanished; §3.1 names the cost -- *"Somebody who came for Singapore needs to be told Singapore is full, not left wondering whether the product sells there at all"*. Two things are still membership: a region no plan of this product may use a profile in, and a region this caller's storefront may not order into.
available
​boolean · required

Kernel's verdict, and the only one. true when a purchase into this region would find hardware with room and a live price right now. Nothing is to be derived alongside it: a second verdict computed on the client could only ever disagree with the real one.

country_code
​string · required

ISO 3166-1 alpha-2, for flag rendering.

​RegionHardwareResponse[] · required

The hardware tiers a customer may choose between in this region (decision-0347: /v1 let a customer pick, say, "Helsinki Budget" or "Helsinki Performance", each with its own Price). Send the chosen one's id back as CheckoutRequest.hardware_profile_id (and to the plan listing to price plans on it); omit it to let kernel place the service on the cheapest tier that fits. Empty for a product placed on no hardware (object storage, a registry).

id
​integer · int32 · required

What a client sends back as CheckoutRequest.region_id.

latitude
​number · float · required
longitude
​number · float · required
name
​string · required
ping_endpoint
​string · required

A URL a browser can time a request against, so the console can show the customer their own latency to each region.

slug
​string · required
unavailable_reason
​string | null

Why not, in kernel's own words, to be rendered as written (guard rail R2). Never composed, templated or interpolated by a client, and never containing a number, a node, a cluster or a percentage. null exactly when available is true.

RegistryCredentialView

created_at
​string · date-time · required
id
​string · uuid · required
name
​string · required
scope
​string · required

pull, push or admin.

username
​string · required

The username to log in with.

last_used_at
​string | null · date-time

RegistryCredentialsRequest

One half of a registry login. **Never serialized**, only accepted.
password
​string · required
username
​string · required

RegistryCredentialsResponse

​RegistryCredentialView[] · required

RegistryRepositoriesResponse

​RegistryRepositoryView[] · required

RegistryRepositoryView

name
​string · required

Full name, <namespace>/<path>.

size_bytes
​integer | null · uint64 · min: 0
tag_count
​integer | null · uint64 · min: 0

RegistryResponse

​RegistryView · required

RegistrySource

An image in an OCI registry, and the username that pulls it. **There is no password field, and that is the security property.** See this module's header.
host
​string · required

ghcr.io, docker.io, registry.example.com:5000. Always present: a reference with no host is normalised to [DOCKER_HUB_HOST] on the way in, so a reader never has to know the defaulting rule.

repository
​string · required

acme/worker, library/nginx. Never carries the host.

tag
​string · required

The mutable name the customer chose.

credential_username
​string | null

The username half of the stored pull credential. None is an anonymous pull.

digest
​string | null

The immutable name of what was actually pulled, when the stored reference pins one.

Always None for anything this platform wrote, because nothing resolves a tag to a digest for a third-party registry: the only manifest client in the codebase authenticates with HTTP basic, and ghcr.io, Docker Hub and Quay all require the Bearer challenge exchange. Some only for a reference a customer pinned themselves.

RegistryTokenErrorBody

​RegistryTokenErrorItem[] · required

RegistryTokenErrorItem

code
​string · required

UNAUTHORIZED, DENIED or UNSUPPORTED.

message
​string · required

RegistryTokenResponse

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.

RegistryUsageResponse

​RegistryUsageView · required

RegistryUsageSampleView

egress_bytes
​integer · uint64 · min: 0 · required
stored_bytes
​integer · uint64 · min: 0 · required
window_start
​string · date-time · required

RegistryUsageView

egress_bytes
​integer · uint64 · min: 0 · required

Bytes served out of the platform in the window (billed once the registry's Price carries an egress line).

from
​string · date-time · required
gap_hours
​integer · uint32 · min: 0 · required

Hours with no measurement, filled with the nearest earlier one.

​RegistryUsageSampleView[] · required

Hourly measurements, oldest first.

storage_gib_months
​string · required

Average stored GiB over the whole window, as a decimal string.

to
​string · date-time · required

RegistryView

access
​string · required

What the registry allows right now: open, read_only (over the cap: no pushes), suspended (unpaid or moderated) or deleted.

cap_bytes
​integer · uint64 · min: 0 · required
created_at
​string · date-time · required
endpoint
​string · required

The host to docker login to.

id
​string · uuid · required
image_prefix
​string · required

<endpoint>/<namespace>: what an image reference starts with.

label
​string · required
namespace
​string · required

The namespace every repository of this registry lives under.

region_id
​integer · int32 · required
subscription_id
​string · uuid · required
updated_at
​string · date-time · required
purge_at
​string | null · date-time

When the registry's images will be purged, once deleted.

stored_bytes
​integer | null · uint64 · min: 0

Latest measured stored bytes (logical); absent before the first measurement (measured hourly).

ReinstallVpsRequest

Body of the VPS reinstall route. Both fields are required and neither has a default: this destroys every byte on the machine's disk, so a request that lost a field in transit must be a 400 and never a wipe. `deny_unknown_fields` for the same reason: a client that misspells `confirm` is refused rather than defaulted.
confirm
​boolean · required

Must be true. Reinstalling erases all data on the disk and cannot be undone; the client is expected to have shown that to the person before sending it.

os_image
​string · uuid · required

The operating system to install: the id of one entry of GET /api/v2/catalog/products/{id}/operating-systems. May be the image already installed.

ReinstallVpsResponse

What queuing a reinstall did.
operation_id
​string · uuid · required

The queued rebuild. Poll GET /api/v2/service-instances/{id}/operations/{operation_id}.

os_image
​string · uuid · required

The operating system the machine will be rebuilt with.

resource_id
​string · uuid · required
service_instance_id
​string · uuid · required

ReplaceFirewallRulesRequest

The whole ruleset, replacing whatever is stored.
​CreateFirewallRuleRequest[] · required

ReplaceGrantsRequest

`PUT .../principal/grants`: the whole grant list, replacing the old one. An empty list removes every grant. Duplicates are kept once.
​GrantDto[] · required

ReportClientErrorRequest

One crash, as the browser saw it. Field-for-field identical to `/v1`'s `ReportClientErrorBody`, which is what `dashboard`'s BFF (`app/api/client-errors/route.ts`) already sends: this route is a transport replacement for a deleted one, so a shape change here would have been a second, silent breakage on top of the one it is fixing.
error_id
​string · required

The reporter's own correlation id for this crash, echoed back to the user so a support conversation can name it. Caller-supplied and caller-shaped; see this module's doc comment for why it is not a key.

message
​string · required

The error message. Truncated to 2000 characters on the way in.

digest
​string | null

Next.js's build-time error digest, when the crash came from a server component and the framework withheld the real message from the browser. Absent for a plain client-side throw.

stack
​string | null

The stack trace. Truncated to 8000 characters on the way in.

url
​string | null

The page the crash happened on.

user_agent
​string | null

The browser, as the browser describes itself.

ReprovisionServiceInstanceRequest

`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.

ReprovisionServiceInstanceResponse

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

RequestPortAllocationRequest

Ask for one new external port on this service instance (`docs/V2Plan/tracker/issues/ZC-0302-external-access-v2-has-no-write-path.md`). Two mutually exclusive shapes in one body, which is `/v1`'s own shape kept deliberately: with `match_port` false the caller names the port their software listens on inside the container and the public port is drawn at random; with `match_port` true neither is named and the drawn port becomes both, so the customer configures their software to the number they are given. `internal_port` is required in the first case and ignored in the second. On an instance served from an exclusive (dedicated) IP the public port equals the requested port, which is the point of buying one; `internal_port` there must be between 1024 and 65535, because everything below that is reserved on the node the traffic lands on.
protocol
​PortProtocol · enum · required
Enum values:
Tcp
Udp
Both
internal_port
​integer | null · uint16 · min: 0 · max: 65535

Required when match_port is false. Ignored when it is true.

match_port
​boolean
Default: false

ResetPasswordRequest

Sets a password from an emailed reset token. Carries no email address on purpose. The token alone decides which account and which address are affected (see `PasswordManagementService::reset`), so an address in the body would be either ignored or, worse, trusted.
password
​string · required
token
​string · required

ResizeBillingResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, subtotal_cents, tax_cents +2 more
type = object · requires: kind
Properties for Variant 1:
At least one dimension grows. `total_cents` is what `POST .../resize` would invoice right now for exactly this shape.
currency
​string · required
kind
​string · const · required
Const value: growth
subtotal_cents
​integer · int64 · required
tax_cents
​integer · int64 · required
total_cents
​integer · int64 · required

ResizeDimensionResponse

available
​boolean · required

Whether stock currently allows this dimension's growth. Always true for a dimension that is not growing. This is a live read, taken with no hold and no lock, so it can change by the time POST .../resize is called -- the authoritative answer is that call's own refusal, not this field.

current
​number · double · required
dimension
​string · required

"cpu", "memory" or "storage".

growing
​boolean · required

Whether this dimension is increasing (billed now). A dimension whose requested is below current shrinks at downsize_applies_at; the same request may do both on different dimensions.

requested
​number · double · required

ResizeInvoiceResponse

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.
amount_cents
​integer · int64 · required
currency
​string · required
id
​string · uuid · required
status
​string · required

ResizePlanChangeRequest

`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

ResizePolicyKindResponse

string · enum
Enum values:
free_grow
catalog_plans
plan_change

Mirrors the domain [ResizePolicyKind] discriminator (ZC-1481) as its own /v2 response type -- same rule and reason as [PurchaseStepResponse] below: a domain rename must be a deliberate, matching edit here rather than a silent wire-format drift.

ResizeQuoteRequest

​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.

ResizeQuoteResponse

​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.

ResizeRequest

​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.

ResizeResourcesRequest

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.
cpu
​number · float · required
memory_gb
​number · double · required

Memory in GiB, fractional (0.5 = 512 MiB)

storage_gb
​integer · uint64 · min: 0 · required

Storage in GiB.

ResizeResponse

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
​

ResolvedJarResponse

A jar Modrinth's hash lookup matched, projected to what a panel renders: enough for an icon, a title and a version.
title
​string · required
game_versions
​string[] · required
loaders
​string[] · required
project_id
​string · required
project_slug
​string · required
version_number
​string · required
icon_url
​string | null

ResourceId

string · uuid

ResourceKindResponse

string · enum
Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

ResourceLifecycleTransitionResponse

One workload a lifecycle verb acted on.
kind
​string · enum · required

deployment or virtual_machine: the only kinds that have a power state.

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
​required

The recorded status before the verb.

resource_id
​string · uuid · required

The same id GET /subscriptions/{id}/resources and the status stream's frames carry, so a client can match this entry to the row it renders.

​required

The recorded status the verb left in its place: running for a start, stopping for a stop (the settled suspended arrives later on the status stream), and for a restart the same as previous_status, because a restart cycles the workload without changing what it is meant to be doing. A restart of a stopped service does not turn it on, and saying running here would claim it had.

ResourceLogsResponse

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

The container these lines came from. Always populated, including when the request named none and the workload runs exactly one.

containers
​string[] · required

Every container on this workload's current pod, so a panel can offer a container picker without a second call. A workload with one container yields one entry, and a panel should render no picker for it rather than a select with a single option.

coverage
​string · required

Kernel's own sentence about what was actually read, to be shown beside the results rather than paraphrased or composed in the browser (R2).

It exists because of one specific way this endpoint can mislead: there is no log store on this platform, so a search covers only the window node log rotation still holds. A customer searching for a line that scrolled out of that window must be told what was searched, not shown an empty result that reads as "it never happened".

​LogLineResponse[] · required

Oldest first, which is reading order for a log.

truncated
​boolean · required

true when the read hit its own bounds, so more output exists on either side of what was returned.

next_cursor
​string | null

Hand this back as cursor to continue forwards from where these lines stopped. Opaque: its spelling may change.

search
​string | null

Echoed back so a client can tell a response to its current filter from a late response to the previous one.

ResourceMetricsResponse

Every series for one resource over one window.
coverage
​string · required

Kernel's own sentence about what was actually read, to be shown beside the charts rather than paraphrased or composed in the browser (R2). The same field, for the same reason, as [ResourceLogsResponse::coverage] -- named precisely because 00-program.md 15.3.1 calls it LogSearchResponse, which does not exist, and a citation drift is how the next reader loses ten minutes.

It exists because of one specific way this endpoint misleads. Every window returns [METRIC_BUCKETS] buckets whether or not anything was sampled, so a resource with no samples at all comes back as four series of sixty nulls -- four empty axes, no summary, and nothing said. A consumer cannot tell that apart from a workload that was genuinely stopped for the whole window, and it must not try: deciding which from the points is exactly the reduction R2 forbids.

So kernel says which it is. The distinction the sentence carries is "no sample was stored" versus "usage was zero", and only the server knows it.

from
​string · date-time · required

Start of the first bucket.

resolution_seconds
​integer · uint32 · min: 0 · required

Bucket width, in seconds. (to - from) / resolution_seconds is the length of every points array in series.

​MetricSeriesResponse[] · required
to
​string · date-time · required

End of the window. Exclusive: no bucket contains it.

window_minutes
​integer · uint32 · min: 0 · required

The window actually served, echoed so a client can tell what it got without re-deriving it from its own request.

ResourceResponse

One resource belonging to a subscription. `/v2` DTOs are always named `*Request`/`*Response` (design doc `§43.4`). **`role` and `is_declared_primary` replace `is_primary` as the modelled way to ask "which one is the main one"** (design doc `§9.1`, A2-S9b). A subscription may have several resources; at most one is ever the declared primary. `is_declared_primary` is computed through `kernel_core::domain::provisions::resolve_declared_primary`, the same function A2-S9b re-pointed the terminal bridge and its six other consumers onto -- never by picking the first item in `data` or assuming `kind == Deployment`.
created_at
​string · date-time · required
hidden
​boolean · required

Whether this resource is hidden from a customer-facing resource list by convention today (sidecar provisions). Exposed rather than filtered out, so this surface never silently drops a resource the caller might reasonably expect to see accounted for.

id
​string · uuid · required
is_declared_primary
​boolean · required

Whether this is the one resource this subscription's product family declares as primary (its ProductDefinition.primary_role), falling back to the legacy is_primary flag only for the families that declare no primary role at all (design doc §9.1). At most one resource in this response ever has this set to true.

kind
​ResourceKindResponse · enum · required

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
role
​string · required

The resource's own declared role (e.g. "server", "sidecar", "sftp", "namespace"). Backfilled from is_primary for rows that predate this column -- see Resource::role's own doc comment.

​ResourceStatusResponse · required
updated_at
​string · date-time · required

ResourceStatusFrameResponse

One resource, as `GET /subscriptions/{id}/resources` describes it, plus what is only knowable live.
​ContainerStatusResponse[] · required

Per-container status for a Deployment's pod. Omitted, not empty, for every non-Deployment resource and for any Deployment the kernel could not reach inside the bounded timeout -- an empty array would claim the pod has no containers.

created_at
​string · date-time · required
hidden
​boolean · required

Whether this resource is hidden from a customer-facing resource list by convention today (sidecar provisions). Exposed rather than filtered out, so this surface never silently drops a resource the caller might reasonably expect to see accounted for.

id
​string · uuid · required
is_declared_primary
​boolean · required

Whether this is the one resource this subscription's product family declares as primary (its ProductDefinition.primary_role), falling back to the legacy is_primary flag only for the families that declare no primary role at all (design doc §9.1). At most one resource in this response ever has this set to true.

kind
​ResourceKindResponse · enum · required

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
role
​string · required

The resource's own declared role (e.g. "server", "sidecar", "sftp", "namespace"). Backfilled from is_primary for rows that predate this column -- see Resource::role's own doc comment.

​ResourceStatusResponse · required
updated_at
​string · date-time · required
​

Present while the workload is stalling on memory reclaim, or was recently. Absent for a healthy workload and on kubelets that do not report memory PSI, so absence means "unknown", not "healthy".

​

CPU, memory and uptime as the cluster reports them right now. null for a resource with no such reading: anything that is not a Deployment, a Deployment with no running pod, and any resource at all while the cluster is unreachable.

ResourceStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
pending
provisioning
running
stopping
suspended
crash_looping
degraded
failed

ResourceUsageResponse

cpu_millicores
​number · double · required
memory_bytes
​number · double · required
uptime_secs
​integer · uint64 · min: 0 · required

RestrictionResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, available_cents, required_cents
type = object · requires: kind, available_cents, floor_cents
type = object · requires: kind, open_receivable_cents, credit_limit_cents
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
type = object · requires: kind
Properties for Variant 1:
available_cents
​integer · int64 · required
kind
​string · const · required
Const value: insufficient_available
required_cents
​integer · int64 · required

RevealedEnvironmentSecretResponse

One secret's plaintext, returned once.
key
​string · required
value
​string · required

RevealedSetupPasswordResponse

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

RolloutStatusResponse

Where a rollout stands (`replace_idle` and `next_start` leave mixed templates, so the console says how many units still run the old one).
replaced_units
​integer · int32 · required

Running units already on the target template.

stale_units
​integer · int32 · required

Running units still on an older template.

started_at
​string · date-time · required
​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.

target_template
​string · required

The template the units are moving to, an opaque fingerprint.

​RolloutWaitingOnResponse[] · required

The stale units replace_idle waits on, each with why.

RolloutStrategy

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
rolling

RolloutWaitingOnResponse

key
​string · required
why
​string · required

allocated: replaced when it exits, stops or is destroyed. busy: replaced at its next idle.

RotateSetupPasswordResponse

`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.

RoutingRuleResponse

order
​integer · uint · min: 0 · required

1 is evaluated first: the highest priority.

priority
​integer · int64 · required
rule
​string · required

The rule exactly as the ingress evaluates it.

​RuleStatusResponse · required

Whether a rule can serve.

strip_prefix
​boolean · required
​RoutingTargetResponse · required
path
​string | null

The path prefix, absent for the default rule that answers everything else.

shadowed_by_path
​string | null

For a shadowed rule, the path of the rule that shadows it (absent when that is the default).

RoutingSampleResponse

What the sample `path` of the query would do.
path
​string · required
upstream_path
​string · required

The path the target receives, after any strip.

winning_order
​integer · uint · min: 0 · required

The order of the rule that takes it.

RoutingStateResponse

string · enum
Enum values:
pending
active
failed

How the domain's certificate and route are doing.

RoutingTargetResponse

endpoint_id
​string · uuid · required
service_instance_id
​string · uuid · required
service_name
​string · required
​TargetStateResponse · required

What a rule's target is doing.

RoutingTlsResponse

status
​string · enum · required

One certificate covers the whole domain, whatever its paths.

Enum values:
pending
active
failed
​

RoutingUsedByResponse

serves
​string[] · required

The paths it serves; / is the default.

​RoutingTargetResponse · required

RuleStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
serving

RunOutcomeResponse

string · enum
Enum values:
succeeded
failed
interrupted
skipped_overlap
skipped_condition
missed

The six, and only six, terminal answers to "what happened to this run". See this module's header for the seventh, unwritten in-flight state.

RunResponse

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.

RunSnapshotResponse

What actually ran, captured at fire time. Every field is `null` when it was not known at that moment; see [`ServiceRun`]'s own module header for why this exists ("it worked yesterday" is otherwise unanswerable).
command
​string[] · required
image_digest
​string | null
​
schedule_cron_expression
​string | null
schedule_timezone
​string | null
timeout_seconds
​integer | null · int32

RunTriggerResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind
type = object · requires: kind, triggered_by_user_id
Properties for Variant 1:
Fired by a schedule. `schedule_id` is `null` when the schedule that caused this run has since been deleted, not when the run was triggerless -- `service_runs.schedule_id` is `ON DELETE SET NULL` so a deleted schedule's history survives it.
kind
​string · const · required
Const value: schedule
schedule_id
​string | null · uuid

RuntimeConfigResponse

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.

RuntimeOptionDto

One runtime a product's `app_runtime` step offers, mirroring [`RuntimeOption`] by hand.
default_start_command
​string · required
id
​string · required
install_command
​string · required
label
​string · required
label_key
​string · required
start_command_presets
​string[] · required
​RuntimeVariantDto[] · required
description
​string | null
description_key
​string | null
icon
​string | null

RuntimeOverrideResponse

One startup value beside the one the image itself declares.
image_default
​string | null

What the image declares.

null means "the image declares nothing" only when [RuntimeConfigResponse::image_defaults_available] is true. When that flag is false it means the registry could not be asked, which is a different statement and must not be rendered as the first one.

value
​string | null

The customer's value. null means the image's own is used.

RuntimeStatsBatchResponse

Only instances the caller may see and the cluster answered for appear; an instance with no reading is absent, never zero.
​RuntimeStatsEntryResponse[] · required

RuntimeStatsEntryResponse

One instance's live reading, taken from its primary workload.
cpu_millicores
​number · double · required
memory_bytes
​number · double · required
service_instance_id
​string · uuid · required
uptime_secs
​integer · uint64 · min: 0 · required

RuntimeVariantDto

One runtime's image for one compatibility tier, mirroring [`RuntimeVariant`] by hand.
id
​string · required
image
​string · required
label
​string · required
label_key
​string · required
description
​string | null
description_key
​string | null

SalesChannelQuery

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: console
The business console. The default.

ScalingAction

string · enum
Enum values:
scale_up
scale_down
hold
refused

What the evaluator (or a policy change) did about the unit count. Stored as text and constrained in SQL to the same four words.

ScalingDecisionResponse

One entry of the decision log. Consecutive identical decisions are one entry: `at` is the first, `last_at` the latest, `repeat_count` how many.
action
​ScalingAction · enum · required

What the evaluator (or a policy change) did about the unit count. Stored as text and constrained in SQL to the same four words.

Enum values:
scale_up
scale_down
hold
refused
at
​string · date-time · required
from_units
​integer · int32 · required
id
​string · uuid · required
inputs
​required

What the decision was made from (samples and bounds), as the evaluator recorded them. Its shape is not a contract.

last_at
​string · date-time · required
​ScalingReason · required

Why. Stored as text and constrained in SQL to the same words. The last two (policy_change, rollout_paused) are not the evaluator's: the first is a customer's PATCH moving a fixed count, the second is the rolling rollout pausing itself.

repeat_count
​integer · int32 · required
summary
​string · required

One sentence saying what happened and why ("Scaled 2 to 4: CPU at 83% against a 60% target"), composed by the kernel from the row so clients show words they did not assemble.

summary_code
​string · required

Which sentence summary is, <action>.<reason> such as scale_up.cpu_high.

​object · required

The figures in it (from, to, and used/target/min/max when recorded), for a client that words the sentence itself.

to_units
​integer · int32 · required

ScalingMarkerKind

string · enum
Enum values:
scale_up
scale_down
refused
rollout_paused

What a marker on the count chart says happened.

ScalingMode

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
fixed
autoscaled

ScalingPolicyQuoteResponse

`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).

ScalingPolicyResponse

`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.

ScalingReason

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
cpu_high
cpu_low
memory_high
memory_low
schedule
cooldown
stabilising
spend_cap

ScannerSubscriptionOrganizationResponse

id
​string · uuid · required
name
​string · required

ScannerSubscriptionOwnerResponse

display_name
​string · required
id
​string · uuid · required
email
​string | null

The owner's primary email (OAuth first, password credential as fallback), or null when none resolves.

ScannerSubscriptionPeriodResponse

The billing period a subscription is currently in.
ends_at
​string · date-time · required
starts_at
​string · date-time · required

ScannerSubscriptionResponse

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.

ScannerSubscriptionStatusRequest

string · enum
Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed

Request-side twin of [SubscriptionStatusResponse]; /v2 keeps Deserialize and Serialize on distinct enums.

ScheduleActionResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind
type = object · requires: kind, tasks
Properties for Variant 1:
Start the service's job.
kind
​string · const · required
Const value: run_job

ScheduleConditionResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: condition, starts_at, ends_at
type = object · requires: condition, excluded_dates
type = object · requires: condition, requires, within_seconds
type = object · requires: condition, url
type = object · requires: condition, schedule_id, within_seconds
Properties for Variant 1:
Fires only inside a clock-time window, in the schedule's own timezone. `starts_at` equal to `ends_at` is refused by the domain.
condition
​string · const · required
Const value: time_window
ends_at
​string · partial-time · required
starts_at
​string · partial-time · required

ScheduleResponse

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.

ScheduleRule

A bounds override that takes effect when its cron expression fires and holds until the next rule fires.
cron
​string · required

Five-field cron expression, numeric fields only.

max_units
​integer · int32 · required
min_units
​integer · int32 · required
timezone
​string · required

IANA zone name the expression is read in.

ScheduleTaskResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, command
type = object · requires: kind, entrypoint
type = object · requires: kind, action
type = object · requires: kind
type = object · requires: kind, seconds
Properties for Variant 1:
Types `command` into the server's console.
command
​string · required
kind
​string · const · required
Const value: console_command
timeout_seconds
​integer · int32

Seconds the command may take. Defaults to 300; at most 3600.

Default: 300

ScheduledDownsizeResponse

A reduction waiting for the end of its billing period (V-0794).
applies_at
​string · date-time · required

When it takes effect, right before the next renewal is priced.

id
​string · uuid · required
cpu
​number | null · double

The vCPU it lowers the service to, or null if cpu is unchanged.

memory_gb
​number | null · double

The memory in GiB it lowers the service to, or null if memory is unchanged.

SeedSettingDto

The optional `seed` setting.
default
​integer | null · uint32 · min: 0

ServerImportListResponse

`GET .../server-imports`: the most recent imports, newest first.
​ServerImportResponse[] · required

ServerImportResponse

One import, as it stands.
bytes_transferred
​integer · int64 · required
cancel_requested
​boolean · required

The customer asked to stop. The import is still running until it moves to cleaning_up, then cancelled.

created_at
​string · date-time · required
delete_extraneous
​boolean · required
files_transferred
​integer · int64 · required
host
​string · required
id
​string · uuid · required
remote_path
​string · required
​ServerImportStatusResponse · required

Where an import is.

target_path
​string · required

The directory in the service the files are copied into.

updated_at
​string · date-time · required
username
​string · required
bytes_total
​integer | null · int64
completed_at
​string | null · date-time
current_file
​string | null

The file being copied now, when the transfer has reported one.

error
​string | null

Why the import failed, in words a customer can act on.

files_total
​integer | null · int64
port
​integer | null · uint16 · min: 0 · max: 65535
progress_percent
​number | null · double

0 to 100, by bytes when the total is known, otherwise by files.

ServerImportStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
succeeded
failed
cancelled

ServerPasswordsParamsDto

[`SetupStepId::ServerPasswords`](kernel_core::domain::setup_steps::SetupStepId::ServerPasswords)'s declared params, mirroring [`ServerPasswordsParams`] by hand on this module's own doc comment's discipline. Published so a product's declared password ids/envs are visible on the wire with a real schema, the same reason [`AppRuntimeParamsDto`] exists.
​PasswordSpecDto[] · required

ServerPasswordsRequestDto

[`kernel_core::domain::setup_steps::SetupStepId::ServerPasswords`]'s request shape: one entry per password the caller wants to set, keyed by [`kernel_core::domain::setup_steps::PasswordSpec::id`]. A caller may submit a subset (an update touching only the passwords it wants to change), the same partial-update discipline `SetupSecretsService::set_passwords` documents.
​object · required

ServerPasswordsResponseDto

[`SetupStepAnswersResponseDto::ServerPasswords`]'s payload: which declared passwords are set, NEVER a value or a ciphertext. Reaching a value needs its own audited request -- `POST .../setup/steps/server_passwords/{id}/reveal`.
​PasswordStatusDto[] · required

ServiceInstanceActivityEntry

One activity entry, as a customer sees it.
action
​string · required
actor_is_staff
​boolean · required

True when a Zaroz staff account did this on the customer's service. The staff member's identity (principal_id, principal_name, token and sponsor) is withheld, so the customer sees only that it was staff.

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

User, Application, or System.

api_token_id
​string | null · uuid
api_token_name
​string | null
principal_id
​string | null
principal_name
​string | null
project_id
​string | null · uuid
service_instance_id
​string | null · uuid
sponsor_application_id
​string | null · uuid
sponsor_application_name
​string | null

ServiceInstanceAvailabilityResponse

string · enum
Enum values:
available
cluster_unavailable

Whether this row's platform can currently be reached to answer a live status question -- see MyServiceInstanceSummaryResponse::availability's own doc comment for exactly what this does and does not mean.

ServiceInstanceFeatureResponse

One feature, by id. Ids only, deliberately: a feature carries no labels or prose (`product_features.rs`), so a client keys its own locale strings and its own panel off this value. Kernel carries it and never branches on it.
id
​string · required

ServiceInstanceFeaturesResponse

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.
​ServiceInstanceFeatureResponse[] · required

ServiceInstanceLifecycleActionResponse

string · enum
Enum values:
start
stop
restart

Mirrors ServiceInstanceLifecycleAction 1:1, kept as its own /v2 response type for the reason ResourceStatusResponse is: a rename on the domain enum must not silently change a wire value.

ServiceInstanceLifecycleResponse

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

ServiceInstanceOperationResponse

`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.

ServiceInstanceResponse

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.

ServiceInstanceSetupResponse

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

ServiceInstanceSetupStepOptionsResponse

`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).

ServiceInstanceSetupStepResponse

One declared step's params and, if answered, its answers -- one row of `GET .../setup/steps`.
​SetupStepParamsDto · required

One declared step's params, typed and tagged by step -- ZC-1379 slice S3 replaces GET .../setup/steps' previous raw [serde_json::Value] with this, mirroring [StepParams] by hand so dashboard's generated types carry a real shape for every step's params (docs/superpowers/specs/2026-09-25-setup-steps-shared-steps-plan.md §2.4) instead of an opaque blob. Tagged exactly like [StepAnswers] itself: {"step": "app_runtime", "params": {...}}, or {"step": "none"} for a step that reads none (every param-less step collapses to the same None shape here, the same way [StepParams::None] does on the domain side -- which step this is at all is already the parent [ServiceInstanceSetupStepResponse::step]'s job, not this type's).

​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.

​

null when this step has not been answered yet.

ServiceInstanceSetupStepsResponse

`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.

ServiceInstanceStatusEventResponse

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.

ServicePrincipalResponse

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.

ServiceResourceRefResponse

The minimal view of one resource a service's holder needs to ADDRESS it (console, files, logs and metrics routes are keyed by resource id): id, kind, role, status and whether it is a hidden helper. Nothing about the owning subscription, its billing, placement or config, so it is safe to hand a per-service share holder (decision-0333).
hidden
​boolean · required

A hidden helper (sidecar) that a customer-facing list normally omits.

id
​string · uuid · required
kind
​ResourceKindResponse · enum · required

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
role
​string · required

The resource's declared role ("server", "sidecar", "sftp", ...).

​ResourceStatusResponse · required

ServiceResourceRefWithPrimaryResponse

A [`ServiceResourceRefResponse`] plus whether it is the product's declared primary.
hidden
​boolean · required

A hidden helper (sidecar) that a customer-facing list normally omits.

id
​string · uuid · required
is_declared_primary
​boolean · required
kind
​ResourceKindResponse · enum · required

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
role
​string · required

The resource's declared role ("server", "sidecar", "sftp", ...).

​ResourceStatusResponse · required

ServiceRunStateResponse

string · enum
Enum values:
not_provisioned
provisioning
running
stopping
suspended
crash_looping
degraded
failed

Mirrors ServiceRunState 1:1, kept as its own /v2 response type so the wire format cannot drift silently if a variant is renamed on the application enum -- the convention contracts::resources's own ResourceStatusResponse already follows for ProvisionStatus.

ServiceSharePermission

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: console
The live windows: log stream, command console, terminal, SSH, framebuffer.

ServiceShareResponse

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.

ServiceShareStatusResponse

string · enum
Enum values:
pending
accepted

Where a share stands: pending until its grantee opens it in "Shared with me", then accepted.

SessionResponse

A freshly opened session. `token` is the bare access token: the caller adds the `Bearer ` scheme itself when sending it back as `Authorization`. (The V1 issuer used to return `"Bearer {token}"` here; that defect is fixed and V2 must not reproduce it.)
refresh_token
​string · required
session_id
​string · uuid · required
token
​string · required
​AuthUserResponse · required

Minimal caller identity carried on a session payload.

Deliberately not the full /console/me shape (kernel_http_v2_identity::contracts::me::MeResponse): /v2 feature crates do not depend on one another (see crates/http/CLAUDE.md), so this is a small, independent projection sized for what a session response needs, not a shared type.

SetAvatarSourceRequest

Body of `PUT /me/avatar/source`.
provider_id
​string · required

The id of an OAuth provider the caller has already linked an account with. Must have a stored avatar (the provider returned one at some login).

SetBillingCountryRequest

Body of `PUT /organizations/{id}/billing-country` (V-0864).
country_code
​string · required

Two-letter country code, e.g. ES. Case-insensitive.

SetDomainPathsRequest

`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

SetEnvironmentVariableRequest

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

SetInitialPasswordRequest

Sets a first password for a caller who has only ever signed in through a provider. The address comes from that provider, never from the request.
new_password
​string · required

SetRegistrySourceRequest

An image in a 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

SetSourceRequest

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

SetSubscriptionCancellationRequest

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

SetSubscriptionRenewalRequest

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.

SettingOptionDto

Mirrors [`SettingOption`] by hand.
label
​string · required
value
​string · required

SetupOptionDto

One item a customer may pick for a setup-step field's value (ZC-1292 slice 1d). Mirrors `kernel_core::domain::setup_steps::SetupOption` by hand rather than deriving `JsonSchema` on the domain type directly -- the same discipline this module's own doc comment states for every other DTO here.
label
​string · required
value
​string · required
detail
​string | null

SetupStepAnswersDto

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

SetupStepAnswersResponseDto

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

SetupStepId

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
minecraft_software
minecraft_gameplay
java_runtime

SetupStepParamsDto

oneOf
Exactly one variant must match.

Decision Table

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

SftpConnectionResponse

Connection details for a service instance's SFTP account. Never a password -- see this module's header.
enabled
​boolean · required

Whether the account is currently reachable.

username
​string · required

The single account name every SFTP deployment is provisioned with.

host
​string | null

null when the account's public port has not resolved yet (a placement fault, or the external allocation is still landing) -- the read itself still succeeds, the same rendering CustomDomainsResponse.platformHost uses for the same situation.

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

SftpCredentialResponse

[`SftpConnectionResponse`] plus the password, returned only in the response to `POST .../sftp/rotate`, and only to the caller who just rotated it.
enabled
​boolean · required
password
​string · required

The new password. Shown here once; it is never returned by a subsequent read.

username
​string · required
host
​string | null
port
​integer | null · uint16 · min: 0 · max: 65535

SharedDatabaseColumnView

column_type
​string · required
data_type
​string · required
extra
​string · required
name
​string · required
nullable
​boolean · required
ordinal_position
​integer · uint32 · min: 0 · required
primary_key
​boolean · required
default_value
​string | null

SharedDatabaseEnginesResponse

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

SharedDatabaseEntitlementResponse

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.

SharedDatabaseRegionAvailabilityView

country_code
​string · required
engine
​string · required
region_id
​integer · int32 · required
region_name
​string · required
region_slug
​string · required

SharedDatabaseRegionsResponse

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

SharedDatabaseResponse

​SharedDatabaseView · required

SharedDatabaseRowsView

columns
​string[] · required
has_more
​boolean · required
limit
​integer · uint16 · min: 0 · max: 65535 · required
pagination_mode
​string · required
​object[] · required
table
​string · required
cursor_column
​string | null
next_cursor
​string | null

SharedDatabaseSqlResultView

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: kind, columns, rows +1 more
type = object · requires: kind, affected_rows
Properties for Variant 1:
columns
​string[] · required
kind
​string · const · required
Const value: rows
​object[] · required
truncated
​boolean · required

SharedDatabaseTableView

​SharedDatabaseColumnView[] · required
name
​string · required
primary_key
​string[] · required
table_type
​string · required
data_length
​integer | null · uint64 · min: 0
engine
​string | null
index_length
​integer | null · uint64 · min: 0
keyset_cursor_column
​string | null
row_estimate
​integer | null · uint64 · min: 0

SharedDatabaseTransferOperationResponse

`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

SharedDatabaseTransferStartedResponse

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

SharedDatabaseView

created_at
​string · date-time · required
database
​string · required
display_name
​string · required
engine
​string · required
external_host
​string · required
external_port
​integer · uint16 · min: 0 · max: 65535 · required
id
​string · uuid · required
included
​boolean · required
internal_connection_available
​boolean · required
internal_host
​string · required
internal_port
​integer · uint16 · min: 0 · max: 65535 · required
password
​string · required
updated_at
​string · date-time · required
username
​string · required
over_quota_since
​string | null · date-time
quota_bytes
​integer | null · uint64 · min: 0
region_country_code
​string | null
region_id
​integer | null · int32
region_name
​string | null

SharedDatabaseWriteResultView

affected_rows
​integer · uint64 · min: 0 · required

SharedDatabasesResponse

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

SharedServiceInstanceResponse

One service shared with the caller: what the gaming app needs to draw and open it, and nothing about the owner beyond a display name. Every other route the caller may use on it is `/api/v2/service-instances/{service_instance_id}/...`, within `permissions`.
name
​string · required
owner_name
​string · required

The owning organization's display name.

​ServiceSharePermission[] · required
​SharedServiceProductResponse · required

The product a shared service runs, by name.

​ServiceResourceRefResponse[] · required

The service's live resources (id, kind, role, status), which the console, files, logs and metrics routes are addressed by. Empty when none exist yet. For which one is the declared primary, call GET /service-instances/{id}/resources.

service_instance_id
​string · uuid · required
share_id
​string · uuid · required
shared_at
​string · date-time · required
slug
​string · required
status
​string · enum · required

The service's commercial status (active, suspended, ...): a suspended service is listed, and its capability routes answer 404 until the owner settles it.

Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed

SharedServiceProductResponse

The product a shared service runs, by name.
id
​string · uuid · required
name
​string | null

null when the product could not be named.

Source

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: host, repository, tag +1 more
Properties for Variant 1:
An image in an OCI registry, and the username that pulls it. **There is no password field, and that is the security property.** See this module's header.
kind
​string · const · required
Const value: registry
host
​string · required

ghcr.io, docker.io, registry.example.com:5000. Always present: a reference with no host is normalised to [DOCKER_HUB_HOST] on the way in, so a reader never has to know the defaulting rule.

repository
​string · required

acme/worker, library/nginx. Never carries the host.

tag
​string · required

The mutable name the customer chose.

credential_username
​string | null

The username half of the stored pull credential. None is an anonymous pull.

digest
​string | null

The immutable name of what was actually pulled, when the stored reference pins one.

Always None for anything this platform wrote, because nothing resolves a tag to a digest for a third-party registry: the only manifest client in the codebase authenticates with HTTP basic, and ghcr.io, Docker Hub and Quay all require the Bearer challenge exchange. Some only for a reference a customer pinned themselves.

SourceArmId

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: registry
An image pulled from an OCI registry, anonymously or with credentials.

SourceResponse

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.

SpendUsageResponse

The organization's metered spend this month against its organization-wide spend cap.
current_cents
​integer · int64 · required

What every service of the organization has spent on metered usage this month, in cents.

​required

ok, near (80 percent of the cap or more) or at_limit (growth is refused).

cap_cents
​integer | null · int64

The cap staff set, in cents; absent when there is none and growth is not refused for spend.

SshConnectionDetailsResponse

`GET /api/v2/service-instances/{id}/resources/{resource_id}/ssh`.
authorized_key_fingerprints
​string[] · required

SHA-256 fingerprints of the public keys the machine was built trusting.

browser_ssh_status
​string · required

ready, enrolling_host_key or unavailable.

command
​string · required

A ready-to-paste ssh user@host, or empty while there is no host.

host
​string · required

The machine's own public address, or empty while it has none.

host_key_fingerprints
​string[] · required

SHA-256 fingerprints of the host keys the machine enrolled on first boot: what a client should see on its first connection.

operating_system
​string · required

Always linux: the capability is only derived for a Linux guest.

password_auth_enabled
​boolean · required
port
​integer · uint16 · min: 0 · max: 65535 · required
public_route_status
​string · required

ready once the public address routes to the machine, else pending.

username
​string · required

The guest account the machine was built with.

SshSessionTicketResponse

The one-time URL a browser terminal connects to.
expires_in
​integer · uint64 · min: 0 · required
session_id
​string · uuid · required
url
​string · required

wss://<edge>/ssh?ticket=.... Redeemable once, within expires_in seconds, and only from the dashboard origin it was issued for.

StandingResponse

The full detail view of one organization's standing (`Q121`: the owner, and staff). No `organization_id` field -- every caller of this type already knows which organization it is about from the route it called.
available_cents
​integer · int64 · required
balance_cents
​integer · int64 · required
credit_limit_cents
​integer · int64 · required
currency
​string · required
floor_cents
​integer · int64 · required
open_receivable_cents
​integer · int64 · required
required_cents
​integer · int64 · required
reserved_cents
​integer · int64 · required
restricted
​boolean · required
​

StartServerImportRequest

`POST .../server-imports` body.
host
​string · required

The remote server's host name or public IP address. Private, loopback and link-local addresses are refused.

password
​string · required

The remote account's password. Stored encrypted for the length of the transfer and never returned.

username
​string · required
delete_extraneous
​boolean

Also delete files in the service's data directory that the remote server does not have (a sync rather than a copy). Defaults to false.

Default: false
port
​integer | null · uint16 · min: 0 · max: 65535

The remote SFTP port. Defaults to 22.

Default: null
remote_path
​string

The directory on the remote server to copy from. Defaults to the account's root.

Default:

StartUnitRequest

`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

StartupFailureResponse

reason
​string · required

The kubelet's waiting reason, e.g. CreateContainerConfigError.

message
​string | null

StaticSiteDto

[`SetupStepId::StaticSite`](kernel_core::domain::setup_steps::SetupStepId::StaticSite)'s answer (V-0887), shared between request and response. A blank index document means `index.html`; a blank error document means nginx's plain 404.
error_document
​string
Default:
index_document
​string
Default:
spa_fallback
​boolean
Default: false

SteamBranchDto

[`SetupStepId::SteamBranch`](kernel_core::domain::setup_steps::SetupStepId::SteamBranch)'s answer (V-0703), shared between request and response. Blank means the product's default branch.
branch
​string
Default:

SteamBranchParamsDto

[`SetupStepId::SteamBranch`](kernel_core::domain::setup_steps::SetupStepId::SteamBranch)'s declared params, mirroring [`SteamBranchParams`] by hand.
custom
​boolean · required
default
​string · required
​SettingOptionDto[] · required

SteamWorkshopDto

[`SetupStepId::SteamWorkshop`](kernel_core::domain::setup_steps::SetupStepId::SteamWorkshop)'s answers (V-0703), shared between request and response.
collection_id
​string | null
Default: null
download_collection_id
​string | null
Default: null

SteamWorkshopParamsDto

[`SetupStepId::SteamWorkshop`](kernel_core::domain::setup_steps::SetupStepId::SteamWorkshop)'s declared params, mirroring [`SteamWorkshopParams`] by hand.
collection
​boolean · required
download_collection
​boolean · required

StorefrontCategory

string · enum
Enum values:
storage
compute
apps
databases
managed-apps
managed-platforms
registry

Which storefront shelf a product appears on, authored rather than derived.

Every variant is a console route directory. The seven wire spellings are exactly the directory names under console/app/[org]/[project]/(document)/, because console links a product as /{org}/{project}/{category}/{slug}:

VariantWire spellingConsole route directory
Storagestorageconsole/app/[org]/[project]/(document)/storage
Computecomputeconsole/app/[org]/[project]/(document)/compute
Appsappsconsole/app/[org]/[project]/(document)/apps
Databasesdatabasesconsole/app/[org]/[project]/(document)/databases
ManagedAppsmanaged-appsconsole/app/[org]/[project]/(document)/managed-apps
ManagedPlatformsmanaged-platformsconsole/app/[org]/[project]/(document)/managed-platforms
Registryregistryconsole/app/[org]/[project]/(document)/registry

A category with no matching directory navigates nowhere: the link renders, the click 404s. Adding a variant here therefore requires adding the matching console route directory in the same change, and removing a directory requires removing the variant. That is the whole anti-drift mechanism: this enum reaches the OpenAPI document as a real string enum, admin renders its authoring dropdown from the generated client, and console checks its own route directories against the same generated list.

This is not [CatalogCategory], and does not replace it. [product_category] derives a category from a product's stored family and slug, and its fallthrough is blunt: everything that is not a fleet, a VPS, a container kind or a flat-fee license kind becomes [CatalogCategory::GameServer], so postgres, object_storage and static_website all land on one shelf. A v2 product is authored data (Product + DisplayProfile + ProductDefinition), so its shelf is authored too, and lives on the display profile alongside the name and icon it is already presented with. Migrating the derived callers is a later slice; both answers coexist for now.

SubmitTicketReviewRequest

Body for `POST /api/v2/support/tickets/{id}/review`. `deny_unknown_fields` (`ZC-0752`'s convention): a caller that misspells `response_quality` or `resolution_speed` is told, rather than having the field silently dropped and the request still succeed with a rating nobody chose. `response_quality` and `resolution_speed` are validated against `1..=5` by `SupportTicketReview::create` (`crates/domain/model/src/domain/support/ review.rs`), not here: this struct only shapes the wire, the domain type owns the invariant.
problem_solved
​boolean · required
resolution_speed
​integer · uint8 · min: 0 · max: 255 · required
response_quality
​integer · uint8 · min: 0 · max: 255 · required
comment
​string | null
Default: null

SubscriptionCancellationResponse

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.

SubscriptionHardwareProfileResponse

The hardware profile a subscription's workload runs on (`Ryzen 8700GE`), by name.
id
​integer · int32 · required
name
​string · required

SubscriptionRegionResponse

Deliberately its own small type here rather than a reuse of `kernel_http_v2_catalog::contracts::regions::RegionResponse`: feature crates do not depend on one another (`crates/http/CLAUDE.md`), and a subscriptions listing needs only enough to name a region and compare it against another one, not the storefront's full catalog shape (country, coordinates, availability verdicts).
country_code
​string · required

ISO 3166-1 alpha-2 code of the region's country (ES, FI), for the flag.

id
​integer · int32 · required
name
​string · required

SubscriptionRenewalResponse

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.

SubscriptionResponse

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).

SubscriptionStatusResponse

string · enum
Enum values:
pending
active
suspended
pending_termination
terminated
cancelled
failed

SudoDisableRequest

Removes one of the caller's own enrolled elevation methods (ZC-0716, `/v1` parity). Requires the caller's session to already be elevated -- see `controllers::sudo::disable_handler`.
method
​ElevationMethod · enum · required

Which admin-elevation module a credential belongs to, or a verification attempt is checked against. AdminElevationModule gives each method (PIN, WebAuthn, TOTP) a home behind this same enum without touching every caller that only cares about routing by name. Serializable so callers of /auth/sudo/* can pick a method by name instead of the HTTP layer assuming PIN.

Enum values:
pin
webauthn
totp

SudoElevateRequest

Re-elevates a session whose PIN is already enrolled (ZC-0412). Field-identical to [`SudoSetupRequest`] and deliberately a separate type rather than a reuse of it: the two operations mean different things to a generated client (one enrolls a new secret, the other proves a stored one), and sharing the schema would make a later divergence -- a `method` selector once `/v2` supports more than PIN, a recovery code on enrolment -- a breaking rename of the other operation's request body.
pin
​string · required

The six ASCII digits the caller enrolled. A wrong PIN and an account with no PIN enrolled are answered identically; see controllers::sudo::elevate_error_response.

SudoElevateResponse

A freshly elevated session, minted on a successful PIN verification. **`token` is a newly signed access token, not a mutation of the one the caller sent.** It carries a fresh one-hour `expires_at` and an `elevated_until` eight hours out (`ElevationMethod::Pin`'s `elevation_window`). The caller must replace its stored access token with this one; nothing else in the session changes, and in particular the refresh token is **not** rotated. **The token the caller presented stays valid until its own `expires_at`.** There is no denylist, so this operation cannot revoke it. That is not a downgrade risk on its own -- the old token is the *un*-elevated one, so a caller that keeps using it simply keeps failing the elevation gate -- but a client that stores both will look intermittently un-elevated. Keep one. One consequence worth stating because it has already bitten this codebase: elevation lives in the JWT claim, so `POST /auth/refresh` carries it forward only when the caller also sends the elevated access token as an `Authorization` header (`utils::elevation::elevated_until_for_refresh`, which tolerates an already-expired one). A refresh that omits it returns a plain token and silently drops the elevation minted here. `token` is bare, for the same reason [`SudoSetupResponse::token`] is.
elevated_until
​string · date-time · required
token
​string · required

SudoExitResponse

Reissues a plain, non-elevated token so the caller can drop sudo early (ZC-0716, `/v1` parity). See `AdminElevationService::exit`'s doc comment on why the previously elevated token is not itself revoked.
token
​string · required

SudoSetupRequest

Enrolls the caller in PIN-based sudo elevation. `/v1`'s equivalent (`crates/http/identity/src/controllers/auth/sudo.rs`) takes `{method, secret}` so one route can also serve WebAuthn and TOTP enrollment. This contract stays PIN-only and keeps its own shape rather than growing a `method` selector: WebAuthn and TOTP now have their own dedicated `/v2` enroll routes (ZC-0716, see `controllers::sudo`'s doc comment), each with a request type shaped for that method's actual enrollment payload (a ceremony response, a confirmation code) rather than an untyped `secret: String` both would have to share here.
pin
​string · required

Six ASCII digits. Format is validated by AdminElevationService.

SudoSetupResponse

A freshly elevated session, minted immediately on successful enrollment. `token` is bare: the caller adds the `Bearer ` scheme itself, matching `contracts::session::SessionResponse::token` (see its doc comment on the `/v1` issuer defect that used to prepend the scheme; that is fixed at the source now and V2 does not need to work around it).
elevated_until
​string · date-time · required
token
​string · required

SudoStatusResponse

Whether the caller's own session is elevated, and what it could elevate with (ZC-0412). This route existing at all is the fix for a real defect: `admin` read the missing route's 404 as "not elevated", so its gate rendered over sessions that had been elevated the whole time.
available_methods
​ElevationMethod[] · required

The elevation methods this surface can actually verify.

Was PIN-only before ZC-0716: /v2 ported no challenge-issuing route, so a client offered webauthn or totp here would have had no way to produce a secret for either. Now equal to AdminElevationService::available_methods() (every module registered in the deployment), because ZC-0716 added the missing enroll/verify challenge routes for both. See controllers::sudo::V2_ELEVATION_METHODS.

Enum values:
pin
webauthn
totp
enrolled_methods
​ElevationMethod[] · required

Every method the caller has actually enrolled, unfiltered, which can include a method available_methods does not list.

That asymmetry is intentional rather than an oversight: a user who enrolled TOTP while /v1 existed still has that credential, and it is what makes POST /auth/sudo/setup demand an already-elevated session before adding a second method (AdminElevationService::enroll). A client that saw only the filtered list would have no way to explain that refusal.

Enum values:
pin
webauthn
totp
is_elevated
​boolean · required

Whether the bearer token on THIS request is elevated right now. Equal to elevated_until.is_some(), and published as its own field so a client never has to compare a timestamp against its own clock to answer the only question it usually asks. elevated_until is already filtered against server time by utils::elevation::elevated_until, so an expired window reads as false with a null deadline rather than as a past date.

elevated_until
​string | null · date-time

When the current elevation lapses, or null when the caller is not elevated.

SupportAccessRequestResponse

One access request as the CUSTOMER sees it. Names no agent, for the reason this module's doc comment gives. It names the service instance instead, which is the thing being consented to, and the chat message the prompt was posted as, so a client can anchor the approve/deny buttons to the right bubble in the thread rather than to a separate inbox the customer would have to go looking for.
chat_message_id
​string · uuid · required

The message in the ticket thread that carries this prompt.

created_at
​string · date-time · required
id
​string · uuid · required
max_duration_hours
​integer · int64 · required

The longest an approval can last, from the domain's MAX_GRANT_DURATION. Served on every request, pending ones included, because that is when the customer needs it: it is the "for how long" half of what they are being asked to agree to. Closing the ticket ends the grant sooner.

service_instance_id
​string · uuid · required

The one service this request would reach. A grant is scoped to a service instance and to nothing else -- not the subscription, not the organization, not the customer's other services.

state
​SupportAccessRequestStateResponse · enum · required

Where one request sits in its lifecycle.

Five states and not four: Denied, Revoked and Expired are three different stories about why access is not live, and collapsing them would make the audit trail the customer reads disagree with the one the platform keeps. Mirrors the domain enum's own snake_case wire form (SupportAccessRequestState), which is persisted, so the two cannot drift.

Enum values:
pending
approved
denied
revoked
expired
ticket_id
​string · uuid · required
expires_at
​string | null · date-time

When an approved grant stops working. Null until approval, and null forever on a request that was denied.

responded_at
​string | null · date-time

When the request was approved, denied or revoked. Null while pending.

SupportAccessRequestStateResponse

string · enum
Enum values:
pending
approved
denied
revoked
expired

Where one request sits in its lifecycle.

Five states and not four: Denied, Revoked and Expired are three different stories about why access is not live, and collapsing them would make the audit trail the customer reads disagree with the one the platform keeps. Mirrors the domain enum's own snake_case wire form (SupportAccessRequestState), which is persisted, so the two cannot drift.

SupportTicketReviewResponse

The customer's own CSAT review of a closed ticket, echoed back once submitted. `submitted_by_id` is deliberately omitted: it is always the caller here by construction (the service refuses anyone else -- see `submit_review_docs`), so echoing it back would only repeat what the caller already knows about themselves.
problem_solved
​boolean · required
resolution_speed
​integer · uint8 · min: 0 · max: 255 · required
response_quality
​integer · uint8 · min: 0 · max: 255 · required
submitted_at
​string · date-time · required
ticket_id
​string · uuid · required
comment
​string | null

TargetStateResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: ready
Open and live.

TaxBehaviorResponse

string · enum
Enum values:
exclusive
inclusive

TextSettingDto

Mirrors [`TextSetting`] by hand, with the effective `max_length`.
default
​string · required
max_length
​integer · uint16 · min: 0 · max: 65535 · required

TicketDetailResponse

​TicketMessageResponse[] · required
​TicketResponse · required

One ticket visible to the caller, in the detail view (paired with its messages in TicketDetailResponse) -- any ticket belonging to any organization the caller may see (design doc §44.2), not only ones the caller personally opened. Same no-staff-identity rule as TicketSummaryResponse.

TicketEventResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: type, ticketId, messageId +1 more
type = object · requires: type, ticketId, messageId +1 more
type = object · requires: type, ticketId, messageId +1 more
type = object · requires: type, ticketId, isStaff
type = object · requires: type, ticketId, isStaff +1 more
type = object · requires: type, ticketId, claimed
type = object · requires: type, ticketId, isStaff
type = object · requires: type, ticketId
Properties for Variant 1:
A new message landed in the thread. Carries ids only. The body is deliberately absent: a client re-reads the ticket, which is the one path where staff authorship is rendered correctly, rather than patching a bubble in from a payload that would have to repeat that logic.
type
​string · const · required
Const value: message_sent
isStaff
​boolean · required
messageId
​string · uuid · required
ticketId
​string · uuid · required
authorId
​string | null · uuid

TicketMessageAttachmentRequest

One already-uploaded file being attached to a message. The fields are echoed back from `POST /api/v2/support/tickets/{id}/attachments`, which is where the bytes actually go: uploading and sending are two steps so that a composer can show upload progress and let the sender remove a file before the message exists. Nothing here is trusted. `AddSupportMessageService` re-reads the asset row named by `asset_id` and refuses the message unless it is a `SupportAttachment` owned by this ticket's owner, stored under this ticket's own prefix, and not already attached to another message -- so a forged id names nothing, and says nothing about whether it named something.
asset_id
​string · uuid · required
mime_type
​string · required
original_filename
​string · required
size_bytes
​integer · int64 · required

TicketMessageAttachmentResponse

asset_id
​string · uuid · required
mime_type
​string · required
original_filename
​string · required
size_bytes
​integer · int64 · required

TicketMessageEditResponse

One superseded version of a message, newest last.
edited_at
​string · date-time · required
id
​string · uuid · required
previous_body
​string · required

TicketMessageKindResponse

string · enum
Enum values:
text
access_request

TicketMessageResponse

One message in `TicketDetailResponse::messages`. Soft-deleted messages never appear here at all -- this endpoint reads `SupportMessageRepository::find_by_ticket_id_visible`, which already excludes them, rather than `/v1`'s `find_by_ticket_id` (every message, with the body blanked for a deleted one at render time). A `/v2` read surface has no reason to reconstruct a "[deleted]" placeholder in the thread; see this crate's report. A staff author's identity is never exposed: `author_is_staff` is `true` and `author_name` reads `"Support Team"`. A non-staff message's `author_name` is that message's own author's display name, resolved per message by the caller of `from_message` -- not necessarily the caller of the request. A ticket's whole thread is now visible to every member of its organization (design doc §44.2), so a non-staff message may have been authored by the ticket's owner, another organization member, or the current caller; there is no single name that is correct for all of them.
​TicketMessageAttachmentResponse[] · required
author_is_caller
​boolean · required

Whether the caller wrote this message.

ZC-1110. A ticket thread is visible to the whole organization (design doc 44.2), so author_name alone cannot answer this: two colleagues can share a display name, and offering edit or retract on the wrong bubble is worse than offering neither.

Stated as a boolean rather than by publishing the author's id on purpose. The id would answer this question and one nobody asked -- which member of staff wrote a staff reply -- and this module deliberately shows staff only as "Support Team". A boolean cannot leak that however it is later refactored; an Option<Uuid> could, by being populated in one branch somebody forgot to guard. It is always false for a staff message.

author_is_staff
​boolean · required
author_name
​string · required
body
​string · required
can_edit
​boolean · required

Whether the caller may correct this message right now.

The server's own rule, not a hint to re-derive. EditSupportMessage refuses a non-author with Forbidden and refuses ANY edit on a closed ticket with TicketClosed, so this is both conditions already resolved. A client computing author_is_caller && !closed for itself would be deciding an entitlement, which is the boundary every frontend in this program is told not to cross, and it would silently drift the day kernel adds a third condition.

can_retract
​boolean · required

Whether the caller may retract this message right now.

Separate from can_edit because the rules genuinely differ: retraction stays available on a CLOSED ticket, where editing does not. Somebody who posted something they regret can always take it back; rewriting history after the conversation ended is what the closed-ticket rule exists to stop. Collapsing these into one flag would quietly pick one of those two behaviours for both.

created_at
​string · date-time · required
id
​string · uuid · required
kind
​TicketMessageKindResponse · enum · required
Enum values:
text
access_request
author_avatar_asset_id
​string | null · uuid

The customer author's avatar (V-0880), as an asset id the client resolves through the asset route. None for every staff message (staff are shown only as "Support Team") and for a customer with no avatar; the client falls back to a monogram.

edited_at
​string | null · date-time

TicketResponse

One ticket visible to the caller, in the detail view (paired with its messages in `TicketDetailResponse`) -- any ticket belonging to any organization the caller may see (design doc §44.2), not only ones the caller personally opened. Same no-staff-identity rule as `TicketSummaryResponse`.
claimed
​boolean · required
created_at
​string · date-time · required
id
​string · uuid · required
reviewed
​boolean · required

Whether the ticket's owner has already submitted a CSAT review for this ticket, regardless of who is viewing it. Orthogonal to whether staff has answered -- see TicketSummaryResponse::reviewed.

status
​TicketStatusResponse · enum · required
Enum values:
open
closed
subject
​string · required
closed_at
​string | null · date-time
owner_last_read_at
​string | null · date-time
staff_last_read_at
​string | null · date-time

TicketStatusResponse

string · enum
Enum values:
open
closed

TicketSummaryResponse

One ticket visible to the caller, in the list view -- any ticket belonging to any organization the caller may see (design doc §44.2), not only ones the caller personally opened. Never carries a staff identity (name or id) -- claimed staff are represented only as `claimed: bool`, matching `/v1`'s privacy rule for a ticket's own owner (`kernel_http_support::controllers::support::tickets::get_ticket_handler`'s `owner_view` masking) applied consistently everywhere on this customer-only surface, not only in the detail view.
claimed
​boolean · required
created_at
​string · date-time · required
id
​string · uuid · required
organization_id
​string · uuid · required

The organization this ticket belongs to.

Carried even when the caller filtered to a single organization, for the same reason InvoiceSummaryResponse carries one: a row that cannot say who it belongs to forces the client to infer it from the request it happened to send, and an unfiltered listing then has no way to group or label rows at all.

reviewed
​boolean · required

Whether the ticket's owner has already submitted a CSAT review for this ticket (support_ticket_reviews.submitted_by_id). A customer-filed survey tied to the ticket's owner regardless of who is viewing it, entirely independent of last_message_from_staff.

status
​TicketStatusResponse · enum · required
Enum values:
open
closed
subject
​string · required
unread_count
​integer · int64 · required

Messages unread since the ticket's own owner last read it (support_tickets.owner_last_read_at), not since the caller personally last read it. For the ticket's own owner this is exactly "tickets you have unread". For another organization member viewing a colleague's ticket, there is no per-member read cursor to compute one from, so this instead reports staff replies the ticket's owner has not yet seen -- still a meaningful freshness signal for the shared workspace, but not a claim about what this viewer has read.

closed_at
​string | null · date-time
last_message_at
​string | null · date-time
last_message_from_staff
​boolean | null

Whether the most recently posted message came from staff. Independent of reviewed below: a customer may submit a CSAT review at any time regardless of whether staff has replied, and a staff reply does not imply a review exists. 'Answered' and 'reviewed' are orthogonal, not sequential -- see this crate's report.

last_message_preview
​string | null
owner_last_read_at
​string | null · date-time

TitleRuleDto

Mirrors [`TitleRule`] by hand.
max_length
​integer · uint16 · min: 0 · max: 65535 · required

ToggleSettingDto

One toggle and its default.
default
​boolean · required

TopUpProviderRequest

string · enum
Enum values:
paypal
nowpayments

A provider a top-up can be started with. Mollie is deliberately absent: a Mollie top-up has no checkout and no settlement path in the kernel.

TopUpStatusResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
type = string
Properties for Variant 1:
Const value: creating
Being set up with the provider. Transient.

TotpEnrollConfirmRequest

Confirms a pending TOTP enrollment with a code from the authenticator app (ZC-0716). A separate, dedicated route (`POST /auth/sudo/totp/enroll`) rather than a `method` branch on [`SudoSetupRequest`], for the same contract-typing reason that request's own doc comment gives.
code
​string · required

The current 6-digit code from the authenticator app. Verified against the secret POST /auth/sudo/totp/enroll-options generated for this caller, which must still be pending (ENROLL_TTL_SECONDS, 5 minutes) -- see TotpElevationModule::read_pending_secret.

TotpEnrollOptionsResponse

A freshly generated TOTP shared secret, ready to show as a QR code or a manual-entry string (ZC-0716). Round-trips `AdminElevationModule::begin_enroll`'s JSON via `serde_json::from_value` into a named shape instead of forwarding the `serde_json::Value` opaquely the way the WebAuthn challenge responses do below: unlike a WebAuthn ceremony object (an external library's wire format, `/v1` deliberately left untyped, see `controllers::sudo`'s `webauthn_register_options_docs`), this JSON is produced entirely by `TotpElevationModule::begin_enroll` in this codebase, so there is no third-party shape to leave opaque.
account
​string · required
issuer
​string · required
otpauth_url
​string · required

otpauth:// URI encoding the same secret, issuer and account name, for a QR code.

secret
​string · required

Base32-encoded shared secret, for manual entry.

TotpVerifyRequest

Re-elevates a session with an already-enrolled TOTP authenticator (ZC-0778). The TOTP counterpart of [`SudoElevateRequest`] and [`WebauthnAuthenticateConfirmRequest`], and field-identical to [`TotpEnrollConfirmRequest`] while deliberately a separate type, for the reason [`SudoElevateRequest`]'s own doc comment gives: one confirms an enrollment, the other proves one.
code
​string · required

The current 6-digit code from the enrolled authenticator app. A wrong code, an already-used code and an account with no TOTP enrolled are answered identically; see controllers::sudo::elevate_error_response.

TrialPurchaseKindResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: upgrade
The trial is still running: buying ends the free period now and starts the paid one, on the same running service.

TrialPurchaseQuoteResponse

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.

TrialPurchaseResponse

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.

TrialPurchaseStateResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: state, kind, deadline
type = object · requires: state, kind, invoice_id
type = object · requires: state
Properties for Variant 1:
Buyable until `deadline`: `POST /subscriptions/{id}/trial/purchase`, priced by `GET /subscriptions/{id}/trial/purchase/quote`.
deadline
​string · date-time · required
​TrialPurchaseKindResponse · required

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

state
​string · const · required
Const value: available

TrialRefusalCodeResponse

string · enum
Enum values:
not_available
already_claimed
capacity_reached

Why a trial cannot be claimed, as a stable wire value.

Three, mirroring TrialClaimRefusal one for one. They are not collapsed into a single "unavailable" because the customer's next action differs for each: nothing (not_available), nothing ever (already_claimed), or come back later (capacity_reached).

TrialRefusalResponse

code
​TrialRefusalCodeResponse · enum · required

Why a trial cannot be claimed, as a stable wire value.

Three, mirroring TrialClaimRefusal one for one. They are not collapsed into a single "unavailable" because the customer's next action differs for each: nothing (not_available), nothing ever (already_claimed), or come back later (capacity_reached).

Enum values:
not_available
already_claimed
capacity_reached
message
​string · required

UnitAllocationResponse

A unit's allocation.
allocated_at
​string · date-time · required
allocation_id
​string · uuid · required

The id the unit's agent acknowledges in /zaroz/allocation.json.

generation
​integer · int32 · required

How many allocations the unit has had, this one included.

delivered_at
​string | null · date-time

When the unit's agent wrote the allocation's files; null until then. ?wait=delivered on allocate waits for it.

UnitControlRefusalResponse

Every refusal of a control route: a stable `code`, its documentation, and the figures that let a program act.
description
​string · required

What was refused, in a sentence.

code
​string · required

Stable, snake_case: policy_not_code, unit_input_differs, no_warm_unit, region_unreachable, container_unit_limit, stopped_unit_limit, org_unit_limit, start_rate, spend_cap, not_provisioned, container_busy, invalid_idempotency_key, or an input refusal (invalid_unit_key, reserved_unit_key, user_data_too_large, ...).

details
​string · required

code: description, the shape every units refusal takes.

docs_url
​string · required

The page that explains this refusal and what to do about it.

retry_after_seconds
​integer | null · uint32 · min: 0

How long to wait before trying again, when waiting helps (also the Retry-After header).

warm_ready
​integer | null · int32

no_warm_unit: warm units ready to allocate (zero) and still starting.

warm_starting
​integer | null · int32

UnitCountBucketResponse

How many units ran in one bucket.
at
​string · date-time · required

The bucket's start.

extra
​integer · uint32 · min: 0 · required

The most units billed as extra (beyond the plan's included units) at one instant inside the bucket.

max_running
​integer · uint32 · min: 0 · required

The most units running at one instant inside the bucket.

min_running
​integer · uint32 · min: 0 · required

The fewest units running at one instant inside the bucket.

UnitCountResponse

`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.

UnitEventResponse

One event: the contract-v1 envelope. Ignore fields and types you do not know.
type
​string · required
contract_version
​integer · int64 · required
data
​required

The type's own fields.

id
​string · required

evt_ and the event's UUID.

occurred_at
​string · date-time · required
seq
​integer · int64 · required

Gapless and strictly increasing per Container.

service_instance_id
​string · required
​

null for an event about the Container (units.scaled, rollout.*).

UnitEventUnitResponse

The unit an event is about.
id
​string · required
key
​string · required
​object · required

UnitLoadResponse

The latest sampled load of a running unit.
cpu_millicores
​number · double · required
memory_bytes
​integer · int64 · required
ready
​boolean · required
sampled_at
​string · date-time · required

When this was measured; a client judges how fresh it is.

cpu_limit_millicores
​integer | null · int64

Absent when the pod has no CPU limit.

memory_limit_bytes
​integer | null · int64

UnitLogLineResponse

at
​string · date-time · required
id
​string · required

<unit_id>:<incarnation>:<container>:<instant>#<n>. The same line has the same id in a history page and on the live stream, so a client draws it once.

text
​string · required

The line, verbatim.

container
​string | null
incarnation
​integer | null · int32
​
unit_id
​string | null · uuid
unit_ordinal
​integer | null · int32

The unit's slot, for the chip beside a line.

UnitLogMarkerResponse

A lifecycle fact of a unit, drawn between lines. `text` is the kernel's sentence for it; the other fields let a client choose its own wording.
at
​string · date-time · required
id
​string · required

Stable across reads: a client that polls forward and meets a fact twice draws it once.

kind
​LogMarkerKindResponse · enum · required
Enum values:
pod_started
pod_replaced
pod_stopped
pod_lost
pod_torn_down
container_restarted
overflow
text
​string · required
unit_id
​string · uuid · required
unit_ordinal
​integer · int32 · required
container
​string | null
exit_code
​integer | null · int32
incarnation
​integer | null · int32
​

Only on overflow: the range and number of lines moved to the archive.

reason
​string | null

For container_restarted, why the container ended (OOMKilled, Error, ...).

UnitLogWindowResponse

from
​string · date-time · required
to
​string · date-time · required

UnitLogsResponse

`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.

UnitMetricKind

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
cpu
memory
network_in
network_out

UnitMetricLineResponse

One unit's line.
​array[] · required

[bucket start in epoch milliseconds, value], 120 of them.

unit_id
​string · uuid · required
unit_ordinal
​integer · int32 · required

The unit's slot, which is what a chart's legend calls it (u1, u2).

limit
​number | null · double

The ceiling this line is measured against, or null when it is unmetered or the unit has no limit set.

​

Latest, mean and max of the buckets with a value; absent when there is none.

UnitMetricSeriesResponse

One kind of metric over the window.
available
​boolean · required

false when the source of this series did not answer; its lines are then all null.

​UnitMetricKind · required

What a customer may ask a metrics read for.

unit
​string · required

The unit every number of this series is in: millicores, bytes, bytes_per_second or lines.

​UnitMetricLineResponse[] · required

Each unit's own line, lowest slot first, at most ten (the ten with the highest mean over the window).

​

The whole service: the units summed. Absent when the read was narrowed to one unit.

UnitMetricWindow

string · enum
Enum values:
1h
6h
24h
7d
30d

A window a customer may ask for.

UnitMetricsResponse

`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

UnitOriginResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
Const value: policy
The scaling policy: a `fixed` or `autoscaled` slot unit.

UnitPodEndResponse

How the last container of a pod ended.
at
​string · date-time · required
container
​string · required
reason
​string · required

OOMKilled, Error, Completed, ... or unknown.

exit_code
​integer | null · int32

UnitPodResponse

One pod a unit has had.
billing_class
​string · enum · required

included when the plan's price covered the pod, extra when it was billed on top.

Enum values:
included
extra
id
​string · uuid · required
incarnation
​integer · int32 · required

1, 2, 3, ... per unit; the highest is the unit's current pod.

pod_name
​string · required
restarts
​integer · int64 · required

Container terminations inside this pod.

started_at
​string · date-time · required
unit_id
​string · uuid · required
​

Why the pod ended: replaced (the platform made the next one), stopped, torn_down or lost (it went away unasked: node loss, eviction). Absent while it runs.

ended_at
​string | null · date-time

Absent while the pod is the unit's current one.

​

The newest container termination inside this pod, with its reason and exit code.

UnitPublicEndpointResponse

One public address that reaches a single unit.
endpoint_id
​string · uuid · required

The endpoint's id, to read its bill line or remove the port.

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

The port the unit listens on inside its container.

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
public_ip
​string · required
public_port
​integer · uint16 · min: 0 · max: 65535 · required

UnitReadiness

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
Properties for Variant 1:
Const value: auto
Phase 1's rule: the app's own TCP probe when it has an endpoint, else started.

UnitResponse

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.

UnitScalingMarkerResponse

A scaling decision worth marking on the count chart.
action
​ScalingMarkerKind · enum · required

What a marker on the count chart says happened.

Enum values:
scale_up
scale_down
refused
rollout_paused
at
​string · date-time · required
from_units
​integer · int32 · required
id
​string · uuid · required
summary
​string · required

The kernel's sentence, for example "Scaled 2 to 4: CPU at 83% against a 60% target".

summary_code
​string · required

Which sentence summary is, <action>.<reason> such as scale_up.cpu_high.

​object · required

The figures in it (from, to, and used/target/min/max when recorded), for a client that words the sentence itself.

to_units
​integer · int32 · required

UnitTerminationEntryResponse

One container restart of a unit.
container
​string · required
id
​string · uuid · required
incarnation
​integer · int32 · required

The pod incarnation the container was in.

observed_at
​string · date-time · required

When the kernel saw the restart.

reason
​string · required

OOMKilled, Error, Completed, ... or unknown.

restart_count
​integer · int32 · required

The kubelet's restart count after this termination.

unit_id
​string · uuid · required
exit_code
​integer | null · int32
finished_at
​string | null · date-time

UnitTerminationResponse

How the unit's latest container ended.
at
​string · date-time · required

When the container ended, or when the platform noticed that it had.

container
​string · required
reason
​string · required

OOMKilled, Error, Completed, ... or unknown when the kubelet did not say.

exit_code
​integer | null · int32

UnitWebhookDeliveryResponse

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

UnitWebhookResponse

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.

UnitWebhookSecretResponse

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.

UpcomingChargeResponse

What the next renewal will charge in tax, computed by kernel from the subscription's pinned Price, its discount and the organization's tax jurisdiction -- the same calculation the renewal invoice is later issued from. A client renders it and adds nothing up.
currency
​string · required
subtotal_cents
​integer · int64 · required

The renewal's pre-tax amount, in cents.

tax_amount_cents
​integer · int64 · required

The tax on it, in cents; 0 when no tax applies.

total_cents
​integer · int64 · required

What the invoice will total (subtotal_cents + tax_amount_cents), in cents.

tax_label
​string | null

The tax's name (VAT); null when no rate applies.

tax_rate_basis_points
​integer | null · int32

The tax rate in basis points (2100 is 21%); null when no rate applies.

UpdateBucketRequest

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.

UpdateFirewallRuleRequest

A partial update. Every field is a double option: absent leaves the stored value alone, `null` clears it. The distinction is load-bearing rather than pedantic. `cidr: null` means "match any source", which on a `Block` rule is a *widening* of what the rule stops and on an `Allow` rule opens the port to the internet. A single `Option` would have made a client that omits the field indistinguishable from one that clears it, and the safer of those two readings is not the one a PATCH usually wants.
description
​string | null
Default: null
​
Default: null
cidr
​string | null
Default: null
​
Default: null
enabled
​boolean | null
Default: null
port_from
​integer | null · uint16 · min: 0 · max: 65535
Default: null
port_to
​integer | null · uint16 · min: 0 · max: 65535
Default: null
priority
​integer | null · uint16 · min: 0 · max: 65535
Default: null
​
Default: null

UpdateMeRequest

The profile fields a caller may change about themselves. Every field is optional and an omitted field is left alone, which is what makes this a `PATCH` rather than a `PUT`: the onboarding screen sends all three at once, while the account page sends whichever single control was touched, and neither has to send back values it never rendered. This is the request half of `/v1`'s deleted `PATCH /users/current-user` (`crates/http/identity/src/controllers/users/admin.rs::update_current_user_handler`), field for field. It carries no `is_onboarded`: completing onboarding is its own operation with its own refusal (`POST /me/complete-onboarding`), so a profile write can never flip the flag that gates the whole signed-in shell as a side effect of setting a name.
billing_country_code
​string | null

The country this account is billed from, ISO 3166-1 alpha-2, case insensitive. Refused with 422 once onboarding is complete and a country is already stored: the value decides tax treatment, so it is deliberately not something a customer can re-answer later.

discord_notifications_enabled
​boolean | null

Whether to relay notifications to Discord.

language
​string | null

A new display language tag.

name
​string | null

A new display name. Moderated by Username::try_new_moderated, so a name that is too short, too long, wrongly shaped or offensive is refused rather than stored.

UpdateMyWalletPreferencesRequest

The one writable preference. Required, not optional: a PATCH that changes nothing and answers 200 would tell the customer their autopay choice was saved when it was not.
autopay_enabled
​boolean · required

UpdateNetworkProtectionModeRequest

Choose the network protection mode for one allocation, or for the instance's dedicated IP. `"normal"` is the only mode any pool accepts today: Warden expresses protection as policy documents and publishes no mode list, and the external relay layer this field's other values came from is retired. The field stays because it is where a Warden policy selector will surface.
mode
​string · required

UpdateOrganizationMemberRequest

Body of `PATCH /organizations/{id}/members/{user_id}`. Unlike adding a member, `role` has no default here: silently defaulting a role-change request would either be a no-op or an unrequested demotion, neither of which the caller asked for.
role
​OrganizationRoleRequest · enum · required

Distinct from OrganizationRoleResponse because request and response enums are separate types on /v2 (design doc §43.4), even where they currently carry the same variants.

Enum values:
owner
admin
billing
member

UpdateOrganizationRequest

Body of `PATCH /organizations/{id}`. `name` and `slug` are each independently optional -- `None` means "leave unchanged" for both, matching PATCH semantics. A personal organization refuses to change either.
name
​string | null
Default: null
slug
​string | null
Default: null

UpdateProjectRequest

Body of `PATCH /projects/{id}`. `name`, `slug`, and `description` are each independently optional, matching PATCH semantics: an absent field leaves it unchanged. `description` is a double option -- absent leaves it unchanged, `null` clears it, and a string sets it -- decoded by `deserialize_double_option`.
description
​string | null
Default: null
name
​string | null
Default: null
slug
​string | null
Default: null

UpdateRegistryRequest

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

UpdateRuntimeConfigRequest

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

UpdateScalingPolicyRequest

`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

UpdateScheduleRequest

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
Default: []
paused
​boolean
Default: false

UpdateServiceInstanceRequest

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

UpdateServiceShareRequest

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

The complete new set, replacing the old one.

UpdateSftpRequest

`PUT .../sftp` body: replaces the account's enabled state, the same `{enabled}`-field shape `schedules`' own `paused` field models a toggle with, rather than splitting into `/enable` and `/disable` verbs.
enabled
​boolean · required

UpdateSharedDatabaseRowRequest

​object · required
primary_key_value
​required

Arbitrary JSON.

UpdateUnitRequest

`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.

UpdateUnitWebhookRequest

`PATCH .../units/webhooks/{webhook_id}`: an absent field is left as it is.
event_types
​array | null

Empty for every type.

url
​string | null

UploadTicketAttachmentResponse

What `POST /api/v2/support/tickets/{id}/attachments` hands back. Exactly the shape `TicketMessageAttachmentRequest` expects, so a composer can hold the response and post it straight back when the message is sent.
asset_id
​string · uuid · required
mime_type
​string · required
original_filename
​string · required
size_bytes
​integer · int64 · required

UsernameRuleDto

Mirrors [`UsernameRule`] by hand.
deny
​string[] · required
max_length
​integer · uint8 · min: 0 · max: 255 · required
min_length
​integer · uint8 · min: 0 · max: 255 · required
default
​string | null

VerifyEmailRequest

Completes a registration by submitting the token emailed after [`PasswordSignupRequest`].
token
​string · required

VerifyLoginCodeRequest

Completes a password login by submitting the code emailed after [`LoginRequest`].
challenge_id
​string · uuid · required
code
​string · required

VmAccessRequestDto

[`SetupStepId::VmAccess`](kernel_core::domain::setup_steps::SetupStepId::VmAccess)'s REQUEST shape (ZC-1410): a VPS's administrator account, submitted whole every time -- unlike `server_passwords`, there is no partial per-id update and no reveal/rotate route; a customer always resupplies the password over TLS, the same "one screen, submitted whole" shape v1's `VpsSetupFlow` already had. `ssh_authorized_keys` is raw multi-line OpenSSH text (one key per line, v1's own `paragraph` field shape), not a `Vec<String>` on the wire -- parsing and normalising it needs `kernel_core::domain::vps_ssh::parse_authorized_ssh_keys`, which the application layer's `VpsAccessService::set_access` calls, not this contract module. Every field but `username`/`password` is optional: the application layer fills a sensible default (`es-ES` for `keyboard`/`language`, `true` for `ssh_password_auth`) or normalises the field away entirely on an OS it does not apply to (Windows overwrites `username` and ignores every SSH/timezone field regardless of what is submitted here).
password
​string · required
username
​string · required
extra_packages
​string

v1's free-text extra packages (commas, spaces or newlines separate); Linux only.

Default:
install_qemu_agent
​boolean | null

v1's "install qemu-guest-agent" checkbox; Linux only, default true.

Default: null
keyboard
​string | null
Default: null
language
​string | null
Default: null
package_update
​boolean | null

v1's "package update" checkbox; Linux only, default true.

Default: null
package_upgrade
​boolean | null

v1's "package upgrade" checkbox; Linux only, default false.

Default: null
ssh_authorized_keys
​string
Default:
ssh_password_auth
​boolean | null
Default: null
timezone
​string | null
Default: null

VmAccessResponseDto

[`SetupStepAnswersResponseDto::VmAccess`]'s payload: every non-secret field of [`VmAccessAnswers`], plus `password_set` in place of the password itself -- never a value or a ciphertext, the same discipline [`ServerPasswordsResponseDto`] already applies to this step's one secret. `ssh_authorized_keys` here IS the normalised `Vec<String>` (public keys are not secret, so echoing them back is safe and lets a Settings screen show what is on file without re-parsing raw text).
extra_packages
​string[] · required
install_qemu_agent
​boolean · required
keyboard
​string · required
language
​string · required
package_update
​boolean · required
package_upgrade
​boolean · required
password_set
​boolean · required
ssh_authorized_keys
​string[] · required
ssh_password_auth
​boolean · required
timezone
​string · required
username
​string · required

VolumeSummaryResponse

Kernel's own summary of the storage behind one service.
capacity_bytes
​integer · uint64 · min: 0 · required

The claim's configured size. A quota rather than a measurement, so it is exactly what the customer bought and it never disagrees with the invoice.

mount_path
​string · required

Where the volume is bound inside the workload, for example /data. A cross reference rather than a property of the claim: the mount lives on the workload that mounts it, and this is the path the file browser opens at for the same volume.

last_backup_at
​string | null · date-time

When the customer's most recent on-demand backup of this volume finished, or null when they have never taken one that completed.

The customer's own snapshots only. The platform's disaster-recovery backups run on a different mechanism and are not counted here, because presenting one as the other would tell a customer their data is protected by something they never invoked.

used_bytes
​integer | null · uint64 · min: 0

What the volume holds, or null when kernel cannot measure exactly this claim: the service has no file sidecar, the sidecar's walk covers more than this volume, or it did not answer in time. Absent rather than approximated.

used_fraction
​number | null · double

A number in [0, 1], already clamped. Multiplying it by 100 for a percentage width is a unit conversion for the widget rather than a computation; deriving it from the two byte counts instead is not. null exactly when used_bytes is.

used_label
​string | null

Kernel's sentence about the meter, for example "3.0 GiB of 25 GiB". Render it as written. null exactly when used_bytes is.

VpsDetails

What the VPS details panel shows for a machine of either OS (V-0712): v1's `VpsDetailsView` (OS, account, public IPv6, internal addresses) plus where to connect, which for a Windows guest is RDP.
connect_port
​integer · uint16 · min: 0 · max: 65535 · required

22 for Linux (SSH), 3389 for Windows (RDP).

connect_protocol
​string · required

ssh or rdp.

operating_system
​string · required

linux or windows.

public_ipv4
​string · required

The machine's public IPv4 address; empty while it has none.

username
​string · required
internal_ipv4
​string | null
internal_ipv6
​string | null
public_ipv6
​string | null

VpsOperatingSystemOptionResponse

One operating system a customer may name in `CheckoutRequest::operating_system_id`. Deliberately its own, smaller wire type rather than the admin mount's `VpsOperatingSystemResponse` (`kernel_http_v2_admin::contracts:: vps_operating_systems`): this is a storefront read, so it carries what a customer picks between (name, logo, description, monthly fee, the shape floor that decides whether their chosen size can run it) and drops the provisioning internals a customer has no use for and should not see -- `source_kind`, `image_url`, `datasource_ref` (where KubeVirt pulls the disk from). `min_memory_gb`/`min_storage_gb` are fractional gibibytes, matching every other `*_gb` field on this surface, converted from the domain's exact byte counts.
description
​string · required
display_name
​string · required
distro_display_name
​string · required
family_display_name
​string · required
id
​string · uuid · required
logo_slug
​string · required
os_kind
​string · required

linux or windows.

slug
​string · required
tags
​string[] · required
fee_per_month
​number | null · double

This image's own recurring monthly fee, if it has one.

min_cpu
​number | null · float

The smallest shape this image can run on, when the image declares one. A chosen size below any of these three is refused at checkout with the matching VpsOperatingSystem*BelowMinimum error.

min_memory_gb
​number | null · double
min_storage_gb
​number | null · double

WalletBalanceResponse

What one organization holds in one currency.
amount_cents
​integer · int64 · required

Every pocket combined (real_cents + promo_cents + legacy_cents), signed. Not what a wallet payment can spend -- see spendable_cents. Kept for a caller reconciling this against the ledger's own raw sum; a wallet is normally at or above zero, but the ledger is the only source of truth and this reports what it sums to rather than clamping.

currency
​string · required

ISO 4217, three uppercase letters.

legacy_cents
​integer · int64 · required

Pre-ledger-cutover money not yet classified into a pocket. Not currently spendable by any wallet-payment path; included so real_cents + promo_cents + legacy_cents always equals amount_cents.

promo_cents
​integer · int64 · required

Promotional credit (ledger design spec §6): never withdrawable, never cash. Under the default policy it is not drawn as cash by a wallet payment; it is applied as an invoice discount line, automatically, when an invoice is issued AND (V-0882) when an unpaid, unnumbered invoice is paid from the wallet. A client shows it as its own figure ("promotional credit"), apart from real_cents and not part of spendable_cents.

real_cents
​integer · int64 · required

The pocket a wallet PAYMENT draws under the default policy (PromoTreatment::Discount): top-ups, refunds settled in cash, reseller payouts.

spendable_cents
​integer · int64 · required

What a wallet payment can draw right now, under the ledger's current policy set. This, not amount_cents, is the figure a "pay with wallet" affordance must compare its total against: a customer holding only promo credit under the default policy sees a non-zero amount_cents here but a spendable_cents of zero, which is why a wallet payment for them is refused as insufficient funds even though the balance above reads as positive.

WalletCodePreviewResponse

What a visitor holding a code is told about it before signing in. Deliberately three fields. It says what the code is worth and until when, because that is what the claim page and its OG image render; it says nothing about who issued it, who it was issued for, whether anyone has tried it, or what it is attached to. An inactive, redeemed, revoked, or expired code is not described at all -- it is a 404, indistinguishable from a code that never existed.
amount_cents
​integer · int64 · required

Face value in minor units of currency

currency
​string · required
expires_at
​string | null · date-time

When this code stops being redeemable, or null if it does not expire. A code already past this instant is reported as not found rather than returned with a past date.

WalletCreditQuoteLineResponse

One invoice line's share of the credit (spec 13.2, F24).
base_cents
​integer · int64 · required

The refunded amount before tax, in minor units.

invoice_id
​string · uuid · required
invoice_line_id
​string · uuid · required
tax_cents
​integer · int64 · required

WalletCreditQuoteResponse

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.

WalletEntryReasonResponse

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = string
type = string
type = string
Properties for Variant 1:
string · enum
Enum values:
top_up_manual
top_up_mollie
top_up_pay_pal
top_up_crypto
promo_grant
invoice_refund
sla_credit
reseller_payout

WalletEntryResponse

One movement of one organization's balance. **There is no `balance_after`.** An entry is a signed movement, and the balance after it is a property of the whole ledger up to that point, not of the row: it is only computable when the reader holds every earlier entry in the same currency, which a page never does. See [`ListOrganizationWalletEntriesResponse`] for the full reasoning and what serving one would take.
amount_cents
​integer · int64 · required

Integer cents, signed: positive credits the organization, negative debits it. Never a formatted string, and never a fraction of a cent.

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

ISO 4217, three uppercase letters. Entries in different currencies move different balances and must never be added together.

id
​string · uuid · required
​required

Why the movement happened. A closed vocabulary -- see [WalletEntryReasonResponse].

actor_user_id
​string | null · uuid

The person who caused the movement, when a person did.

The id alone, not a name: an organization is a shared workspace, so "who spent this" is a real question here in a way it never was on /me/wallet -- but resolving names is a second query, and the member listing already maps ids to people for exactly this organization. Absent for anything the system did on its own (autopay, a scheduled grant).

invoice_id
​string | null · uuid

The invoice this entry paid or refunded, when it names one.

note
​string | null

A free-text note, when one was written. Staff-authored on an adjustment; usually absent.

related_entry_id
​string | null · uuid

The entry this one reverses, when it is a reversal. The ledger is append-only, so a correction is always a second row pointing back at the first, never an edit of it.

WalletResponse

The caller's own wallet: their personal organization's balance, one row per currency. No `organization_id` field, unlike `OrganizationWalletResponse` -- dashboard never learns which organization backs this balance, only that it is "mine".
​WalletBalanceResponse[] · required

One row per currency the caller's personal organization holds.

A currency with no money in it has no row. An organization that has never been credited, or one whose movements net to exactly zero, answers with an empty list -- this is a normal, fully-formed 200, not a sign that the wallet is missing. Treat an absent currency as zero.

WalletTopUpAvailabilityResponse

Which providers can take a top-up right now.
nowpayments
​boolean · required
paypal
​boolean · required

WebPanelLogin

oneOf
Exactly one variant must match.

Decision Table

VariantMatching Criteria
type = object · requires: step
type = object · requires: step, password_id
Properties for Variant 1:
The product's `app_admin` setup step: a username and password the customer chose (or was given) at setup, for software whose account the customer creates (Nighty).
step
​string · const · required
Const value: app_admin

WebauthnAuthenticateConfirmRequest

Confirms a passkey authentication ceremony (ZC-0716). Field-identical to [`WebauthnRegisterConfirmRequest`] and deliberately a separate type: one confirms an enrollment, the other proves one, matching [`SudoElevateRequest`]'s own reasoning for not sharing [`SudoSetupRequest`]'s schema.
credential
​required

The browser's PublicKeyCredential assertion response (from navigator.credentials.get).

WebauthnRegisterConfirmRequest

Confirms a passkey registration ceremony (ZC-0716). `credential` is the browser's `RegisterPublicKeyCredential` response (from `navigator.credentials.create`), serialized to a string before being passed to `AdminElevationService::enroll` -- see `WebauthnElevationModule::enroll`'s own doc comment on why that module takes a JSON string rather than a typed struct (it parses and verifies the ceremony itself, against the challenge state `POST /auth/sudo/webauthn/register-options` stashed in Valkey). Carried here as `serde_json::Value` rather than `String` so an OpenAPI client sends the ceremony object as-is instead of double-encoding it.
credential
​required

Arbitrary JSON.

WorkloadRolloutResponse

One of the instance's workloads, and what it is doing right now. **This is not a property of any deployment**, and the field name says so: it is the live state of the things a deployment acts on, read off the resource rows the reconciler maintains. It is here because without it the history answers "somebody deployed at 14:02" and nothing answers "and is it serving", which is the question a customer opened the panel with. Only workload kinds appear. A volume, a Service and a namespace have no version to put into service, so listing them here would pad the answer with rows that can never move. **No image reference, and no crash message.** Both were considered and refused. On a product that declares `Deployments` without `Source` the image is operator-authored, so naming it tells a customer which internal registry path the platform pulls from; the Source capability returns the running image where the customer chose it themselves, which is the case where it is theirs to see. A kubelet's crash or image-pull message quotes registry hostnames, paths and occasionally credential fragments, and `ResourceStatusResponse::CrashLooping` carries the same operational fact with none of that.
is_primary
​boolean · required

Whether this is the workload the instance's product treats as primary.

kind
​ResourceKindResponse · enum · required

Mirrors ProvisionKind 1:1, kept as its own /v2 response type so this crate's wire format cannot drift silently if a variant is ever renamed on the domain enum without a matching, deliberate wire change here -- the same convention kernel_http_v2_catalog::contracts::products:: ProductFamilyView already follows for ProductFamily (the retired ProductKindResponse, which used to follow it for CatalogServiceKind, was deleted alongside that enum, A2-S13d).

Enum values:
persistent_volume_claim
deployment
virtual_machine
cluster_ip_service
external_service
ingress_route
tls_certificate
kubernetes_namespace
resource_id
​string · uuid · required
role
​string · required

The resource's declared role, e.g. "server". What a panel labels the row with when an instance owns several workloads.

​ResourceStatusResponse · required
updated_at
​string · date-time · required

When the reconciler last wrote this row. A rollout that landed shows a timestamp after the deployment that caused it; one that never landed shows a timestamp before it. That comparison is the closest thing to a per-deployment outcome the platform can currently support, and it is a hint rather than a verdict, which is why kernel does not present it as one.

WorldNameLabelDto

string · enum
Enum values:
world_name
save_name
instance_name
profile

Mirrors [WorldNameLabel] by hand.

WorldNameSettingDto

The `world_name` setting, with its label and effective `max_length`.
default
​string · required
label
​WorldNameLabelDto · enum · required

Mirrors [WorldNameLabel] by hand.

Enum values:
world_name
save_name
instance_name
profile
max_length
​integer · uint16 · min: 0 · max: 65535 · required

WriteFileRequest

New contents for one file.
content
​string · required

Replaces the file's contents entirely. Creates it when it does not exist.

RegistryTagView

name
​string · required
digest
​string · required

The manifest digest the tag points at (sha256:...).

size_bytes
​integer | null · uint64 · min: 0

Compressed size of the image: its config and layers.

created_at
​string | null · date-time

When the image was built. The registry reports no push time.

RegistryTagsResponse

​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.

ObjectView

key
​string · required

The full key.

size_bytes
​integer · uint64 · min: 0 · required
etag
​string | null
last_modified
​string | null · date-time

ObjectListingView

auto_preview_max_bytes
​integer · uint64 · min: 0 · required

Previewing a file loads it, and a download is billed egress. Show a preview without asking only for images up to this size.

folders
​string[] · required

Full prefixes of the folders directly under it, each ending in /.

max_upload_bytes
​integer · uint64 · min: 0 · required

The largest object the browser uploads.

​ObjectView[] · required
prefix
​string · required

The folder listed, "" for the root.

next_token
​string | null

Pass as token to get the next page; absent on the last.

ObjectListingResponse

​ObjectListingView · required

HeaderView

name
​string · required
value
​string · required

PresignedUrlView

A URL to call and the headers to send with it (besides `Content-Length`, which the browser sets).
expires_at
​string · date-time · required
​HeaderView[] · required
method
​string · required
url
​string · required

DownloadRequest

key
​string · required
save_as
​string | null

Makes the browser save the file under this name instead of showing it.

DownloadResponse

​PresignedUrlView · required

A GET. Billed egress, like any S3 download.

StartUploadRequest

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

StartUploadView

mode
​string · required

single or multipart.

part_bytes
​integer | null · uint64 · min: 0

multipart: every part but the last is this many bytes.

parts
​integer | null · uint32 · min: 0

multipart: how many parts there are.

​

single: PUT exactly size_bytes here.

upload_id
​string | null

multipart: the upload to add parts to.

StartUploadResponse

​StartUploadView · required

PresignPartsRequest

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

PartUrlView

number
​integer · uint32 · min: 0 · required
​PresignedUrlView · required

A URL to call and the headers to send with it (besides Content-Length, which the browser sets).

PresignPartsResponse

​PartUrlView[] · required

CompletedPartRequest

etag
​string · required

The ETag response header of the part's PUT.

number
​integer · uint32 · min: 0 · required

CompleteUploadRequest

key
​string · required
​CompletedPartRequest[] · required
upload_id
​string · required

AbortUploadRequest

key
​string · required
upload_id
​string · required

DeleteObjectsRequest

keys
​string[] · required

1 to 1000 object keys.

DeleteObjectsView

failed
​string[] · required

Keys that could not be deleted; the rest are gone.

DeleteObjectsResponse

​DeleteObjectsView · required

CreateFolderRequest

prefix
​string · required

The folder's full path, e.g. photos/2026/.

SearchKnowledgeTier

string · enum
Enum values:
best
also
alt

SearchKnowledgeProductExpectation

string · enum
Enum values:
live
coming-soon
planned
retired
external

SearchKnowledgeConceptKind

string · enum
Enum values:
technology
need
comparison
game
unsupported

SearchKnowledgeProvenance

string · enum
Enum values:
ai-draft
owner-reviewed
data-driven

SearchKnowledgeGamingBridge

string · enum
Enum values:
first
after

SearchKnowledgeProductStatus

string · enum
Enum values:
live
coming-soon
planned
retired

SearchKnowledgeRecommendation

slug
​string · required

A catalog product slug named in products.

expect
​SearchKnowledgeProductExpectation · enum · required
Enum values:
live
coming-soon
planned
retired
external
tier
​SearchKnowledgeTier · enum · required
Enum values:
best
also
alt
​object · required

One sentence saying why, per locale.

SearchKnowledgeConcept

id
​string · required

kebab-case, unique across the knowledge base.

kind
​SearchKnowledgeConceptKind · enum · required
Enum values:
technology
need
comparison
game
unsupported
​object · required

What people type, per locale.

​SearchKnowledgeRecommendation[] · required
provenance
​SearchKnowledgeProvenance · enum · required
Enum values:
ai-draft
owner-reviewed
data-driven
reviewedAt
​string | null · required

ISO date of the last human review.

platformClaims
​string[] · required

Ids of claims this entry relies on.

name
​string | null

A proper noun shown in copy; required for unsupported.

​

For unsupported: what to say about it.

​

For game: whether the Gaming bridge goes before or after the answers.

soldInGaming
​boolean | null

SearchKnowledgePlatformClaim

id
​string · required
statement
​string · required
sources
​string[] · required
caveat
​string | null
reviewedAt
​string | null

ISO date a human last confirmed this claim still holds.

SearchKnowledgeProduct

slug
​string · required
status
​SearchKnowledgeProductStatus · enum · required
Enum values:
live
coming-soon
planned
retired
replacedBy
​array | null

For retired: the products that took over.

SearchKnowledgePageTerms

id
​string · required
​object · required

SearchKnowledge

version
​string · required
​SearchKnowledgeConcept[] · required
​SearchKnowledgeProduct[] · required
​SearchKnowledgePlatformClaim[] · required
​SearchKnowledgePageTerms[] · required