Deployments
Deployment Zones
Shared and dedicated zones — what a deployment zone models, how releases and instances relate to one, and where the dedicated model meets Kaiten's own editions.
A deployment zone is a logical execution environment where instances run. It is the unit at which a release lands and the place an instance attaches to. A zone is not a cluster, a cloud account, or a region by itself — those are properties a zone may carry — but the thing that has a current release and a set of instances at the same time.
Examples use $KAITEN_API and $KAITEN_TOKEN as set up in the quickstart, and add to the seeded Kaiten Sushi Shop zones without altering them. Responses are illustrative — ids, actors, and timestamps depend on your stack.
What a zone models
Two isolation models, one entity:
- A shared zone runs instances of many customers side by side — one operated stack of your product serving several restaurants. Kaiten Sushi Shop runs two:
shared-euandshared-apac. - A dedicated zone runs the instances of one customer alone — its own stack, upgraded on its own schedule. Sakura Tokyo's
sakura-dedicatedis one; its metadata recordscompliance_profile: pci-dss, the reason it exists.
Both are the same record with the same fields, and nothing in the record's structure separates them: Kaiten Sushi Shop says which is which with a boolean metadata field, dedicated, set to true on sakura-dedicated alone. What differs is how you operate them, and the model makes that visible: the three seeded zones hold three journal histories, and the dedicated one still runs 2026.7.0 while both shared zones moved to 2026.8.0 — a fact the journal shows and the contract can be compared against.
Fields
| Field | Required | What it holds |
|---|---|---|
name | yes | Display name, at least 1 character |
type | yes | Environment class of the zone, free-form — see below |
description | yes | Free text |
slug | no | 2–100 characters, ^[a-z0-9][a-z0-9-]*[a-z0-9]$, unique per organization; generated when omitted |
metadata | no | Typed properties, validated against the DEPLOYMENT_ZONE metadata fields your organization declared |
releaseId | no | The release the zone currently runs |
Responses add id, createdBy, createdAt, updatedBy, and updatedAt. Reading requires read:deployment_zones; every write requires write:deployment_zones.
type is the zone's environment class. The API stores any string as sent and exposes it to targeting rules as __kaiten.deploymentZone.type; production, staging, and development are the values the console labels. Kaiten Sushi Shop follows that convention, and its three zones are all production — whether a zone is shared or dedicated is not an environment, so it is not recorded here. Pick one convention and hold to it: rules and filters compare the raw value.
metadata is where the infrastructure facts go. Kaiten Sushi Shop declares three zone fields — compliance_profile (Compliance profile), an enum of standard and pci-dss; region (Cloud region), free text; and dedicated (Dedicated), a boolean — so every zone carries them in a shape the console renders and a rule can read. A write whose metadata does not conform to the declared fields is rejected.
Create a zone
Open a shared zone for North American restaurants, already running the current release. Supplying releaseId at creation records the zone's first deployment — one journal entry, one RELEASE_DEPLOYED. Look up the release's id — a zone takes the id, not the slug. The seeded 2026.8.0 is also addressable as r-2026-8-0; finding it by version in the list works on any stack:
export RELEASE_280_ID=$(curl -s "$KAITEN_API/releases" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
| jq -r '.items[] | select(.version == "2026.8.0") | .id')curl -X POST "$KAITEN_API/deployment-zones" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Shared US",
"slug": "shared-us",
"type": "production",
"description": "Shared zone for North American restaurants.",
"metadata": { "region": "us-east-1", "compliance_profile": "standard", "dedicated": false },
"releaseId": "'"$RELEASE_280_ID"'"
}'{
"id": "5e6f7a8b-9c0d-4e1f-a2b3-c4d5e6f7a8b9",
"name": "Shared US",
"slug": "shared-us",
"type": "production",
"description": "Shared zone for North American restaurants.",
"metadata": { "region": "us-east-1", "compliance_profile": "standard", "dedicated": false },
"releaseId": "2b6f9c1e-8d3a-4f7b-9e0c-6a5d4b3c2f1e",
"createdBy": { "id": "…", "name": "…" },
"createdAt": "2026-09-08T11:00:00Z",
"updatedBy": { "id": "…", "name": "…" },
"updatedAt": "2026-09-08T11:00:00Z"
}201 Created; DEPLOYMENT_ZONE_CREATED and RELEASE_DEPLOYED are emitted. A slug already in use is a 409. A compliance_profile outside the declared enum — "hipaa", say — is refused, because the metadata field says what is allowed there.
A zone created without releaseId exists with nothing deployed: it has no releaseId until the first PUT that sets one.
Update a zone
PUT /deployment-zones/{slug} replaces the zone's details — name, type, and description are required again, and metadata is written as sent. Send everything you want to keep.
curl -i -X PUT "$KAITEN_API/deployment-zones/shared-us" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Shared US East",
"type": "production",
"description": "Shared zone for North American restaurants (US East).",
"metadata": { "region": "us-east-1", "compliance_profile": "standard", "dedicated": false },
"releaseId": "'"$RELEASE_280_ID"'"
}'HTTP/1.1 204 No Content; DEPLOYMENT_ZONE_UPDATED is emitted. The releaseId in this call echoes the current one, so the journal is untouched — omitting it has the same effect. Sending a different releaseId is how a deployment is recorded; that is the subject of Deployments & Lifecycle.
How instances relate to zones
An instance optionally points at one zone, through deploymentZoneId; responses also carry the zone's slug as deploymentZoneSlug. The link is the whole of the runtime association: an instance runs whatever its zone currently runs, and that is a lookup — instance → zone → releaseId — not a field you maintain on the instance. The six seeded instances are spread across the three zones: sakura-tokyo-prod alone in sakura-dedicated; demo-restaurant-prod in shared-eu; and sakura-tokyo-demo, ninja-osaka-prod, ninja-osaka-delivery, and kappa-kyoto-prod side by side in shared-apac — one Customer can have an instance in a dedicated zone and another in a shared one.
Placing an instance in a zone emits INSTANCE_DEPLOYED; moving it from one zone to another emits INSTANCE_MIGRATED.
At creation. POST /instances accepts deploymentZoneId, and the instance is placed from its first moment. A staging instance for Sakura Tokyo, on the current version of its Premium License, in the shared APAC zone:
curl -X POST "$KAITEN_API/instances" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Sakura Tokyo Staging",
"slug": "sakura-tokyo-staging",
"description": "Staging instance for the Sakura Tokyo restaurant group.",
"customerId": "'"$SAKURA_TOKYO_ID"'",
"licenseId": "'"$PREMIUM_ID"'",
"deploymentZoneId": "'"$SHARED_APAC_ID"'",
"startLicenseDate": "2026-09-01T00:00:00Z",
"endLicenseDate": "2027-09-01T00:00:00Z",
"metadata": { "environment": "staging" }
}'The ids come from GET /customers/sakura-tokyo, GET /deployment-zones/shared-apac, and GET /license-families/premium, whose currentVersion.id is the version the Premium family serves — premium-v2. GET /licenses/premium would return the family's first version, which is archived: assigning it to an instance is refused with 422 Unprocessable Entity. metadata is the instance's only free-form object, and it is typed like the zone's: every key a declared INSTANCE metadata field covers is validated against that field (Kaiten Sushi Shop declares environment, an enum of production, staging, and demo), and keys no field declares are kept as sent. There is no separate infrastructure object on an instance: a region or a runtime, if you want the model to know and the console to render them, are metadata fields you declare. The 201 response carries the placement:
{
"id": "…",
"name": "Sakura Tokyo Staging",
"slug": "sakura-tokyo-staging",
"customerId": "…",
"customerSlug": "sakura-tokyo",
"licenseId": "…",
"licenseSlug": "premium-v2",
"deploymentZoneId": "…",
"deploymentZoneSlug": "shared-apac",
"status": "HEALTHY",
"metadata": { "environment": "staging" },
"…": "…"
}Later. PUT /instances/{slug} with a different deploymentZoneId moves the instance. The same call with deploymentZoneId omitted preserves the current zone, and clearing the link back to "no zone" is not supported through this endpoint. PUT on an instance is also a full replacement, so resend the fields you keep:
curl -i -X PUT "$KAITEN_API/instances/sakura-tokyo-staging" \
-H "Authorization: Bearer $KAITEN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Sakura Tokyo Staging",
"description": "Staging instance for the Sakura Tokyo restaurant group.",
"customerId": "'"$SAKURA_TOKYO_ID"'",
"licenseId": "'"$PREMIUM_ID"'",
"deploymentZoneId": "'"$SAKURA_DEDICATED_ID"'",
"startLicenseDate": "2026-09-01T00:00:00Z",
"endLicenseDate": "2027-09-01T00:00:00Z",
"metadata": { "environment": "staging" }
}'HTTP/1.1 204 No Content. The staging instance now runs what sakura-dedicated runs — 2026.7.0, until that zone is upgraded.
A deploymentZoneId that does not exist in your organization is a 404, on create and on update alike: another organization's zone is indistinguishable from a zone that does not exist.
Delete a zone
DELETE /deployment-zones/{slug} returns 204 No Content and emits DEPLOYMENT_ZONE_DELETED. The instances that were placed in the zone are detached, not deleted: their deploymentZoneId is cleared, so the model no longer knows what they run until they are placed again. The journal entries recorded for the zone are removed with it; the releases they pointed at are untouched.
Zones in targeting rules
A zone's type and typed metadata are among the facts a flag rule sees, under __kaiten.deploymentZone. A rule that serves a variant only to instances running in a dedicated, PCI-certified zone:
__kaiten.deploymentZone.metadata.dedicated == true && __kaiten.deploymentZone.metadata.compliance_profile == 'pci-dss'A rule that reads the environment class, which every seeded zone matches:
__kaiten.deploymentZone.type == 'production'Dedicated zones and the Standalone edition
If your product is sold with a dedicated option — a customer's own stack, on their infrastructure or yours — a dedicated zone is how that option appears in the model: one zone, that customer's instances, its own release history, and metadata that says why it is separate. Kaiten is offered the same way. Kaiten Cloud runs your organization on a shared, managed stack; the Standalone edition runs Kaiten itself dedicated, on your infrastructure. The editions page lays out what each edition includes.
Events
| Event name | Event type | When |
|---|---|---|
DEPLOYMENT_ZONE_CREATED | com.kaiten.deployment_zone.v1.created | A zone is created |
DEPLOYMENT_ZONE_UPDATED | com.kaiten.deployment_zone.v1.updated | A zone's details change |
DEPLOYMENT_ZONE_DELETED | com.kaiten.deployment_zone.v1.deleted | A zone is deleted |
RELEASE_DEPLOYED | com.kaiten.deployment_zone.v1.release_deployed | A zone's releaseId is set at creation or changed |
INSTANCE_DEPLOYED | com.kaiten.instance.v1.deployed | An instance is placed in a zone |
INSTANCE_MIGRATED | com.kaiten.instance.v1.migrated | An instance moves from one zone to another |
The zone events carry the DeploymentZone object; the instance events carry the Instance; RELEASE_DEPLOYED carries the journal entry itself.
Where to go next
What happens when a zone's release changes, and how to read what a zone ran and when: Deployments & Lifecycle. Exact reference: data model.
Components & Releases
The component as a versioned brick and the release as an immutable bundle of component versions — creation, versioning, the immutability rule, and the associations the API exposes.
Deployments & Lifecycle
The append-only deployment journal — how a deployment is recorded, how the instance linkage resolves what runs where, how history is read, and the lifecycle states the model carries.