Catalog
List My Referral Links
Lists the caller's own referral links, each with its click count (use_count) and its real signup count (conversion_count). Never returns another account's links -- there is no id-scoped read on this surface at all, only this self-scoped list.
List My Referral Links › Responses
The caller's own referral links
Create My Referral Link
Creates a new referral link with a custom slug, owned by the caller's personal organization. The slug must be unique platform-wide, 1-64 characters of letters, numbers, hyphens, or underscores; it is lowercased and trimmed before the uniqueness check. Rate-limited per caller (RateLimitScope::ReferralLinkCreate).
Create My Referral Link › Request Body
slug1-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.
Create My Referral Link › Responses
The new link
conversion_countcreated_atidslugupdated_atuse_countDelete My Referral Link
Deletes one of the caller's own referral links immediately. A link id that belongs to another account, or to nothing at all, both answer the same not found, so this route cannot be used to discover which referral link ids exist or who owns them.
path Parameters
idDelete My Referral Link › Responses
Link deleted
Redeem My Referral Link
Attributes the authenticated user to the referral link with the given slug, and increments its click count. This is /v1's own two-step attribution mechanism (register, then call this once signed in), kept for an account that signed up with no code attached -- an account that submitted referral_code on POST /auth/password/signup or the OAuth login start is attributed automatically when the account is created (ZC-1032, owner ruling 2026-09-17) and never needs this route at all. Refuses the caller's own link (a business rule, not an enumeration concern: nobody discovers a slug they did not create). Idempotent in the failure direction only: an account already attributed to any link -- this one or another -- gets a conflict rather than a second row.
path Parameters
slugRedeem My Referral Link › Responses
Conversion recorded
List Products
Lists catalog products available to the caller's own storefront, paginated. A customer bound to a reseller sees only that reseller's enabled products, priced with its multiplier; a platform customer sees the full platform catalog. Only products sold on the requested sales_channel are listed: console (the default) or dashboard (the gaming store, where every game server is sold).
query Parameters
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
sales_channelWhich storefront is asking: console (default) or dashboard (the gaming store).
A product is listed only on the channels it is sold on. The x-zaroz-client header
is observation-only and never selects a channel.
Which storefront is asking: console (default) or dashboard (the gaming store).
A product is listed only on the channels it is sold on. The x-zaroz-client header
is observation-only and never selects a channel.
List Products › Responses
Products
Pagination metadata included in paginated responses
Get Product
Gets one catalog product, on the caller's own storefront.
path Parameters
idGet Product › Responses
The product
available_for_orderWhether this product can currently start a new order
categoryThe 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.
created_atWhen this product was created. There is no separate published_at; console uses this as the product's introduced date for discovery dots (ZC-1302).
enabledfamilyThe 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.
growable_dimensionsThe 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).
has_free_trialWhether this product currently has an enabled, non-expired free-trial configuration. Per-account eligibility is not checked here.
idminimum_cpuminimum_memory_gbFractional gibibytes (2026-09-04 mebibyte migration): 0.5 is 512 MiB.
minimum_storage_gbnameproduct_slugThis 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.
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_stepsThe 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.
resize_policy_kindHow 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.
slugURL-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.
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.
iconAn 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_idmaximum_cpumaximum_memory_gbmaximum_storage_gbPer-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_priceCheapest 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.
List Product Plans
Lists the plans available for one product on the caller's own storefront, each with its resolved monthly price. A plan whose price cannot be resolved (no live Price matches, or an ambiguous catalog data bug) is listed with price: null rather than removed or errored -- see this slice's report for why. Pass region_id, from the region listing, to have each plan additionally carry kernel's verdict for that region: available true, or available false with an unavailable_reason sentence to be rendered exactly as written. A refused plan stays in the set, disabled, so the grid does not reshuffle when the region above it changes, and a null price is never a refusal. Without region_id nothing has been judged, so every plan is available with no reason. Pass hardware_profile_id, a tier from the region listing, to price every plan (and judge it, with region_id) on that tier alone. No capacity figure of any kind is returned either way.
path Parameters
idquery Parameters
hardware_profile_idThe hardware tier the customer has chosen, as
RegionHardwareResponse.id from GET /api/v2/catalog/regions
(decision-0347: tiers carry their own Prices, so a plan costs what it
costs on that tier).
Optional. Given, every plan's price and terms are resolved on
that tier alone, and -- with region_id -- its verdict is that tier's,
by the narrowing checkout applies to CheckoutRequest.hardware_profile_id.
Omitted, prices are the cheapest across every tier, as before. A tier
that does not exist, is not sold, or is not in region_id is a 404.
The hardware tier the customer has chosen, as
RegionHardwareResponse.id from GET /api/v2/catalog/regions
(decision-0347: tiers carry their own Prices, so a plan costs what it
costs on that tier).
Optional. Given, every plan's price and terms are resolved on
that tier alone, and -- with region_id -- its verdict is that tier's,
by the narrowing checkout applies to CheckoutRequest.hardware_profile_id.
Omitted, prices are the cheapest across every tier, as before. A tier
that does not exist, is not sold, or is not in region_id is a 404.
langLanguage code (e.g. es-ES) plan names and descriptions are returned
in (V-0921), falling back to en-US and then to the stored text.
Language code (e.g. es-ES) plan names and descriptions are returned
in (V-0921), falling back to en-US and then to the stored text.
region_idThe region the customer has already chosen, as RegionResponse.id
from GET /api/v2/console/catalog/regions.
Optional. Omitted, this listing is what it has always been: every
visible plan with its resolved price, nothing judged and so nothing
refused. Given, each plan additionally carries available and
unavailable_reason for that region, resolved by the placement rule
checkout itself applies.
It exists because a plan carried no per-region verdict at all, so an
unbuyable (plan, region) pair was only discovered at checkout
(purchase/21-steps.md §3.2). A null price is not a refusal
sentence, and a plan card rendering a blank where a price goes is the
worst of the three possible answers.
The region the customer has already chosen, as RegionResponse.id
from GET /api/v2/console/catalog/regions.
Optional. Omitted, this listing is what it has always been: every
visible plan with its resolved price, nothing judged and so nothing
refused. Given, each plan additionally carries available and
unavailable_reason for that region, resolved by the placement rule
checkout itself applies.
It exists because a plan carried no per-region verdict at all, so an
unbuyable (plan, region) pair was only discovered at checkout
(purchase/21-steps.md §3.2). A null price is not a refusal
sentence, and a plan card rendering a blank where a price goes is the
worst of the three possible answers.
List Product Plans › Responses
Plans
List Product Operating Systems
Lists the operating systems a VPS product can be built from, for the caller's own storefront. Every enabled image in the platform catalog, each with the shape floor (min_cpu/min_memory_gb/min_storage_gb) a chosen size must meet -- send the chosen image's id as operating_system_id on POST /checkout. A product that is not a VPS answers with an empty list rather than an error.
path Parameters
idList Product Operating Systems › Responses
Every operating system this product can be built from
List Purchasable Regions
Lists every region this customer is sold the given product in, in catalog order, each carrying kernel's verdict: available true, or available false with an unavailable_reason sentence to be rendered exactly as written. A region is available when it holds a hardware profile that the plan allows, has room for its cpu/memory/storage right now, and has a live published price -- the same rules POST /api/v2/console/checkout applies to the region that is then chosen, so anything marked available here is buyable. A region that fails those tests is returned refused rather than omitted, so a customer who came for one region is told it is unavailable instead of being left to wonder whether the product sells there at all. Omitting plan_id asks the weaker question the first step of a purchase flow actually has -- which regions sell this product at all -- answered as a union over every plan the caller can see; the refusal sentence then names no plan. Two things are membership rather than verdict and are simply absent: a region no plan may use a profile in, and a region this caller's storefront may not order into. No capacity figure of any kind is returned -- not a count, a percentage, a node or a cluster -- only the boolean and the sentence. Each region lists its hardware tiers under hardware (name, processor, disk, and the same available/unavailable_reason verdict), so a customer can choose one and send its id as hardware_profile_id to the plan listing, the quote and checkout; omitting it lets kernel place the service on the cheapest tier that fits. An empty list means this product is sold nowhere this caller can reach. cpu, memory_gb and storage_gb are optional and must be sent together or not at all: they name a shape the customer has sized for themselves, which is judged instead of the named plan's own shape for this scan, so a region that cannot run the customer's actual shape is refused here rather than only at checkout.
query Parameters
product_idThe product being bought, as ProductResponse.id from
GET /api/v2/console/catalog/products.
The product being bought, as ProductResponse.id from
GET /api/v2/console/catalog/products.
cpuThe processor share the customer has sized for themselves, when they
have gone on to size one. Must be sent together with memory_gb and
storage_gb, or not at all.
00-program.md §38's finding: this endpoint used to price its
scan only against the plan's own shape, so a customer who sized a
custom shape larger than their plan could clear this step and be
refused only at checkout -- the refusal arriving as far from the
cause (the region picked first) as it could. Sending the shape being
actually priced here closes that gap: a region judged against it is a
region checkout will actually accept.
Optional, and omitting all three is every existing caller's behaviour, unchanged: each region is judged against the named plan's own shape (or, with no plan named, the union over every visible plan's own shape).
The processor share the customer has sized for themselves, when they
have gone on to size one. Must be sent together with memory_gb and
storage_gb, or not at all.
00-program.md §38's finding: this endpoint used to price its
scan only against the plan's own shape, so a customer who sized a
custom shape larger than their plan could clear this step and be
refused only at checkout -- the refusal arriving as far from the
cause (the region picked first) as it could. Sending the shape being
actually priced here closes that gap: a region judged against it is a
region checkout will actually accept.
Optional, and omitting all three is every existing caller's behaviour, unchanged: each region is judged against the named plan's own shape (or, with no plan named, the union over every visible plan's own shape).
memory_gbMemory in GiB, fractional (0.5 = 512 MiB). See cpu.
Memory in GiB, fractional (0.5 = 512 MiB). See cpu.
plan_idThe chosen plan, as PlanResponse.id from
GET /api/v2/console/catalog/products/{id}/plans. An i32
(impl_int_id!), not a UUID.
Optional, because the purchase flow asks region before plan
(purchase/21-steps.md §3.1) and step one therefore has no plan to
name. Omitted, this answers "which regions sell this product at all" as
a union over every plan the caller can see -- not an intersection, and
not a default plan, which would make step one's answer depend on a
choice the customer has not made.
The chosen plan, as PlanResponse.id from
GET /api/v2/console/catalog/products/{id}/plans. An i32
(impl_int_id!), not a UUID.
Optional, because the purchase flow asks region before plan
(purchase/21-steps.md §3.1) and step one therefore has no plan to
name. Omitted, this answers "which regions sell this product at all" as
a union over every plan the caller can see -- not an intersection, and
not a default plan, which would make step one's answer depend on a
choice the customer has not made.
storage_gbStorage in GiB. See cpu.
Storage in GiB. See cpu.
List Purchasable Regions › Responses
The regions this product and plan can be bought in
List Public Products
Lists the enabled catalog products a storefront sells, paginated. Requires no authentication: this is the shop window the marketing site renders. Pass reseller with a reseller storefront hostname to get that storefront's visible products and its multiplied prices; omit it for the platform's own catalog and unmultiplied prices. An unknown or paused hostname is rejected rather than served at platform prices. Disabled products are never listed.
query Parameters
pagePage number (1-indexed). Defaults to 1.
Page number (1-indexed). Defaults to 1.
page_sizeNumber of items per page. Defaults to 20, max 100.
Number of items per page. Defaults to 20, max 100.
resellerHostname of the reseller storefront this request is being rendered
for. Carried over unchanged from /v1's StorefrontQuery::reseller
(kernel_http_catalog::controllers::catalog), which is the only
mechanism in the codebase by which an anonymous catalog reader is
given a reseller's prices, visible products, and free-trial badges.
Omitted means the platform's own storefront: no price multiplier and
no visibility filter. An unknown, paused, or unparseable hostname is
rejected with 404 rather than silently falling back to platform
pricing.
Hostname of the reseller storefront this request is being rendered
for. Carried over unchanged from /v1's StorefrontQuery::reseller
(kernel_http_catalog::controllers::catalog), which is the only
mechanism in the codebase by which an anonymous catalog reader is
given a reseller's prices, visible products, and free-trial badges.
Omitted means the platform's own storefront: no price multiplier and
no visibility filter. An unknown, paused, or unparseable hostname is
rejected with 404 rather than silently falling back to platform
pricing.
List Public Products › Responses
Products this storefront sells
Pagination metadata included in paginated responses
Get Public Product
Gets one product from a storefront's public catalog. Requires no authentication. Accepts the same reseller storefront hostname as the listing; a product hidden or disabled on that storefront is reported as not found rather than served with a disabled flag.
path Parameters
idquery Parameters
resellerSee ListPublicProductsRequest::reseller.
See ListPublicProductsRequest::reseller.
Get Public Product › Responses
The product
available_for_orderWhether this product can currently start a new order
categoryfamilyThe 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.
has_free_trialWhether 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.
idnameproduct_slugThis 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.
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.
iconAn 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_idPer-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_priceCheapest 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.
List Public Product Plans
Lists the plans one product is sold in on a storefront, each with its resolved monthly price. Requires no authentication. Accepts the same reseller storefront hostname as the listing. A plan whose price cannot be resolved (no live Price matches, or an ambiguous catalog data bug) is listed with price: null rather than removed or errored, so a pricing gap shows as a missing figure instead of an empty shop.
path Parameters
idquery Parameters
resellerSee ListPublicProductsRequest::reseller.
See ListPublicProductsRequest::reseller.
List Public Product Plans › Responses
Plans
Get Search Knowledge
The command palette's search knowledge base: concepts (what people type for a need or a technology), the catalog products they point at, platform claims with sources, and page vocabulary. Requires no authentication and is the same for every storefront. Pass locale to keep one locale's text. Sends an ETag; If-None-Match answers 304.
query Parameters
localeKeep only this locale's vocabulary and copy (en-US or es-ES).
Omit it for every locale, which is what the ranker uses to search
across languages.
Keep only this locale's vocabulary and copy (en-US or es-ES).
Omit it for every locale, which is what the ranker uses to search
across languages.
Get Search Knowledge › Responses
The knowledge base
version