Skip to content

Admin API ​

The JSON Admin API lives at /admin/api/v1/ on the admin listener (never the public S3 port). It is the control plane: tenants, service accounts, scoped access keys, quotas, usage, and audit.

It reuses the exact in-process domain services the HTML admin UI uses (tenant lifecycle, the metadata repo, the SigV4 secret cipher, the auditor), so it cannot bypass any authorization, audit, retention, or quota gate.

For most apps, reach this API through a first-party SDK rather than calling it directly: Go, Node, or Java. This page is the wire-level reference.

Authentication ​

Authentication is by an admin API bearer token: a high-entropy secret minted offline with lockwell admin-token create, stored only hashed (SHA-256), and distinct from S3 access keys. The wire form is prefixed lwadm_…. Send it as:

text
Authorization: Bearer lwadm_…
  • Token bootstrap is offline-only (lockwell admin-token create needs filesystem access, the same trust model as lockwell admin-create). There is no JSON route to mint the first token.
  • Bearer tokens are not sent automatically by browsers, so the API is for server-to-server use and carries no CSRF flow.
  • Anonymous, unauthenticated, revoked, and expired tokens are denied with 401.
  • Every request, success and denial, writes an audit row through the existing auditor. Tokens are rate-limited per token.

RBAC roles ​

Authorization composes the token's RBAC role with an optional single-tenant scope. A tenant-scoped token cannot cross to another tenant: a cross-tenant target is a 403, never a 404 existence leak.

RoleCan do
viewerread-only: list/get tenants, accounts, keys, quota, usage, audit
operatorthe above plus create tenants/accounts/keys, set/clear quota, rotate/revoke keys
ownerthe above plus the destructive tenant lifecycle: disable and delete

Routes ​

The server base is /admin/api/v1. GET /healthz and GET /openapi.json are unauthenticated; everything else requires a valid admin token.

Tenants ​

Method + pathRolePurpose
GET /tenantsviewerlist tenants (global token = all; scoped = its tenant)
POST /tenantsoperatorcreate a tenant
GET /tenants/{id}viewerget one tenant
POST /tenants/{id}/disableownerdisable a tenant; reason required
POST /tenants/{id}/deleteownerdelete a disabled tenant; reason + confirm required

disable revokes the tenant's active access keys. delete requires the tenant to be disabled first, requires confirm to equal the tenant id, and fails closed with a 412 when a retention window or legal hold gates the delete.

Accounts, keys, quota, usage ​

Method + pathRolePurpose
GET /tenants/{id}/accountsviewerlist service accounts
POST /tenants/{id}/accountsoperatorcreate a service account
GET /tenants/{id}/keysviewerlist access keys (secrets never returned)
POST /tenants/{id}/keysoperatorcreate an access key (secret returned once)
POST /tenants/{id}/keys/{keyId}/rotateoperatorrotate a key (new secret returned once)
POST /tenants/{id}/keys/{keyId}/revokeoperatorrevoke a key; reason required
GET|PUT|DELETE /tenants/{id}/quotaviewer / operatorget / set / clear the tenant quota
GET /tenants/{id}/usageviewerstorage usage report

The secret on a created or rotated key is shown exactly once and is never recoverable afterward; persist it immediately. listKeys returns only metadata.

The key request body accepts a scopes string (verb list read,write,delete,admin, or the resource form op=read:bucket=reports:prefix=in/,op=write:bucket=reports:prefix=in/) and an optional expiresAt (RFC3339 or YYYY-MM-DD).

A created or rotated key returns its secret exactly once. Capture it from the response immediately; there is

no endpoint that reads it back. :::

Audit ​

Method + pathRolePurpose
GET /audit?tenant=&since=&limit=viewerquery the audit log

since is a Go duration string (e.g. 24h); limit is clamped to [1, 1000] (default 100). A tenant-scoped token is forced to its own tenant regardless of the tenant parameter; an explicit cross-tenant tenant from a scoped token is a 403.

reason / confirm and dry runs ​

Destructive operations are gated on the wire:

  • reason is required on disable, delete, and revoke (a 400 otherwise).
  • confirm must equal the tenant id on delete (a 400 otherwise).
  • ?dryRun=true on any mutation returns the plan the call would execute and applies nothing. For tenant disable/delete the plan enumerates the buckets, objects, versions, delete markers, legal-held and retained versions, access keys, and physical bytes that would be affected, so you can preview an offboarding before committing it.
