Admin API

The Admin API provides HTTP endpoints for managing users, credentials, and system information. All endpoints (except login) require a JWT token with SuperUser role.

Authentication

First, obtain a JWT token:

TOKEN=$(curl -s -k -X POST https://localhost:9000/api/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"root","password":"password12345"}' | jq -r '.token')

Use the token in subsequent requests:

curl -s -k https://localhost:9000/api/admin/users \
  -H "Authorization: Bearer $TOKEN"

Endpoints

Login

POST /api/admin/login

No authentication required.

curl -s -k -X POST https://localhost:9000/api/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"root","password":"password12345"}'

Response:

{
  "token": "eyJhbGciOiJIUzI1NiIs...",
  "expires_at": "2026-02-11T10:00:00Z"
}

A wrong password and an unknown account both answer 401 with code InvalidCredentials — identical responses, so the endpoint cannot be used to find out which account names exist.

List Users

GET /api/admin/users

curl -s -k https://localhost:9000/api/admin/users \
  -H "Authorization: Bearer $TOKEN"

Response: Array of user objects (password hashes and secret keys are never returned).

Create User

POST /api/admin/users

curl -s -k -X POST https://localhost:9000/api/admin/users \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"username":"alice","password":"alice123","role":"Writer"}'

Request body:

Field Type Required Description
username string Yes 3-32 characters, alphanumeric and underscore
password string Yes User password
role string Yes Reader, Writer, or SuperUser

Get User

GET /api/admin/users/{id}

curl -s -k https://localhost:9000/api/admin/users/<user-id> \
  -H "Authorization: Bearer $TOKEN"

Update User

PUT /api/admin/users/{id}

curl -s -k -X PUT https://localhost:9000/api/admin/users/<user-id> \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"role":"Reader"}'

Request body (all fields optional):

Field Type Description
password string New password
role string New role (Reader, Writer, SuperUser)
is_active boolean Enable or disable the account

Delete User

DELETE /api/admin/users/{id}

curl -s -k -X DELETE https://localhost:9000/api/admin/users/<user-id> \
  -H "Authorization: Bearer $TOKEN"

Response: 204 No Content

Generate S3 Credentials

POST /api/admin/users/{id}/credentials

curl -s -k -X POST https://localhost:9000/api/admin/users/<user-id>/credentials \
  -H "Authorization: Bearer $TOKEN"

Response:

{
  "access_key": "S4AKxxxxxxxxxxxxxxxxxxxx",
  "secret_key": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}

Important: The secret_key is shown only once. Store it securely — it cannot be retrieved again.

Delete S3 Credentials

DELETE /api/admin/users/{id}/credentials

curl -s -k -X DELETE https://localhost:9000/api/admin/users/<user-id>/credentials \
  -H "Authorization: Bearer $TOKEN"

Response: 204 No Content

Create Bucket

PUT /api/admin/buckets/{name}

curl -s -k -X PUT https://localhost:9000/api/admin/buckets/my-bucket \
  -H "Authorization: Bearer $TOKEN"

Response:

{ "name": "my-bucket" }

The name must satisfy the same S3 rules the S3 API enforces (3–63 characters, lowercase letters, digits, hyphens and dots, starting with a letter or digit and not ending with a hyphen) — otherwise 400 InvalidBucketName. A name already in use answers 409 BucketAlreadyExists.

Delete Bucket

DELETE /api/admin/buckets/{name}

Deletes a bucket. By default the bucket must be empty — a bucket that still holds objects answers 409 BucketNotEmpty, and an unknown bucket answers 404 NoSuchBucket.

curl -s -k -X DELETE https://localhost:9000/api/admin/buckets/my-bucket \
  -H "Authorization: Bearer $TOKEN"

Force delete — recursively deletes all objects in the bucket first:

curl -s -k -X DELETE "https://localhost:9000/api/admin/buckets/my-bucket?force=true" \
  -H "Authorization: Bearer $TOKEN"

Response: 204 No Content

Parameter Type Default Description
force bool false When true, all objects are deleted before removing the bucket

List Bucket Objects

GET /api/admin/buckets/{name}/objects

curl -s -k "https://localhost:9000/api/admin/buckets/my-bucket/objects?max-keys=100" \
  -H "Authorization: Bearer $TOKEN"

Query parameters:

Parameter Type Description
prefix string Filter objects by key prefix
max-keys integer Maximum number of keys to return
continuation-token string Pagination token from previous response

An unknown bucket answers 404 NoSuchBucket; an existing bucket with nothing in it answers 200 with an empty objects array.

Bucket Stats

GET /api/admin/bucket-stats

Returns storage statistics for all buckets.

curl -s -k https://localhost:9000/api/admin/bucket-stats \
  -H "Authorization: Bearer $TOKEN"

Error Responses

Every failure returns JSON with a human-readable error and a stable, machine-readable code. Branch on code, not on the message text:

{ "error": "Bucket 'my-bucket' is not empty. Delete its objects first, or repeat the request with ?force=true",
  "code": "BucketNotEmpty" }
Code Status Meaning
Unauthorized 401 Token missing, malformed or expired
InvalidCredentials 401 Login rejected: unknown account or wrong password
Forbidden 403 Authenticated, but the role is not SuperUser
UserNotFound 404 No user with this id or name
UserAlreadyExists 409 A user with this name already exists
InvalidUsername 400 Username breaks the format rules
NoS3Credentials 404 The user has no S3 credentials issued
NoSuchBucket 404 No bucket with this name
BucketAlreadyExists 409 A bucket with this name already exists
BucketNotEmpty 409 The bucket still holds objects and force was not set
InvalidBucketName 400 Bucket name breaks the S3 naming rules
InvalidData 400 Request body or parameters could not be interpreted
InternalError 500 Server-side fault (storage or IAM subsystem)

500 means a fault on the server, never a mistake in the request — anything the caller can correct itself is reported as 4xx.