HTTP API Reference
All product API endpoints are mounted under:
/api/v1Operational endpoints (/health, /ready, /metrics) are at the root (relative to your Keydock base URL — for example http://127.0.0.1:8080).
On a running Keydock instance:
- Swagger UI —
GET /swagger-ui/(interactive explorer) - OpenAPI JSON —
GET /api-docs/openapi.json
These paths are served by Keydock itself, not by this documentation site — open them against your deployment origin.
Authentication
Section titled “Authentication”Credentials can be sent in three ways, evaluated in this order:
Authorization: Bearer <credential>Authorization: Basic <base64(username:password)>— username is used as the credential- Query parameters:
?access_token=<credential>or?key=<credential>
Bucket credentials:
| Credential | Access |
|---|---|
secret_key | Admin — all operations including policy management and token minting |
read_key | Read and list keys |
write_key | Write keys |
| Temporary token | Scoped by prefix and explicit permissions |
Anonymous access depends on which credentials are configured. A bucket with no credentials is fully public. Configuring read_key restricts anonymous read/list. Configuring write_key restricts anonymous write. See the README for the full access matrix.
Operational Endpoints
Section titled “Operational Endpoints”Health
Section titled “Health”GET /healthLiveness probe. The handler does not probe storage — storage is always "ok" in this response shape (OpenAPI-compatible field only).
{"status":"ok","storage":"ok","version":"0.1.0-alpha"}Readiness
Section titled “Readiness”GET /readyChecks whether metadata storage is reachable. On success (200): same shape as /health. On failure (503):
{"status":"degraded","storage":"error","version":"0.1.0-alpha"}Metrics
Section titled “Metrics”GET /metricsPrometheus metrics in text exposition format.
OpenAPI
Section titled “OpenAPI”GET /api-docs/openapi.jsonGET /swagger-ui/Bucket API
Section titled “Bucket API”All bucket routes are under /api/v1.
Create Bucket
Section titled “Create Bucket”POST /api/v1/Content-Type: application/x-www-form-urlencodedForm fields:
| Field | Required | Description |
|---|---|---|
email | Yes | Admin label for the bucket |
secret_key | No | Admin credential |
read_key | No | Read/list credential |
write_key | No | Write credential |
signing_key | No | Used only to mint temporary tokens |
default_ttl | No | Default TTL in seconds (0 = no expiry; omitting applies the hosted default of 604800 s) |
Response: bucket ID as text/plain.
curl -X POST http://127.0.0.1:8080/api/v1/ \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'email=owner@example.com' \ --data-urlencode 'secret_key=admin-secret' \ --data-urlencode 'signing_key=token-signing-secret'Get Bucket Policy
Section titled “Get Bucket Policy”GET /api/v1/{bucket}Authorization: Bearer <secret_key>Returns a public projection of the bucket policy. Secrets and key hashes are never included.
{ "default_ttl": 604800, "has_secret_key": true, "has_read_key": false, "has_write_key": false, "has_signing_key": true, "signing_key_generation": 0, "anonymous_access": { "read": true, "write": true, "enumerate": true, "delete": false }}Check Bucket Existence
Section titled “Check Bucket Existence”HEAD /api/v1/{bucket}Authorization: Bearer <secret_key>Returns 200 if the bucket exists and the credential has admin access, 404 otherwise.
Update Bucket Policy
Section titled “Update Bucket Policy”PATCH /api/v1/{bucket}Authorization: Bearer <secret_key>Content-Type: application/jsonSupported fields: secret_key, read_key, write_key, signing_key, default_ttl. Unknown fields are rejected with 400.
Patch semantics:
- Field absent — leave unchanged
- Field
null— clear the value (not allowed forsecret_key) - Field string/number — set or rotate
curl -X PATCH "http://127.0.0.1:8080/api/v1/$BUCKET" \ -H 'Authorization: Bearer admin-secret' \ -H 'Content-Type: application/json' \ -d '{"read_key":"new-read-secret","default_ttl":3600}'Returns 204 No Content.
Delete Bucket
Section titled “Delete Bucket”DELETE /api/v1/{bucket}DELETE /api/v1/{bucket}/Authorization: Bearer <secret_key>Both forms are equivalent. Returns 204 No Content.
List Keys
Section titled “List Keys”GET /api/v1/{bucket}/Query parameters:
| Parameter | Default | Description |
|---|---|---|
prefix | — | Only return keys with this prefix |
limit | 10000 | Maximum number of results |
skip | 0 | Keys to skip after ordering |
reverse | false | Reverse lexicographic order |
values | false | Include stored values |
format | text | Response format: text, json, or jsonl |
Format can also be selected with the Accept header: application/json, application/x-ndjson, or text/plain.
curl "http://127.0.0.1:8080/api/v1/$BUCKET/?format=json&prefix=user:" \ -H 'Authorization: Bearer read-secret'["user:1","user:2","user:42"]With values (?values=true&format=json):
[["user:42",{"name":"Ana"}]]Key API
Section titled “Key API”All key routes are under /api/v1/{bucket}/{key}.
Keys are percent-encoded in the path. The server percent-decodes them before storage, so user%2F42 and user/42 refer to the same logical key. Clients should encode /, spaces, and other reserved characters.
Key limits: up to 128 bytes. Value limit: up to 16 KiB.
Write Key
Section titled “Write Key”PUT /api/v1/{bucket}/{key}POST /api/v1/{bucket}/{key} (primary method, PUT is an alias)Optional query parameter: ?ttl=<seconds> — overrides the bucket default TTL.
Value type is inferred from Content-Type and the body:
Content-Type: application/json→ stored as JSONContent-Type: text/plain→ stored as UTF-8 text- UTF-8 body that parses as a number → stored as
Int64orFloat64 - UTF-8 body that is valid JSON → stored as JSON
- Otherwise → raw bytes
curl -X PUT "http://127.0.0.1:8080/api/v1/$BUCKET/counter?ttl=3600" \ -H 'Authorization: Bearer write-secret' \ -d '0'The stored value is echoed in the response.
Read Key
Section titled “Read Key”GET /api/v1/{bucket}/{key}Returns the stored value with the appropriate Content-Type. Returns 404 if the key does not exist or has expired.
curl "http://127.0.0.1:8080/api/v1/$BUCKET/user:42" \ -H 'Authorization: Bearer read-secret'Check Key Existence
Section titled “Check Key Existence”HEAD /api/v1/{bucket}/{key}Returns 200 if the key exists and has not expired. Body is empty, Content-Type matches GET.
Delete Key
Section titled “Delete Key”DELETE /api/v1/{bucket}/{key}Returns 204 No Content. Returns 404 if the key does not exist.
Counter
Section titled “Counter”PATCH /api/v1/{bucket}/{key}Optional query parameter: ?ttl=<seconds>.
The request body must be a signed decimal delta: +N or -N where N is an integer or float.
curl -X PATCH "http://127.0.0.1:8080/api/v1/$BUCKET/page-views" \ -H 'Authorization: Bearer write-secret' \ -d '+1'Response body is the new counter value.
Type promotion rules:
Int64 + Int64→Int64- Any float operand →
Float64 - Applying a counter delta to a missing key creates the key with the delta as its initial value
NaN,Inf, or overflow →400
Temporary Tokens
Section titled “Temporary Tokens”Tokens allow scoping credentials to a key prefix and a set of permissions.
Create Token
Section titled “Create Token”POST /api/v1/{bucket}/tokens/Authorization: Bearer <secret_key>Content-Type: application/x-www-form-urlencodedThe bucket must have a signing_key configured. Returns 503 if it does not.
Form fields:
| Field | Required | Description |
|---|---|---|
prefix | Yes | Non-empty key prefix the token is restricted to |
permissions | Yes | Comma-separated: read, write, enumerate, delete |
ttl | Yes | Token lifetime in seconds (must be > 0) |
curl -X POST "http://127.0.0.1:8080/api/v1/$BUCKET/tokens/" \ -H 'Authorization: Bearer admin-secret' \ -H 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'prefix=user:42:' \ --data-urlencode 'permissions=read,write,enumerate' \ --data-urlencode 'ttl=900'Response:
{"access_token":"<jwt>"}Use the token as a Bearer credential:
curl "http://127.0.0.1:8080/api/v1/$BUCKET/user:42:profile" \ -H 'Authorization: Bearer <jwt>'Tokens are restricted to their configured prefix and permissions. A token scoped to user:42: cannot access admin:config. Rotating the bucket signing_key invalidates all previously issued tokens.
Transactions
Section titled “Transactions”Atomic multi-key operations.
POST /api/v1/{bucket}Authorization: Bearer <credential>Content-Type: application/jsonRequest body:
{ "txn": [ { "set": "profile:name", "value": "Ada", "ttl": 3600 }, { "delete": "profile:old-name" } ]}Each operation is one of:
{ "set": "<key>", "value": <json>, "ttl": <seconds> }—ttlis optional{ "delete": "<key>" }
Value rules for set:
- JSON strings are stored as UTF-8 text
- Numbers, booleans, arrays, and objects are stored as JSON
nullis rejected with400
curl -X POST "http://127.0.0.1:8080/api/v1/$BUCKET" \ -H 'Authorization: Bearer write-secret' \ -H 'Content-Type: application/json' \ -d '{"txn":[{"set":"k1","value":"hello"},{"delete":"k2"}]}'Returns 204 No Content on success. All operations either commit together or none do.
Error Responses
Section titled “Error Responses”All errors use a consistent JSON envelope:
{ "error": { "code": 404, "message": "not_found" }}Common messages:
| Message | HTTP status |
|---|---|
bad_request | 400 |
unauthorized | 401 |
forbidden | 403 |
not_found | 404 |
not_acceptable | 406 |
method_not_allowed | 405 |
service_unavailable | 503 |
internal_error | 500 |