Skip to content

HTTP API Reference

Stowry provides a REST API for object storage operations. All endpoints support presigned URL authentication.

http://localhost:5708

Requests require presigned URL authentication unless public_read or public_write is enabled. See Authentication for details.

Store mode only. Not available in static or SPA modes.

List objects with optional prefix filtering and pagination.

GET /

Query Parameters:

ParameterTypeDefaultDescription
prefixstring-Filter objects by path prefix
limitint100Maximum objects per page (1-1000)
cursorstring-Pagination cursor from previous response

Response: 200 OK

{
"items": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"path": "photos/vacation.jpg",
"content_type": "image/jpeg",
"etag": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e",
"file_size_bytes": 1048576,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}
],
"next_cursor": "MjAyNC0wMS0xNVQxMDozMDowMFp8cGhvdG9zL3ZhY2F0aW9uLmpwZw=="
}

Example:

Terminal window
# List all objects
curl "http://localhost:5708/"
# List with prefix
curl "http://localhost:5708/?prefix=photos/"
# Paginate
curl "http://localhost:5708/?limit=10&cursor=MjAyNC0wMS0..."

Download an object by path.

GET /{path}

Path Parameters:

ParameterDescription
pathObject path (e.g., photos/vacation.jpg)

Response Headers:

HeaderDescription
Content-TypeMIME type of the object
ETagObject hash (SHA256)
Content-LengthObject size in bytes
Last-ModifiedLast modification time

Response: 200 OK with file content

Errors:

StatusError CodeDescription
400invalid_pathInvalid path format
404not_foundObject not found

Example:

Terminal window
# Download object
curl -O http://localhost:5708/photos/vacation.jpg

Server Mode Behavior:

  • Store: Returns exact path or 404 (JSON)
  • Static: Tries exact → {path}.html{path}/index.html → 404 (HTML)
  • SPA: Falls back to /index.html for client-side routing

Retrieve object metadata without downloading the file body.

HEAD /{path}

Path Parameters:

ParameterDescription
pathObject path (e.g., photos/vacation.jpg)

Response Headers:

HeaderDescription
Content-TypeMIME type of the object
ETagObject hash (SHA256), quoted
Content-LengthObject size in bytes
Last-ModifiedLast modification time
Accept-RangesIndicates byte-range support

Conditional Headers:

HeaderDescription
If-None-MatchReturns 304 Not Modified if ETag matches (weak comparison per RFC 9110)
If-Modified-SinceReturns 304 Not Modified if object hasn’t changed since the given time

If-None-Match takes precedence over If-Modified-Since per RFC 7232.

Response: 200 OK (empty body)

Errors:

StatusError CodeDescription
304-Not modified (conditional request)
400invalid_pathInvalid path format
404not_foundObject not found

Example:

Terminal window
# Get metadata
curl -I http://localhost:5708/photos/vacation.jpg
# Conditional request (ETag)
curl -I -H 'If-None-Match: "a591a6d..."' http://localhost:5708/photos/vacation.jpg
# Conditional request (time)
curl -I -H "If-Modified-Since: Wed, 15 Jan 2024 10:30:00 GMT" http://localhost:5708/photos/vacation.jpg

Server Mode Behavior:

Same fallback logic as GET — applies static and SPA mode path resolution. Returns 404 HTML in static mode.


Store mode only. Returns 405 Method Not Allowed in static and SPA modes.

Upload or overwrite an object.

PUT /{path}

Path Parameters:

ParameterDescription
pathObject path (e.g., photos/vacation.jpg)

Request Headers:

HeaderRequiredDescription
Content-TypeNoMIME type (auto-detected if not provided)
If-MatchNoConditional update — ETag must match; returns 412 if object doesn’t exist (per RFC 9110 §13.1.1)

Request Body: Raw file content

Response: 200 OK

{
"id": "550e8400-e29b-41d4-a716-446655440000",
"path": "photos/vacation.jpg",
"content_type": "image/jpeg",
"etag": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e",
"file_size_bytes": 1048576,
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z"
}

Errors:

StatusError CodeDescription
400invalid_pathInvalid path format
412precondition_failedETag mismatch (If-Match)
500internal_errorServer error

Example:

Terminal window
# Upload file
curl -X PUT \
-H "Content-Type: image/jpeg" \
--data-binary @vacation.jpg \
http://localhost:5708/photos/vacation.jpg
# Conditional update
curl -X PUT \
-H "If-Match: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e" \
--data-binary @vacation.jpg \
http://localhost:5708/photos/vacation.jpg

Store mode only. Returns 405 Method Not Allowed in static and SPA modes.

Soft delete an object (marks for deletion but doesn’t remove immediately).

DELETE /{path}

Path Parameters:

ParameterDescription
pathObject path (e.g., photos/vacation.jpg)

Response: 204 No Content

Errors:

StatusError CodeDescription
400invalid_pathInvalid path format
404not_foundObject not found

Example:

Terminal window
curl -X DELETE http://localhost:5708/photos/vacation.jpg

Note: Deleted objects remain in storage until stowry cleanup is run.


All errors return JSON:

{
"error": "error_code",
"message": "Human-readable error message"
}

Valid paths must:

  • Be relative (no leading /)
  • Contain no path traversal (..)
  • Contain no empty segments (//)
  • Contain no invalid characters (\ ? # ~)
  • Be valid UTF-8
  • Have no trailing slashes

Valid paths:

  • file.txt
  • photos/vacation.jpg
  • data/2024/01/report.pdf

Invalid paths:

  • /file.txt (leading slash)
  • ../secret.txt (path traversal)
  • folder//file.txt (empty segment)
  • file?.txt (invalid character)

If Content-Type header is not provided on upload, Stowry detects it from the file extension:

ExtensionContent Type
.htmltext/html
.csstext/css
.jsapplication/javascript
.jsonapplication/json
.pngimage/png
.jpg, .jpegimage/jpeg
.gifimage/gif
.svgimage/svg+xml
.pdfapplication/pdf
(unknown)application/octet-stream

List responses use cursor-based pagination:

  1. Make initial request: GET /?limit=10
  2. If next_cursor is present in response, there are more results
  3. Request next page: GET /?limit=10&cursor={next_cursor}
  4. Repeat until next_cursor is empty or null

Benefits:

  • Consistent results during concurrent modifications
  • Efficient for large datasets
  • No offset skipping issues