JSON API Reference
The Oyster JSON API is served under /api/v1/. All requests and responses
use JSON (except blob content, which is raw binary). Authenticated endpoints
require a Bearer token in the Authorization header.
Base URL
All API endpoints are prefixed with /api/v1:
$OYSTER_URL/api/v1/
Throughout this reference, $OYSTER_URL is set to your Oyster
server address (for example, http://localhost:3000).
Authentication
Most endpoints require a Bearer token:
Authorization: Bearer <your-api-key>
Endpoints that do not require authentication:
- Reading blobs by key or blob ID
- Health, readiness, and metrics probes
- OpenAPI documentation
Error Responses
All errors return a JSON body with a single error field:
{
"error": "human-readable error message"
}
Status Codes
| Code | Meaning |
|---|---|
200 | Success (GET, PATCH) |
201 | Created (POST, PUT) |
204 | No Content (DELETE) |
304 | Not Modified: If-None-Match matched on a GET request |
400 | Bad Request: invalid input or validation failure |
401 | Unauthorized: missing or invalid API key |
404 | Not Found: resource doesn't exist or not owned by your account |
409 | Conflict: resource already exists or limit reached |
412 | Precondition Failed: If-Match or If-None-Match condition not met |
413 | Payload Too Large: blob exceeds 1 GB |
500 | Internal Server Error |
501 | Not Implemented: endpoint exists but isn't functional yet |
503 | Service Unavailable: a dependent service is unreachable |
501 is currently produced only by three account stubs that are wired up but
not yet implemented: PUT /account/billing, GET /account/report, and
POST /account/transfer. These are intentionally omitted from the per-endpoint
reference until they are functional.
Cross-Cutting Error Contracts
A few error bodies are shared across multiple routes and carry a
structured block alongside the standard error string. Document
once here; per-route docs link back.
InsufficientBalance(402): the Pearl-derived wallet doesn't hold enough WAL or SUI to fund the onchain action. Body carries afunding_required: { wal_frost, sui_mist }block (both decimal strings). Currently fires onPUT /buckets/{bucket}/blobs/{key}(see Store Blob) andDELETE /buckets/{bucket}/blobs/{key}(see Delete Blob). When the lookup itself fails,funding_requiredisnull.CapExceeded(400): the upload would push the account past its per-accountmax_unencoded_bytescap. Body carries acap_exceededblock pointing at the admin endpoint that can raise the cap. Currently fires onPUT /buckets/{bucket}/blobs/{key}(see Store Blob). The cap is an upper bound by default; a per-accountavg_blob_sizeturns it into a lower bound on storable capacity for blobs of that size.
The admin-side onchain shrink endpoint
(PUT /accounts/{account_id}/max-storage)
has its own 400 variants (would_orphan, shrink_aborted)
documented in the admin reference.
Pagination
List endpoints use cursor-based pagination:
Query parameters:
cursor(optional): opaque string from a previous response'snext_cursorlimit(optional): number of items per page (default: 20, max: 100)
Response format:
{
"data": [ ... ],
"next_cursor": "opaque-cursor-string"
}
When next_cursor is null, there are no more results. To fetch the next
page, pass the next_cursor value as the cursor query parameter.
Example: paginating through buckets:
# First page
curl -s -H "Authorization: Bearer $API_KEY" \
"$OYSTER_URL/api/v1/buckets?limit=10" | jq
# Next page (using next_cursor from previous response)
curl -s -H "Authorization: Bearer $API_KEY" \
"$OYSTER_URL/api/v1/buckets?limit=10&cursor=eyJjcmVhdGVk..." | jq
Interactive Documentation
Oyster serves an interactive OpenAPI UI at:
$OYSTER_URL/api/docs
Explore and test all endpoints directly from your browser.