Instances
The developer clone lifecycle: launch, inspect, get credentials, reset, refresh, extend, and destroy. Every route on this page is bearer-scoped and, unless noted, owner-scoped — you only ever operate on your own instances (an admin actor is not special-cased here, except where noted).
See Getting started: developer for the linear walkthrough and Clone pools for how a launch can be fast-claimed instead of provisioned on demand.
The instance object
Every route below that returns an instance uses this shape:
{
"id": "0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d",
"owner_user": "3e6f0a2b-7c8d-4e1f-9a2b-3c4d5e6f7a8b",
"golden": "prod-pg16",
"golden_version": 3,
"namespace": "ns-3e6f0a2b",
"size": "medium",
"ttl": "168h",
"reset_generation": 0,
"desired_state": "present",
"phase": "Ready",
"endpoint": "0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d-rw.ns-3e6f0a2b.svc:5432",
"message": "",
"created_at": "2026-07-18T21:03:12Z",
"expires_at": "2026-07-25T21:03:12Z"
}
phasemirrors the agent's observed state:Pending,Provisioning,Ready,Failed, or similar — the same vocabularyadj getprints.messagecarries the failure reason whenphaseisFailed; empty otherwise.expires_atiscreated_at+ttl, approximating when the data-plane TTL reaper destroys the instance;nullifttldoesn't parse as a duration.reset_generationincrements on everyadj reset— a cheap way to tell whether an instance has been reset since you last looked.
POST /v1/instances — launch (clone)
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
golden |
string | yes | The golden lineage name. |
size |
string | no | "small", "medium", or "large". |
ttl |
string | yes | A positive Go duration string, e.g. "8h", "168h". |
version |
integer | no | Pin a specific version; 0 (or omit) clones the latest visible version. |
$ curl -s https://api.stagdb.com/v1/instances -X POST -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"golden":"prod-pg16","size":"medium","ttl":"168h"}'
Response 201 Created — an instance object (see above), typically with
phase: "Pending" — poll GET /v1/instances/{id} (or use adj get --wait)
until phase reaches Ready.
If a clone pool has a warm clone for this golden, the launch fast-claims it instead of triggering a fresh volume provision — same request, same response shape, just faster.
| Status | When |
|---|---|
| 400 | golden empty; size not one of the three valid values; ttl not a valid positive duration; or version/golden doesn't resolve to a visible ready version. Instance-launch 400s surface the specific reason after an invalid input: prefix — e.g. {"error": "invalid input: golden \"prod-pg16\" has no visible version 4"} or {"error": "invalid input: invalid ttl \"8x\" (must be a positive Go duration like 8h or 72h)"}. |
| 409 | The org has no registered cluster yet ("org has no registered cluster"). |
| 429 | The launch would exceed the per-user or per-org instance cap. The body is exactly {"error": "instance quota exceeded"} — it doesn't say which cap; check GET /v1/org/quota to tell. See Quota. |
GET /v1/instances — list your instances
$ curl -s https://api.stagdb.com/v1/instances -H "Authorization: Bearer $TOKEN"
Response 200 OK
{"instances": [ /* instance objects, as above */ ]}
Non-admins see only their own instances; admins see every instance in the org.
| Status | When |
|---|---|
| 401 | Missing/invalid/expired bearer token. |
GET /v1/instances/{id} — get one instance
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d -H "Authorization: Bearer $TOKEN"
Response 200 OK — a single instance object.
| Status | When |
|---|---|
| 400 | {id} not a valid UUID. |
| 403 | The instance belongs to another user (non-admin caller). |
| 404 | No such instance in this org. |
GET /v1/instances/{id}/credentials — get live credentials
Owner-only — even an admin JWT gets 403 reading another member's
credentials; this is the one route where admin doesn't override ownership.
Credentials are brokered live through the data-plane agent on every call
— nothing is cached or persisted in the control plane. The access itself is
audited (instance.credentials.read) as metadata only; the secret value
never appears in the audit trail.
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d/credentials \
-H "Authorization: Bearer $TOKEN"
Response 200 OK
{
"uri": "postgresql://app:s3cr3t-pw@0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d-rw.ns-3e6f0a2b.svc:5432/app",
"host": "0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d-rw.ns-3e6f0a2b.svc",
"port": 5432,
"dbname": "app",
"username": "app",
"password": "s3cr3t-pw"
}
| Status | When |
|---|---|
| 400 | {id} not a valid UUID. |
| 403 | Caller is not this instance's owner. |
| 404 | No such instance; or the instance exists but credentials aren't ready yet (adj clone returned before the instance reached Ready — check phase via GET /v1/instances/{id} first). |
| 503 | The data-plane agent for this instance's cluster is offline — nothing to broker through ("data-plane agent offline"). |
| 504 | The credential request was brokered to the agent but timed out waiting for a reply ("credential request timed out"). Usually transient — the underlying instance is likely fine; retry. |
DELETE /v1/instances/{id} — destroy
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d -X DELETE \
-H "Authorization: Bearer $TOKEN" -o /dev/null -w '%{http_code}\n'
202
Response: 202 Accepted, empty body — teardown proceeds asynchronously
on the data plane. Poll GET /v1/instances/{id} (it will 404 once fully
reaped) or watch adj get <id> --wait.
| Status | When |
|---|---|
| 400 | {id} not a valid UUID. |
| 403 | The instance belongs to another user (non-admin caller). |
| 404 | No such instance in this org. |
POST /v1/instances/{id}/reset — discard changes
Re-clones the instance from the same golden version it was already on — whatever the developer changed is discarded, but nothing about which version it tracks changes.
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d/reset -X POST \
-H "Authorization: Bearer $TOKEN"
Response 200 OK — the updated instance object; reset_generation is
incremented and phase typically returns to Pending/Provisioning while
the re-clone happens.
| Status | When |
|---|---|
| 400 | {id} not a valid UUID. |
| 403 | The instance belongs to another user (non-admin caller). |
| 404 | No such instance in this org. |
POST /v1/instances/{id}/refresh — move to the latest version
Re-clones the instance onto the latest version of its golden (discarding changes, same as reset, but also advancing the version pin).
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
golden |
string | no | Re-clone against a different golden lineage entirely instead of the instance's current one. Omit (or send an empty body) for the default: refresh onto the latest version of the same golden. |
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d/refresh -X POST \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{}'
Response 200 OK — the updated instance object, with golden_version
bumped to whatever was latest at request time.
| Status | When |
|---|---|
| 400 | {id} not a valid UUID; or the target golden has no visible ready version. |
| 403 | The instance belongs to another user (non-admin caller). |
| 404 | No such instance in this org. |
POST /v1/instances/{id}/extend — extend the TTL
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
ttl |
string | yes | A positive Go duration, e.g. "168h". Must be larger than the instance's current remaining TTL — this extends the expiry, it doesn't reset it to an arbitrary value. |
$ curl -s https://api.stagdb.com/v1/instances/0ab2b1ab-bd4a-4e1a-9ab8-c3b0251ee37d/extend -X POST \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"ttl":"336h"}'
Response 200 OK — the updated instance object with a later expires_at.
| Status | When |
|---|---|
| 400 | {id} not a valid UUID; body isn't {"ttl":"168h"}-shaped; ttl isn't a valid positive duration; or ttl doesn't actually extend past the current expiry. Like launch, the body carries the specific reason after an invalid input: prefix — e.g. {"error": "invalid input: ttl \"1h\" does not extend the current expiry (use a larger duration)"}. |
| 403 | The instance belongs to another user (non-admin caller). |
| 404 | No such instance in this org. |