API reference
The control-plane REST API that adj and the portal
talk to. Everything the CLI does goes through this surface — if you're
scripting against Adjoint directly (CI, an internal tool) instead of shelling
out to adj, this is the contract.
This is a metadata-only API. It never sees your database rows: source credentials go straight into your data-plane cluster as a Kubernetes Secret, and instance credentials are brokered live from the agent on each request (see Instances). The control plane stores orgs, users, the golden-snapshot catalog, cluster registrations, and an audit trail.
Base URL
https://api.stagdb.com
All paths below are relative to this base and are versioned under /v1.
The only other route is GET /healthz — a bare liveness probe
({"status":"ok"}, no auth) used by load balancers and uptime checks, not
part of the customer-facing surface documented here.
Auth model
Login is org-scoped: you authenticate with an org slug, an email, and a password — the same email can belong to different orgs with different passwords, so the slug disambiguates.
$ curl -s https://api.stagdb.com/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"org_slug":"acme","email":"[email protected]","password":"hunter22"}'
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","refresh_token":"3n8fK2pQZmR1sV6xY0bC4dE7gH9jL_wA5oT8uI2rP0k"}
POST /v1/auth/login returns two tokens in the same response:
token— a short-lived access JWT (HS256, default TTL 15 minutes, server-configurable viaADJ_JWT_TTL). Send it asAuthorization: Bearer <token>on every request below that isn't marked public. It carries the org id, role, and email as claims — nothing else is looked up per request.refresh_token— an opaque, cryptographically random, bare base64url string (no prefix, no structure — treat it as an opaque secret). Default TTL 720 hours (30 days), server-configurable viaADJ_REFRESH_TTL. Exchange it for a new token pair atPOST /v1/auth/refresh.
Refresh tokens rotate: every successful POST /v1/auth/refresh invalidates
the token you presented and returns a brand-new one. Presenting an
already-rotated (or already-revoked) token trips reuse detection — the
server treats it as evidence of token theft and revokes every refresh
token for that user, forcing a fresh login everywhere. This is the same
behavior adj logout --all triggers on purpose; reuse detection triggers it
automatically as a defensive measure.
The CLI's adj1.… invite codes (see Users) are a
client-side convenience that base64url-packs a server URL, org slug, and
raw invite token into one relayable string — the API itself only ever deals
in the bare token underneath.
Conventions
- Content type. Request and response bodies are
application/json. Endpoints with no meaningful response body (most deletes and some updates) return an empty body with a 2xx status — see each page's status table. - Errors. Every non-2xx response is
{"error": "<message>"}(occasionally with extra fields — e.g. golden-delete 409s add achildrenarray; see Snapshots). Theerrorstring is written for humans and safe to print directly; the CLI does exactly that, appending its ownhint:line for common cases (see the Troubleshooting guide). - IDs. All resource ids are UUIDs (v4), rendered as lowercase hyphenated
strings (
7a1e9c3d-5b2f-4c8a-9d0e-2f3a4b5c6d7e). - Timestamps. RFC3339, UTC (
2026-07-18T21:03:12Z), except audit eventts, which is RFC3339Nano (2026-07-18T22:10:04.812345Z) — full sub-second precision matters there because it doubles as a pagination cursor. See Org. - Admin gating. Routes marked admin require the caller's JWT to carry
role: admin; a non-admin actor gets403 {"error":"forbidden"}(or{"error":"admin required"}on a few routes — both mean the same thing). Routes marked bearer just need a valid, unexpired access token, any role. Routes marked public need noAuthorizationheader at all. - Ownership. A handful of developer routes (instance credentials) are
further scoped to the resource's owner — even an admin JWT gets
403reading another member's live credentials. Everything else that's bearer-scoped is org-scoped: any authenticated member of the org can read it, regardless of who created it. - 204 on success mutations. Actions with no useful response body (revoke,
logout, delete-without-cascade-body, etc.) return
204 No Content. A few return202 Acceptedinstead, specifically where the mutation starts an asynchronous data-plane operation (instance destroy, golden force-delete, pool create, import start, user remove) — the 202 body, where present, reflects the object's state at request time, not its final state.
Pages in this reference
| Page | Covers |
|---|---|
| Auth | Org registration, login, refresh, logout, invite acceptance |
| Client config | Published data-plane image refs + the CLI support policy |
| Account | Your own profile, password, sessions |
| Users | Invites, member listing, disable/enable/remove |
| Quota | Per-user / per-org instance caps |
| Snapshots | The golden-snapshot catalog: versions, sharing, delete |
| Instances | Clone lifecycle: launch, reset, refresh, extend, credentials |
| Clusters | Cluster registration, health, tokens, bootstrap bundle |
| Sources & imports | Source connections and the RDS import pipeline |
| Pools | Pre-warmed clone pools |
| Org | Org-wide usage rollup and the audit trail |
Every route below is documented straight from the handler code — request fields, response shapes, and the exact error statuses that route can actually produce (not a generic catalog of HTTP codes).