text
POST /admin/api/v1/tenants/acme/delete?dryRun=true
Authorization: Bearer lwadm_…
Content-Type: application/json

{ "reason": "offboarding", "confirm": "acme" }

The SDKs surface this directly: every mutation takes a dryRun option (Go/Node) or has a …DryRun twin (Java). See tenancy and auth.

Error shape ​

Failures return an RFC-7807-style JSON problem with a stable code, message, status, and requestId (also echoed in X-Request-Id), so an operator can correlate a failure with its audit row:

json
{
  "code": "precondition_failed",
  "message": "tenant has legal-held object versions",
  "status": 412,
  "requestId": "req_01HXY…"
}

Status mapping:

StatusMeaning
400validation (missing reason, bad confirm, malformed body)
401missing/invalid/revoked/expired admin token
403RBAC role or cross-tenant scope denial
404target not found
412retention / legal-hold gated tenant delete (fail-closed)
429per-token rate limit exceeded

OpenAPI ​

The full machine-readable contract is an OpenAPI 3 document, available two ways:

  • Served live at GET /admin/api/v1/openapi.json (unauthenticated, since an operator needs the contract before holding a token; it leaks no tenant data).
  • Committed at internal/adminapi/openapi.json (the canonical source; a human-readable YAML twin lives alongside at internal/adminapi/openapi.yaml).

Each operation carries a unique operationId (listTenants, createTenantKey, queryAuditLog, …), so you can generate a client in any language with openapi-generator. Prefer the first-party Go/Node/Java admin clients; codegen is the path for every other language.

Not exposed here ​

This JSON API mirrors the tenant / account / key / quota / audit subset of the admin surface. Operational controls that live on the HTML admin UI (encryption-key rotation and rewrap, lifecycle, repair/scrub, placement, backup/restore) are not part of the JSON Admin API.

There is also no public-access, bucket-policy, or notification configuration surface here. Notifications are configured on the native data plane.

Interactive reference ​

Every admin operation below is generated from the OpenAPI document, so it always matches the shipped server. Expand an operation for its parameters, request and response schemas, and copy-paste samples. Send the admin token as Authorization: Bearer <token>. The example host is a placeholder; swap in your own admin endpoint.

Versioned, authenticated, RBAC'd, audited JSON Admin API for Lockwell. Mounted under /admin/api/v1/ on the admin/web listener only (never the public S3 data-plane port). Authentication is by admin API bearer token; tokens are minted offline with lockwell admin-token create, stored only hashed, scoped by RBAC role (owner/operator/viewer) and optional single-tenant scope. Every request, success and denial, writes an audit row. Server-to-server use only; bearer tokens are not sent automatically by browsers, so there is no CSRF flow.

Servers​

https://admin.your-lockwell-host.com/admin/api/v1Your Lockwell deployment (replace with your own host)

system​

Liveness and contract endpoints.


Liveness probe for the API (unauthenticated).​

GET
/healthz

Responses​

API process is up.

Playground​

Samples​


This OpenAPI document (unauthenticated).​

GET
/openapi.json

Responses​

OpenAPI 3 document.

Playground​

Samples​


List tenants (global token = all; tenant-scoped = its tenant).​

GET
/tenants

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Responses​

Tenant list.

Playground​

Authorization

Samples​


Create a tenant (owner/operator).​

POST
/tenants

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Responses​

Created tenant.

Playground​

Authorization

Samples​


Get one tenant.​

GET
/tenants/{id}

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Tenant.

Playground​

Authorization
Variables
Key
Value

Samples​


Get a tenant's quota and usage.​

GET
/tenants/{id}/quota

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Quota.

Playground​

Authorization
Variables
Key
Value

Samples​


Set a tenant's quota (owner/operator).​

PUT
/tenants/{id}/quota

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Updated quota.

Playground​

Authorization
Variables
Key
Value

Samples​


Clear a tenant's quota (owner/operator).​

DELETE
/tenants/{id}/quota

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Cleared quota.

Playground​

Authorization
Variables
Key
Value

Samples​


List service accounts in a tenant.​

GET
/tenants/{id}/accounts

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Account list.

Playground​

Authorization
Variables
Key
Value

Samples​


Create a service account (owner/operator).​

POST
/tenants/{id}/accounts

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Created account.

Playground​

Authorization
Variables
Key
Value

Samples​


List access keys in a tenant (secrets never returned).​

GET
/tenants/{id}/keys

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Key list.

Playground​

Authorization
Variables
Key
Value

Samples​


