API Reference · Assets & files
API Reference

Assets: artwork, content-addressed

Uploads are image-only and capped at 25 MB. Files are stored under their SHA-256 hash, so the same artwork never duplicates on disk, and fetched back byte-identical from /files/<name>.

POST/assetsJWT Bearer · multipart/form-data

Uploads one image. The request must be multipart/form-data with a file field; the mime type must start with image/. Over the 25 MB cap the request fails with 413 before a byte is stored.

curl
curl -X POST http://localhost/api/assets \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…" \
  -F "file=@/path/to/artwork.png"
201 Created
{
  "asset": {
    "id": "cml4kc9gh0003jq8f2a1b0d9f",
    "userId": "cml4k9x2b0000jq8f3h5a1d2e",
    "filename": "9f2c4e6a8b0d1f3e5a7c9b8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f.png",
    "originalName": "artwork.png",
    "url": "/files/9f2c4e6a8b0d1f3e5a7c9b8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f.png",
    "mimeType": "image/png",
    "sizeBytes": 184320,
    "createdAt": "2026-08-06T10:26:40.000Z"
  }
}
FieldTypeDescription
400ApiError“A multipart file field is required.”
413ApiErrorFile over the 25 MB artwork limit.
415ApiError“Only image uploads are allowed (received '<mime>').”
401ApiErrorMissing or expired JWT.
GET/assetsJWT Bearer

Lists your uploads, newest first.

curl
curl http://localhost/api/assets \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…"
200 OK
{ "assets": [ { "id": "cml4kc9gh0003jq8f2a1b0d9f", … } ], "total": 1 }
GET/files/:nameNo auth

Serves a stored upload by its stored filename (the <sha256>.<ext> pattern — path traversal is rejected by a strict name check). Responses carry immutable cache headers, and the bytes round-trip exactly. In the Docker deployment nginx serves /files/<name> straight from MinIO (the api stores objects with anonymous-read policy); in local development the api's local-disk driver serves the same path — same URLs, same behavior.

curl
curl http://localhost/files/9f2c4e6a…c0d9e8f.png --output artwork.png
# → 200, image/png, byte-identical to the upload

Where files live

Storage sits behind a StorageDriver interface chosen by the STORAGE_DRIVER environment variable. The local driver writes to ./data/uploads (override with UPLOAD_DIR); the Docker deployment targets MinIO. The database row only stores the filename, original name, URL, mime type, and size — deleting an account cascades to its asset records.

Continue to API key endpoints