Org

Org-wide visibility and lifecycle: a single usage rollup, the tamper-evident audit trail, a tiny org-identity lookup, and permanent org deletion. See Usage and audit for the CLI-rendered table views of the usage/audit responses, and Uninstall for the full adj org delete walkthrough.

GET /v1/org — get the caller's own org

Auth: bearer (any authenticated member — no admin gate).

$ curl -s https://api.stagdb.com/v1/org -H "Authorization: Bearer $TOKEN"

Response 200 OK

{"id": "8f1e2d3c-4b5a-4978-8c9d-0e1f2a3b4c5d", "name": "Acme Corp", "slug": "acme"}

name is the freeform display name set at signup — an independent string from slug, not derived from it. This route exists chiefly so a caller that only holds the slug (e.g. the CLI's stored auth context) can look up the org's actual name — the value DELETE /v1/org's confirm_name is checked against.

Status When
401 Missing/invalid/expired bearer token.

GET /v1/org/usage — usage rollup

Auth: admin.

$ curl -s https://api.stagdb.com/v1/org/usage -H "Authorization: Bearer $TOKEN"

Response 200 OK

{
  "quota": {"max_per_user": 3, "max_per_org": 30, "active_in_org": 14},
  "users": [
    {"email": "[email protected]", "display_name": "Alex Park", "role": "admin", "status": "active", "active": 1, "total": 4},
    {"email": "[email protected]", "display_name": "Dana Lin", "role": "user", "status": "active", "active": 2, "total": 9}
  ],
  "goldens": [
    {"name": "prod-pg16", "latest_version": 3, "shared": true, "active": 11, "total": 40}
  ],
  "clusters": [
    {"name": "prod", "health": "connected", "last_seen": "2026-07-19T08:12:41Z", "active_instances": 14}
  ],
  "pools": [
    {"golden": "prod-pg16", "warm": 5, "target": 5}
  ],
  "imports": {
    "total": 3,
    "succeeded": 3,
    "failed": 0,
    "last_finished_at": "2026-07-18T22:10:04Z"
  }
}

One call, six sections — quota, users, goldens, clusters, pools, imports. This is the same data available piecemeal from GET /v1/org/quota, GET /v1/users, GET /v1/snapshots, GET /v1/clusters, GET /v1/pools, and GET /v1/imports, joined into one document — useful for a dashboard or a single scripted health check instead of five separate calls. pools is [] (empty array) if the org has none.

Status When
403 Caller is not an admin.

GET /v1/org/audit — audit trail

Auth: admin.

Query parameters

Param Type Notes
limit integer Page size. Clamped server-side to (0, 500]; an out-of-range or omitted value falls back to 100. Must parse as an integer if present (?limit=abc is a 400, not a silent fallback).
before RFC3339 timestamp Pagination cursor — restricts the page to events strictly older than this timestamp. Compares at full precision, so pass back the exact ts of the last event on a page (RFC3339Nano) to page further without skipping same-second events.
verify 1 When set to exactly "1", adds a chain-verification pass to the response (see below). Any other value is ignored (verification is skipped, not errored).
$ curl -s "https://api.stagdb.com/v1/org/audit?limit=3" -H "Authorization: Bearer $TOKEN"

Response 200 OK

{
  "events": [
    {
      "seq": 102,
      "actor": "[email protected]",
      "action": "import.start",
      "resource": "5b9d7c31-2e4f-4a6b-8c0d-1e2f3a4b5c6d",
      "ts": "2026-07-18T22:10:04.812345Z",
      "payload": {}
    },
    {
      "seq": 101,
      "actor": "[email protected]",
      "action": "instance.launch",
      "resource": "7a1e9c3d-5b2f-4c8a-9d0e-2f3a4b5c6d7e",
      "ts": "2026-07-18T21:02:11.204819Z",
      "payload": {}
    },
    {
      "seq": 100,
      "actor": "[email protected]",
      "action": "invite.create",
      "resource": "1f2e3d4c-5b6a-4978-8c9d-0e1f2a3b4c5d",
      "ts": "2026-07-18T20:58:40.573062Z",
      "payload": {}
    }
  ]
}

Every state-changing action in the org is recorded: logins, invites, instance launches/destroys, snapshot registration/share/delete, quota changes, cluster registration, and more — as a sequenced (seq, strictly increasing), hash-chained event. action is a dotted verb (instance.launch, snapshot.share, invite.create, cluster.register, …); resource is the affected object's UUID as a string. ts is RFC3339Nano, not plain RFC3339 like every other timestamp in this API — full sub-second precision is load-bearing for the before cursor (see above). payload is {} when the event carries no extra structured detail.

With ?verify=1 — tamper-evident chain check

$ curl -s "https://api.stagdb.com/v1/org/audit?verify=1" -H "Authorization: Bearer $TOKEN"
{
  "events": [ /* ... */ ],
  "chain_ok": true,
  "events_checked": 102
}

Each event is hash-chained to the one before it. verify=1 re-derives the chain from the stored events and reports whether it's intact — chain_ok: true/false plus events_checked. If verification fails, a verify_error field is added with the specific reason:

{"events": [ /* ... */ ], "chain_ok": false, "events_checked": 102, "verify_error": "hash mismatch at seq 57"}

Note that chain_ok: false does not change the HTTP status — this response is still 200 OK even when the chain is broken, since the read succeeded; the caller (the CLI) is what turns chain_ok: false into a non-zero exit.

Exporting

There's no separate export endpoint — page through with limit/before and concatenate. This is exactly what adj org audit --limit 500 -o json > audit-export.json does under the hood.

Status When
400 limit present but not a valid integer ("limit must be an integer"); or before present but not a valid RFC3339 timestamp ("before must be an RFC3339 timestamp").
403 Caller is not an admin.

DELETE /v1/org — permanently delete the org

Auth: admin.

Request body

Field Type Required Notes
confirm_name string yes Must exactly match the org's name (from GET /v1/org) — not the slug.
password string yes The acting admin's own current password — a fresh, inline re-auth for this endpoint.
$ curl -s https://api.stagdb.com/v1/org -X DELETE -H "Authorization: Bearer $TOKEN" \
    -H 'Content-Type: application/json' \
    -d '{"confirm_name":"Acme Corp","password":"correct horse battery staple"}'

Response 200 OK

{
  "audit_export_url": "/v1/org/export/Uz9x1qk...base64url-token",
  "chain_head": "3f9a1c7e2b8d4f0a6c5e9b2d1a8f7c3e0b6d4a9f2c1e8b7d3a0f9c6e2b1d8a4f",
  "expires_in_seconds": 900
}

Deletes, in one transaction: every user, invite, session, catalog entry, source, import record, clone pool, quota row, org policy row, and the full audit log — replaced by a single anonymized tombstone (SHA-256 hashes of the org name and the deleting user's id; no names or emails). The password is checked before confirm_name, so a wrong password is always the reported failure — it never reveals whether the typed name was right. audit_export_url is a path (not absolute); fetch it against the same BASE_URL. Every token issued for this org is invalidated as part of the same operation — any subsequent request with an old token gets 401, not a second delete.

Status When
403 Caller is not an admin, or password doesn't match the acting admin's own credential.
400 confirm_name doesn't exactly match the org's name.
409 The org still has one or more registered clusters. Body: {"clusters": [{"id": "...", "name": "..."}, ...], "hint": "deprovision then cluster rm each — see /guides/uninstall/"}. There is no --force equivalent — deregister every cluster first.

GET /v1/org/export/{token} — fetch the final audit export

Auth: none — the org's session is already dead by the time this is fetched.

$ curl -s https://api.stagdb.com/v1/org/export/Uz9x1qk...base64url-token

Response 200 OKContent-Type: application/x-ndjson. One JSON line per audit event (in seq order: id, seq, actor, action, resource, ts, payload, prev_hash, hash), followed by a final signature line:

{"chain_head": "3f9a1c7e2b8d4f0a6c5e9b2d1a8f7c3e0b6d4a9f2c1e8b7d3a0f9c6e2b1d8a4f", "sig": "b7e2...a1f9"}

sig is an HMAC-SHA256 over every byte written before this line, keyed with a server-held secret — it lets Adjoint attest that the export is complete and unaltered, but it is not customer-verifiable: you cannot recompute it yourself without the server's key. Treat chain_head as the durable reference to cross-check against what was printed at delete time.

The token is single-use (consumed on the first successful GET) and expires 15 minutes after DELETE /v1/org returned it — whichever comes first.

Status When
404 Token unknown, already retrieved, or expired.

Next