This guide takes you from nothing to a running Workload using only the API. It runs nginx from Docker Hub, but any public image works the same way.
The Workload was previously called a Container; the guide file names keep the old word.
Before you start
You need an account with an organization, a wallet balance or another way to pay, and a token. The examples read two environment variables:
Code
export ZAROZ_API="https://kernel.zaroz.cloud/api/v2"export ZAROZ_TOKEN="<your session token or personal access token>"
Every request sends Authorization: Bearer $ZAROZ_TOKEN. The TypeScript examples need Node 20 or later (run with npx tsx file.ts); the Python examples need pip install requests. All of them use this small helper, shown once:
import { api } from "./api";const products = await api("GET", "/catalog/products?page_size=100");const product = products.data.find((p: any) => /workload/i.test(p.name));const regions = await api("GET", `/catalog/regions?product_id=${product.id}`);const region = regions.data.find((r: any) => r.available);const plans = await api("GET", `/catalog/products/${product.id}/plans?region_id=${region.id}`);const plan = plans.data.find((p: any) => p.available);
Code
products = api("GET", "/catalog/products", page_size=100)product = next(p for p in products["data"] if "workload" in p["name"].lower())regions = api("GET", "/catalog/regions", product_id=product["id"])region = next(r for r in regions["data"] if r["available"])plans = api("GET", f"/catalog/products/{product['id']}/plans", region_id=region["id"])plan = next(p for p in plans["data"] if p["available"])
plan.price is the monthly price; the extra-unit rate is that divided by 720 (see Billing).
2. Quote the purchase
A quote prices the purchase without buying anything. Send the checkout body without name and slug.
Checkout creates a pending subscription and the invoice that establishes it. name is yours to choose. slug is the URL segment that addresses the service, unique inside its project; it also becomes the Workload's private name.
Add organization_id to bill an organization rather than your personal one, and project_id to file it in a specific project. If you leave project_id out, the organization's default project is used.
4. Pay the invoice
From the wallet:
Code
curl -s -X POST "$ZAROZ_API/billing/invoices/$INVOICE_ID/pay-with-wallet" -H "Authorization: Bearer $ZAROZ_TOKEN"
To pay by card or PayPal instead, start the invoice checkout (/billing/invoices/{id}/checkout/mollie or .../paypal) and follow the returned link. Once paid, the subscription activates and provisioning starts on its own.
5. Find the service instance
The subscription is the purchase; the service instance is the thing that runs. Read the subscription for its project_id and its resources for the instance id. Wait until the status is active.
const sub = await api("GET", `/subscriptions/${subscriptionId}`); // wait for sub.status === "active"const projectId = sub.project_id;const res = await api("GET", `/subscriptions/${subscriptionId}/resources`);const serviceId = res.service_instance.id;
Code
sub = api("GET", f"/subscriptions/{subscription_id}") # wait for sub["status"] == "active"project_id = sub["project_id"]res = api("GET", f"/subscriptions/{subscription_id}/resources")service_id = res["service_instance"]["id"]
If the product asks setup questions, GET /service-instances/{id}/setup lists the unanswered steps, and answering the last one starts the first provisioning (PUT /service-instances/{id}/setup/steps/{step}).
6. Say which image to run
Saving the source does not start anything; it only records what the next deployment pulls. Use kind: registry. For a public image, send no credentials.
Zaroz qualifies a single-segment Docker Hub name with library/, so nginx works. Use a specific tag; the platform does not re-pull when a tag moves (watch_tag is always false).
7. Deploy
A deployment takes no body. It ships the source exactly as saved.
Code
curl -s -X POST "$ZAROZ_API/service-instances/$SERVICE_ID/deployments" -H "Authorization: Bearer $ZAROZ_TOKEN"
Code
const deployment = await api("POST", `/service-instances/${serviceId}/deployments`);// poll until the outcome is recordedfor (;;) { const d = await api("GET", `/service-instances/${serviceId}/deployments/${deployment.id}`); if (d.outcome) { console.log(d.outcome, d.failure_reason); break; } await new Promise((r) => setTimeout(r, 3000));}
Code
import timedeployment = api("POST", f"/service-instances/{service_id}/deployments")while True: d = api("GET", f"/service-instances/{service_id}/deployments/{deployment['id']}") if d["outcome"]: print(d["outcome"], d.get("failure_reason")); break time.sleep(3)
outcome is succeeded or failed; null means not recorded yet, never a failure. Your Workload now runs one unit.