Create an access key (owner/operator); secret returned once.​

POST
/tenants/{id}/keys

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Request Body​

application/json
JSON
{
  
"accountId": "string",
  
"externalRef": "string",
  
"scopes": "string",
  
"expiresAt": "string",
  
"reason": "string"
}

Responses​

Created key with one-time secret.

Playground​

Authorization
Variables
Key
Value
Body

Samples​


Rotate an access key (owner/operator); new secret returned once.​

POST
/tenants/{id}/keys/{keyId}/rotate

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required
keyId*
Type
string
Required

Request Body​

application/json
JSON
{
  
"scopes": "string",
  
"expiresAt": "string",
  
"reason": "string"
}

Responses​

Rotated key with one-time secret.

Playground​

Authorization
Variables
Key
Value
Body

Samples​


Revoke an access key (owner/operator); reason required.​

POST
/tenants/{id}/keys/{keyId}/revoke

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required
keyId*
Type
string
Required

Responses​

Revoked key.

Playground​

Authorization
Variables
Key
Value

Samples​


Tenant storage usage report.​

GET
/tenants/{id}/usage

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Usage report.

Playground​

Authorization
Variables
Key
Value

Samples​


Disable a tenant (owner only); reason required.​

POST
/tenants/{id}/disable

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Disable plan applied.

Playground​

Authorization
Variables
Key
Value

Samples​


Delete a disabled tenant (owner only); reason + confirm required; retention/legal-hold gated.​

POST
/tenants/{id}/delete

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Path Parameters

id*
Type
string
Required

Responses​

Delete plan applied.

Playground​

Authorization
Variables
Key
Value

Samples​


audit​

Audit log queries.

Operations​


Query the audit log (tenant-scoped token forced to its tenant).​

GET
/audit

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Parameters​

Query Parameters

tenant
Type
string
since

Go duration, e.g. 24h

Type
string
limit
Type
integer

Responses​

Audit events.

Playground​

Authorization
Variables
Key
Value

Samples​


Read the committed Raft membership configuration.​

GET
/cluster/consensus/membership

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Responses​

Committed member list.

Playground​

Authorization

Samples​


Join and commit a new Raft voter (owner only).​

POST
/cluster/consensus/join

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Request Body​

application/json
JSON
{
  
"id": "string",
  
"address": "string",
  
"listenAddr": "string",
  
"secret": "string",
  
"name": "string",
  
"softwareVersion": "string",
  
"zone": "string",
  
"site": "string",
  
"reason": "string",
  
"confirm": "string"
}

Responses​

Membership change committed.

Playground​

Authorization
Body

Samples​


Demote a Raft voter before shutdown, or explicitly mark an unreachable voter lost (owner only).​

POST
/cluster/consensus/drain

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Request Body​

application/json
JSON
{
  
"id": "string",
  
"reason": "string",
  
"confirm": "string",
  
"forceLost": true
}

Responses​

Drain demotion committed.

Playground​

Authorization
Body

Samples​


Remove a drained Raft member (owner only).​

POST
/cluster/consensus/remove

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Request Body​

application/json
JSON
{
  
"id": "string",
  
"reason": "string",
  
"confirm": "string",
  
"forceLost": true
}

Responses​

Member removal committed.

Playground​

Authorization
Body

Samples​


Add a replacement voter then remove the old member (owner only).​

POST
/cluster/consensus/replace

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Request Body​

application/json
JSON
{
  
"oldId": "string",
  
"id": "string",
  
"address": "string",
  
"listenAddr": "string",
  
"secret": "string",
  
"name": "string",
  
"softwareVersion": "string",
  
"zone": "string",
  
"site": "string",
  
"reason": "string",
  
"confirm": "string"
}

Responses​

Replacement sequence committed.

Playground​

Authorization
Body

Samples​


Execute checksum-verified replica rebalancing for a tenant or bucket (owner only).​

POST
/cluster/repair/rebalance

Authorizations​

adminToken

Admin API bearer token minted by lockwell admin-token create.

Type
HTTP (bearer)

Request Body​

application/json
JSON
{
  
"tenantId": "string",
  
"bucketName": "string",
  
"reason": "string",
  
"confirmTenant": "string",
  
"confirmBucket": "string"
}

Responses​

Rebalance job accepted and completed/queued by the cluster repair engine.

Playground​

Authorization
Body

Samples​


Powered by VitePress OpenAPI

Source-available under PolyForm Noncommercial 1.0.0; commercial use requires a written grant. License