Zaroz
Zaroz Cloud v2 concepts

Subscriptions and billing

Quote, then buy

POST /checkout/quote prices a purchase and writes nothing. It uses the same resolution, placement, discount and tax rules as POST /checkout, so the total you quote is the total you are invoiced. A code is validated but not redeemed.

curl

TerminalCode
# same body as /checkout, minus name and slug: writes nothing curl -sS -X POST "$ZAROZ_API/checkout/quote" -H "$AUTH" -H "Content-Type: application/json" -d '{ "product_id": "<product uuid>", "plan_id": 3, "region_id": "<region uuid>", "organization_id": "<org uuid>", "discount_code": "WELCOME10" }' # then, to buy: the same body plus "name" and "slug" on POST /checkout

TypeScript

TypeScriptCode
const body = { product_id: PRODUCT, plan_id: 3, region_id: REGION, organization_id: ORG, discount_code: "WELCOME10" }; const quote = await call("POST", "/checkout/quote", body); console.log(quote.body.total_cents, quote.body.due_now_cents, quote.body.cadence_label); const bought = await call("POST", "/checkout", { ...body, name: "My API", slug: "my-api" }); console.log(bought.body.subscription.id, bought.body.invoice.id);

Python

Code
body = {"product_id": PRODUCT, "plan_id": 3, "region_id": REGION, "organization_id": ORG, "discount_code": "WELCOME10"} _, quote = call("POST", "/checkout/quote", body) print(quote["total_cents"], quote["due_now_cents"], quote["cadence_label"]) _, bought = call("POST", "/checkout", {**body, "name": "My API", "slug": "my-api"}) print(bought["subscription"]["id"], bought["invoice"]["id"])

The quote answers lines, subtotal_cents, tax, total_cents (per billing cycle), due_now_cents (what you pay today, which is not always the total: a setup fee, a prepaid term or a prorated first period change it), cadence_label and the resources that were priced. If you name public ports, metered_estimate shows what holding them can cost.

POST /checkout creates a pending subscription and the establishing invoice. Pay the invoice by the existing invoice payment routes; when it is paid the service is provisioned. You need Owner or Admin on the organization named by organization_id; without it the purchase bills your personal organization.

Renewal and cancellation

A subscription renews at the end of each period. Two different things you can ask for:

  • PUT /subscriptions/{id}/renewal {auto_renew}: stop or resume the next invoice. The service keeps running.
  • PUT /subscriptions/{id}/cancellation: schedule the service itself to end.

curl

TerminalCode
curl -sS -X PUT "$ZAROZ_API/subscriptions/<subscription uuid>/renewal" -H "$AUTH" \ -H "Content-Type: application/json" -d '{"auto_renew": false}' # stops the NEXT invoice, not the service

TypeScript

TypeScriptCode
await call("PUT", `/subscriptions/${SUB}/renewal`, { auto_renew: false }); // not a cancellation

Python

Code
call("PUT", f"/subscriptions/{SUB}/renewal", {"auto_renew": False}) # not a cancellation

Resizing is a quote and a confirm: POST /subscriptions/{id}/resize/quote, then POST /subscriptions/{id}/resize. A plan change is the same shape under /resize/plan-change.

How Units are billed

A Workload's plan includes a number of units (included_units). Units above that are extra units.

  • An extra unit is billed per hour while it has a pod, plus a minimum charge per start of 5 minutes. Churn is never free.
  • Which units are "included" is decided when a pod is created, by its slot number. A stopped code-controlled unit releases its slot, so the included slots are always the first ones that run.
  • A stopped unit costs nothing. A warm unit is billed like any running unit, because it holds CPU and memory.
  • Public ports are billed by the hour they are held.
  • Events, webhooks and service principals are included at no charge.

You control the worst case with limits: the policy's max_units, your organization's unit limit (default 50), the start rate (default 600 per hour) and a monthly spend_cap_cents for extra units. POST .../units/policy/quote shows the worst case of a policy before you save it. When a cap or limit is reached, calls that would start a pod are refused with a stable code (see Units API errors); running units are never stopped by a limit.

A worked example:

SetupCalculationResult
1 included unit, 3 warm units kept all month3 extra units × 720 h × the extra hourly ratethe maximum the warm buffer can cost in a 30-day month
a job that runs 40 s, 100 jobs a day100 starts × the 5-minute minimumbilled as 500 minutes of unit time, not 67
a plot stopped between visits0 while stoppedpay only while players are in

The hourly rate is the plan's price; read it from the quote.

Last modified on