Zaroz
Units API guide

Units API: overview and authentication

A Workload is your app on Zaroz. It runs as one or more units (copies). The Units API is how you, or the code inside your own Workloads, decide which units exist: create one by name, hand a warm one to a match, stop one that nobody uses, and watch what happened.

Every page in this guide has the same three samples for each operation: curl, TypeScript and Python. They assume the small helper below and nothing else. Copy it once.

curl

TerminalCode
export ZAROZ_API="https://kernel.zaroz.cloud/api/v2" export ZAROZ_TOKEN="zaroz_..." # see "Authentication" export PROJECT="<project uuid>" export SERVICE="<service instance uuid>" UNITS="$ZAROZ_API/projects/$PROJECT/service-instances/$SERVICE/units" AUTH="Authorization: Bearer $ZAROZ_TOKEN"

TypeScript

TypeScriptCode
// zaroz.ts: the only helper every sample on these pages relies on (Node 20+, no dependencies). const API = process.env.ZAROZ_API ?? "https://kernel.zaroz.cloud/api/v2"; const TOKEN = process.env.ZAROZ_TOKEN!; export const PROJECT = process.env.PROJECT ?? ""; export const SERVICE = process.env.SERVICE ?? ""; export const UNITS = `/projects/${PROJECT}/service-instances/${SERVICE}/units`; export async function call<T = any>( method: string, path: string, body?: unknown, headers: Record<string, string> = {}, ): Promise<{ status: number; body: T }> { const res = await fetch(`${API}${path}`, { method, headers: { Authorization: `Bearer ${TOKEN}`, ...(body !== undefined ? { "Content-Type": "application/json" } : {}), ...headers, }, body: body !== undefined ? JSON.stringify(body) : undefined, }); const text = await res.text(); return { status: res.status, body: (text ? JSON.parse(text) : null) as T }; }

Python

Code
# zaroz.py: the only helper every sample on these pages relies on (pip install requests). import os import requests API = os.environ.get("ZAROZ_API", "https://kernel.zaroz.cloud/api/v2") PROJECT = os.environ.get("PROJECT", "") SERVICE = os.environ.get("SERVICE", "") UNITS = f"/projects/{PROJECT}/service-instances/{SERVICE}/units" _session = requests.Session() _session.headers["Authorization"] = "Bearer " + os.environ["ZAROZ_TOKEN"] def call(method, path, body=None, **kwargs): """Returns (status, parsed JSON or None).""" r = _session.request(method, API + path, json=body, timeout=40, **kwargs) return r.status_code, (r.json() if r.content else None)

Every route in this guide is in the kernel and in the API reference: list, policy, quote, logs, metrics and decisions; create by key, start, stop, destroy, labels and allocate; events; webhooks (create, update, delete, enable, test, rotate-secret, deliveries); service principals and their credentials; and the replace_idle and next_start rollouts. The one exception is GET .../units/webhooks/metrics, which the kernel serves but the reference does not list yet.

Where a request goes

Every Units route lives under one service:

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

{project_id} and {id} are UUIDs: the project, and the Workload (a service instance). Both are in the console URL of the Workload page.

Route familyPage
POST .../units, start, stop, DELETE .../units/{key}Create by key and the lifecycle
PATCH .../units/{key}, user_data, list filtersLabels, user data and filters
PATCH .../units/policy, POST .../units/allocateThe warm buffer and allocate
GET .../units/eventsEvents
.../units/webhooksWebhooks
.../principalService principals
GET .../units/logs, .../units/metricsLogs and metrics
every refusalErrors

Authentication

Send a bearer token on every request: Authorization: Bearer <token>. Three kinds of token can call these routes.

CallerTokenHow you get it
You, from a script or CIA personal API token, scoped to the serviceCI/CD guides show how to mint one with only the permissions you need
Code inside a unit of your WorkloadThe service principal token mounted at /zaroz/credentials/tokenService principals
A program outside Zaroz (a matchmaker, a scheduler)An external credential of the principal, zaroz_sp_...Service principals

A principal can only use the unit routes it was granted (units.read, units.control). It can never change the scaling policy, the webhooks or its own grants: those stay with people.

Conventions

  • Bodies are JSON and unknown fields are refused (400), so a typo is an error, not a silent no-op.
  • Keys are 1 to 40 characters, lowercase letters, digits and hyphens, starting and ending with a letter or digit. u7-style keys, keys starting w- or zaroz, and the words the routes use (allocate, events, policy) are reserved.
  • Labels: at most 16 pairs, replaced whole. user_data: any JSON up to 64 KiB, 32 levels deep. It is not a secret store: every reader of the unit sees it. Secrets go in the Workload's environment.
  • Rate limit for a service principal: 50 requests per second. A 429 carries Retry-After.
  • Errors always carry a stable code and a docs_url. See Errors.
  • Billing: a created, warm or allocated unit is an ordinary unit. It bills like any extra unit, per hour, with a 5-minute minimum per start. A stopped unit costs nothing. Events, webhooks and principals are free.

The unit routes only work in code mode

create, start, stop, destroy and allocate answer 409 policy-not-code unless the Workload's scaling policy has mode: "code", because in fixed and autoscaled modes the platform decides which units exist. Switch the mode on the warm buffer page. Reading, listing and PATCH labels work in every mode.

Try it end to end

The example repositories run every call in this guide against a real project.

Last modified on