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"
}

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.

Next