
API Reference
This document covers the JSON APIs provided by the orchestrator for programmatic access. For the S3-compatible API, see the S3 API Coverage section of the README.
Authentication
UI API
UI API endpoints use session cookie authentication. Obtain a session by posting credentials to the login endpoint:
Sessions are HMAC-SHA256 signed cookies with a 24-hour TTL.
Admin API
Admin API endpoints use token authentication via the X-Admin-Token header:
The token is the ui.admin_token value from the configuration file. If admin_token is not set, it falls back to ui.admin_key. The CLI subcommand (s3-orchestrator admin) handles this automatically.
All JSON request bodies on admin and UI endpoints are limited to 1 MB.
Object data caching: When the optional in-memory cache is enabled, GET responses for eligible objects may be served from cache rather than from a backend. This is fully transparent to S3 API clients — cached responses have the same headers, status codes, and body content as uncached responses. No client-side configuration or awareness is needed.
UI API Endpoints
All UI API endpoints are mounted under the configured UI path (default: /ui). They require an authenticated session cookie.
GET /ui/api/dashboard
Returns the full dashboard data snapshot.
Response:
GET /ui/api/tree
Returns children of a directory prefix for the lazy-loaded file browser.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
prefix | No | Directory prefix to list (e.g., my-bucket/photos/). Empty returns top-level entries. |
startAfter | No | Cursor for pagination (value of nextCursor from previous response) |
maxKeys | No | Maximum entries to return (1-200, default: 200) |
Response:
GET /ui/api/logs
Returns buffered log entries from the in-memory ring buffer (last 5,000 entries).
Query parameters:
| Parameter | Required | Description |
|---|---|---|
level | No | Minimum severity: DEBUG, INFO, WARN, ERROR (default: all levels) |
since | No | RFC3339 timestamp — only return entries after this time |
component | No | Filter by component attribute value |
limit | No | Maximum entries to return (default: all). When applied, returns the most recent N matching entries. |
Response:
POST /ui/api/delete
Deletes a single object by key.
Request body:
Response (success):
Response (error):
POST /ui/api/delete-prefix
Deletes all objects under a given key prefix.
Request body:
Response (success):
Response (partial failure):
POST /ui/api/upload
Uploads a file via multipart form data. Maximum upload size is 512 MiB.
Request:
The key must start with a configured virtual bucket name (e.g., my-bucket/).
Response (success):
GET /ui/api/download
Downloads a single object by key. The response streams the object body with appropriate headers for the browser to trigger a save dialog.
Request:
The key must start with a configured virtual bucket name.
Response: Binary object content with Content-Disposition: attachment, Content-Type, and Content-Length headers.
POST /ui/api/rebalance
Triggers an on-demand rebalance in the background. Returns immediately with 202 Accepted. Poll the status endpoint for results.
Request: No body required.
Response (202):
Response (409): Returned if a rebalance is already running.
GET /ui/api/rebalance/status
Returns the status of the most recent rebalance operation.
Response:
POST /ui/api/clean-excess
Removes over-replicated copies in the background. Returns immediately with 202 Accepted. Poll the status endpoint for results.
Request: No body required.
Response (202):
Response (409): Returned if cleanup is already running.
GET /ui/api/clean-excess/status
Returns the status of the most recent cleanup operation.
Response:
POST /ui/api/sync
Imports pre-existing objects from a backend’s S3 bucket into the database.
Request body:
Both backend (a configured backend name) and bucket (a configured virtual bucket name) are required.
Response (success):
Admin API Endpoints
All admin API endpoints are mounted under /admin/api/. They require the X-Admin-Token header.
GET /admin/api/status
Returns backend health, quota usage, object counts, and monthly usage stats.
Response:
GET /admin/api/workers
Returns a snapshot of every registered background service’s last-tick
health. Use this during incidents to distinguish “worker is running
but every tick fails” from “worker has not run”. Returns 503 when
the deployment runs in proxy-only mode and no worker pool is wired.
Response:
Fields:
name— registration name, matches the snake_case slug on the scoped logger (e.g.cleanup_queue,replicator,over_replication_cleanup).last_success— RFC 3339 timestamp of the most recent successful tick. Omitted before the first success.last_failure— most recent failed tick. Omitted before the first failure. Stays set after recovery so operators can see how long ago the service was last failing.last_error— error string from the most recent failure. Cleared on the next success.consecutive_failures— count of back-to-back failed ticks since the last success. Resets to 0 on success.
The same data is exposed via Prometheus as
s3o_worker_last_success_timestamp_seconds,
s3o_worker_consecutive_failures, and s3o_worker_ticks_total so
alerting can run without scraping this endpoint.
GET /admin/api/object-locations
Returns all copies of an object across backends.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
key | Yes | Full object key including bucket prefix (e.g., my-bucket/path/to/file.txt) |
Response:
GET /admin/api/cleanup-queue
Returns the cleanup queue depth and pending items (up to 50).
Response:
POST /admin/api/usage-flush
Forces an immediate flush of usage counters to the database. Flushes from Redis when active, otherwise from local in-memory counters.
Request: No body required.
Response:
POST /admin/api/replicate
Triggers one replication cycle. Returns immediately if replication is not configured or factor is 1.
Request: No body required.
Response:
Or if replication is not configured:
GET /admin/api/over-replication
Returns the current replication factor and count of over-replicated objects.
Response:
POST /admin/api/over-replication
Triggers an immediate over-replication cleanup pass.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
batch_size | No | Override the configured batch size for this run |
Request: No body required.
Response:
Or if replication is not configured:
GET /admin/api/log-level
Returns the current runtime log level.
Response:
PUT /admin/api/log-level
Changes the runtime log level without restart or SIGHUP.
Request body:
Valid levels: debug, info, warn, error.
Response:
POST /admin/api/backends/{name}/drain
Starts draining a backend. All objects are migrated to other backends in the background. The backend is immediately excluded from new writes.
Response (202 Accepted):
Error responses:
GET /admin/api/backends/{name}/drain
Returns the current state of a drain operation.
Response (active drain):
Response (no drain active):
Response (completed with error):
DELETE /admin/api/backends/{name}/drain
Cancels an active drain. Objects already moved are not rolled back.
Response:
Error response:
DELETE /admin/api/backends/{name}
Removes all database records for a backend. This is destructive.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
purge | No | Set to true to also delete objects from the backend’s S3 storage |
Response:
Error responses:
POST /admin/api/encrypt-existing
Encrypts all unencrypted objects in-place. Requires encryption to be enabled in the config. Each object is downloaded, encrypted, and re-uploaded to the same backend, counting as 2 API calls plus egress and ingress against the backend’s usage quota.
Response:
POST /admin/api/decrypt-existing
Decrypts all encrypted objects back to plaintext. Requires encryption to be enabled in the config (the key provider is needed to unwrap DEKs). Each object is downloaded, decrypted, and re-uploaded as plaintext, counting as 2 API calls plus egress and ingress against the backend’s usage quota.
Response:
POST /admin/api/rotate-encryption-key
Re-wraps all DEKs encrypted with a specific key ID using the current master key. This is a metadata-only operation — no object data is re-uploaded.
Request body:
Response:
POST /admin/api/scrub
Triggers an on-demand integrity scrub cycle. Verifies stored content hashes against actual object data on backends.
Request body (optional):
Response:
POST /admin/api/hash-existing
Computes and stores content hashes for all objects that don’t have one yet.
Response:
Error Responses
All endpoints return errors as JSON:
Common HTTP status codes:
| Code | Meaning |
|---|---|
| 400 | Bad request (missing required parameters, invalid JSON) |
| 401 | Unauthorized (missing or invalid token/session) |
| 405 | Method not allowed |
| 500 | Internal server error |