Management API
The management API handles everything that is not object data: buckets, access keys, grants, usage, and audit. It is separate from the S3 endpoint and is never co-hosted with it.
- Base:
https://storey.scailabs.ai/api/v1 - JSON in, JSON out,
snake_casefields, RFC 3339 UTC timestamps. - Every response carries
X-Request-Id. Quote it when reporting a problem.
Authentication#
The console authenticates with your ScaiLabs account through a server-side session — no access token is ever stored in your browser. Requests from the console carry a session cookie plus a CSRF header.
Pagination#
Listings are cursor-based: pass ?limit= (default 50, max 200) and follow
next_cursor until it is null. There is no offset pagination — bucket and
object listings are unbounded, and offsets over an unbounded set produce
duplicates and gaps under concurrent writes.
Errors#
Every error uses one shape:
1 2 3 4 5 6 7 8 | |
The code is the contract — match on it, not on the message. Messages state
what to do next and may be reworded.
| Code | Status | Meaning |
|---|---|---|
Unauthenticated |
401 | No valid session |
Forbidden |
403 | Your role does not allow this |
NotFound |
404 | No such resource in your tenant |
NameConflict |
409 | That name is already used |
BucketNotEmpty |
409 | Delete the objects first |
PlacementUnavailable |
409 | No cluster serves that region and tier |
EntitlementExceeded |
409 | You are at a limit on your plan |
KeyClusterMismatch |
409 | The key belongs to a different region |
OperationInProgress |
409 | Still provisioning; retry shortly |
ValidationError |
422 | The request was malformed |
RateLimited |
429 | Slow down |
ClusterUnavailable |
503 | Temporary; retry |
A resource belonging to another tenant returns NotFound, never
Forbidden — a 403 would confirm the id exists somewhere.
Idempotency#
Mutating requests accept an Idempotency-Key header. Replaying a request with
the same key returns the original result instead of acting twice. Bucket and key
creation are long-running: the response carries an operation_id you can poll
at GET /v1/operations/{id}.
Endpoint groups#
| Group | Purpose |
|---|---|
/v1/regions, /tiers, /placements |
What you can order |
/v1/tenant, /tenant/entitlements, /tenant/usage |
Your account |
/v1/buckets |
Bucket lifecycle, stats, usage |
/v1/buckets/{id}/objects |
Listing, metadata, presigned URLs, delete, copy |
/v1/buckets/{id}/share |
Time-boxed public links |
/v1/keys, /v1/buckets/{id}/grants |
Access keys and their grants |
/v1/operations, /v1/audit, /v1/events |
Progress, history, live updates |
Objects: bytes never pass through this API#
Object listing and metadata come from the management API, but the bytes do not. Uploads and downloads use presigned URLs that your client sends directly to the S3 endpoint:
1 2 3 | |
That keeps a large transfer off the control plane and means a presigned request is metered exactly like any other S3 request — there is no cheaper path.
Presigned URLs are credentials. Their lifetime is capped by your role, every one is recorded in the audit log, and a share link can be revoked.