Account developer guide
What this is
Section titled “What this is”How to call the account API from code. Concepts are in the user guide; every endpoint and field is in the API reference.
This API is not the compute API. It does not use SigV4 and it does not use access keys.
Base URL
Section titled “Base URL”https://storefront.job-rss-processor.workers.dev[!primary]
No custom domain is attached yet, so this hostname will change. Read it from configuration rather than hard-coding it in a client you ship.
Three levels of access
Section titled “Three levels of access”| Level | What it needs |
|---|---|
| Public | Nothing. /v1/catalog, /v1/prices, /v1/prices/comparison |
| Session | A Bearer JWT |
| Session + onboarded | A Bearer JWT and a completed onboarding |
A session call before onboarding does not fail with an auth error — it returns a
400 telling you to become a customer first. Handle that as a distinct state,
not as a broken token.
Sessions
Section titled “Sessions”Identity is a hosted auth service, and this API and the site are on different hosts — so the browser’s session cookie is never sent here and there is nothing to forward. Instead the site asks the auth service for a short-lived JWT and sends it on every call:
Authorization: Bearer <jwt>The token is verified against the issuer’s JWKS on every request. Three claims are read: the user id, the email, and whether the email is verified.
Expiry is 15 minutes by default. There is no refresh endpoint on this API —
ask the auth service for a new token. Build the refresh into your client rather
than treating a 401 as a dead end.
async function call(path, init = {}) { const jwt = await getFreshToken(); // yours; refresh when near expiry const res = await fetch(BASE + path, { ...init, headers: { ...init.headers, Authorization: `Bearer ${jwt}` }, }); if (!res.ok) throw await res.json(); return res.status === 204 ? null : res.json();}Onboarding, idempotently
Section titled “Onboarding, idempotently”await call("/v1/onboard", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ password: cloudPassword }),});Every step is find-before-create and every grant is re-applied until onboarding completes, so calling it again is the recovery path — not an error, and not something to guard against. If a call times out, call it again.
Two things to get right:
- The body carries the cloud password. It is used only on first creation; a later call with a different password does nothing. It is also stored in recoverable form so the console can be opened for the customer — see Credentials and trust.
- Check completion with
GET /v1/merather than assuming aPOSTsucceeded. Once onboarded it also returns your cloud auth URL and username.
Minting an access key
Section titled “Minting an access key”const key = await call("/v1/access-keys", { method: "POST" }); // 201// { access, secret }await call("/v1/access-keys"); // listawait call(`/v1/access-keys/${encodeURIComponent(access)}`, { method: "DELETE" }); // 204Rotate in this order: mint, roll out, confirm the old key is idle, delete. Deleting is immediate with no grace period.
See Access keys for the security property that differs from AWS.
Reading usage
Section titled “Reading usage”const usage = await call("/v1/usage");Machines running and the month-to-date cost, in one call. This is the only cost-before-the-invoice surface. The cost half lags by about two hours — an hour is rated roughly two hours after it ends — while the machines half is live. Field by field: Usage and cost.
Consoles
Section titled “Consoles”await call("/v1/console/session", { method: "POST" }); // web consoleawait call(`/v1/machines/${id}/console`, { method: "POST" }); // one machineThe machine console returns a single-use, time-limited URL and refuses a machine that is not yours. Do not cache it, and do not paste one into a ticket.
Caching
Section titled “Caching”The three public endpoints are cached for 60 seconds at the edge, keyed on the URL. Every other route is per-request.
That has one consequence worth coding around: inStock in the catalog can be up
to a minute stale. It is a snapshot, not a reservation — another customer can
take the capacity, and a launch can still fail with
InsufficientInstanceCapacity. Treat the launch as the authoritative answer.
Browsers and CORS
Section titled “Browsers and CORS”Exactly one origin is allowed with credentials — this site’s own. A page on another origin cannot call the session routes from a browser, by design.
Server-side callers are unaffected.
Errors
Section titled “Errors”Errors come back as JSON problem documents, not the EC2 XML shape the compute API uses. Do not share one error handler between the two.
| Status | Means | Do |
|---|---|---|
400 | Not onboarded, or a malformed body, or a card not matching its checkout session | Read the message; it names the missing precondition |
401 | Missing, expired or unverifiable token | Refresh and retry once |
403 | Email not verified | Verify, then retry |
404 | Not yours, or no such thing | Treat as not-found |
502/503 | An upstream did not answer — “the cloud did not answer about access keys”, “prices are momentarily unavailable” | Retry with backoff. Nothing has been half-done: writes here are find-before-create |