Storage API

Status: Implemented (Milestone 8)

Base path: /api/storage

POST /api/storage/upload

Upload a file. Requires authentication.

Content-Type: multipart/form-data

FieldTypeRequiredDescription
filefileyesFile bytes
visibilitystringnopublic or protected (default: protected)

Response 201:

{
  "id": "fil_550e8400-e29b-41d4-a716-446655440000",
  "filename": "photo.png",
  "mimeType": "image/png",
  "size": 12345,
  "visibility": "protected",
  "userId": "usr_...",
  "createdAt": "2026-01-01T00:00:00.000Z"
}

Errors:

StatusCodeWhen
400bad_requestMissing file, empty file, or file exceeds 10 MB
401unauthorizedNo valid Bearer token

GET /api/storage/:id

Download a file by ID.

Auth: optional for public files; required for protected files (owner or admin).

Response 200: file bytes with Content-Type and Content-Disposition headers.

Errors:

StatusCodeWhen
401unauthorizedProtected file, no token
403forbiddenProtected file, not owner/admin
404not_foundUnknown file ID

DELETE /api/storage/:id

Delete a file. Requires authentication (owner or admin).

Response 204: no body.

Errors:

StatusCodeWhen
401unauthorizedNo valid Bearer token
403forbiddenNot owner or admin
404not_foundUnknown file ID

Example

# Register and get token
TOKEN=$(curl -s -X POST http://localhost:8080/api/auth/register \
  -H 'Content-Type: application/json' \
  -d '{"email":"user@example.com","password":"password123"}' \
  | jq -r .token)

# Upload
curl -X POST http://localhost:8080/api/storage/upload \
  -H "Authorization: Bearer $TOKEN" \
  -F 'file=@document.pdf' \
  -F 'visibility=public'

# Download (no auth for public files)
curl -O http://localhost:8080/api/storage/fil_...

# Delete
curl -X DELETE http://localhost:8080/api/storage/fil_... \
  -H "Authorization: Bearer $TOKEN"