Media
File Upload
Upload a file to S3-backed storage and resolve an accessible file URL.
Overview
The media module currently exposes two protected endpoints:
POST /api/v1/media/fileGET /api/v1/media/file/{path}
Both endpoints are behind the normal mobile API bearer-token middleware, so call them with:
Authorization: Bearer <access_token>
1. Upload a File
POST /api/v1/media/file
Uploads a single file to the backend S3 storage.
Request type
This endpoint uses multipart/form-data, not JSON.
Form fields
| Field | Required | Notes |
|---|---|---|
file | Yes | One file only, max size 20MB. |
is_public | No | Boolean string. Defaults to true. |
Example cURL
curl -X POST "http://localhost:5053/api/v1/media/file" \
-H "Authorization: Bearer <access_token>" \
-H "Accept-Language: en" \
-F "file=@/path/to/avatar.png" \
-F "is_public=false"
Storage behavior
- When
is_public=true(default), the file is stored under thepublic/prefix. - When
is_public=false, the file is stored under theprivate/prefix. - The backend generates a unique filename based on the current timestamp while preserving the original extension.
Success response
{
"status_code": 200,
"message": "File uploaded successfully",
"data": {
"url": "https://storage.example.com/private/1740823335123456000.png",
"path": "private/1740823335123456000.png",
"filename": "1740823335123456000.png",
"original_name": "avatar.png",
"size": 245120,
"etag": "\"9b2cf535f27731c974343645a3985328\"",
"bucket": "htp-mobile",
"key": "private/1740823335123456000.png",
"mime_type": "image/png",
"uploaded_at": "2026-02-28T10:30:00Z",
"expire_in": 1800
}
}
Response fields
| Field | Meaning |
|---|---|
url | Public URL or pre-signed private URL |
path | Stored object path |
filename | Generated filename only |
original_name | Filename received from client |
key | S3 object key |
expire_in | null for public files, 1800 seconds for private files |
Common failures
400: missing file form field.400: file larger than20MB.400: invalidis_publicboolean.500: S3 upload failed.
2. Get an Accessible URL for a Stored File
GET /api/v1/media/file/{path}
Resolves a stored file path into a usable URL.
Path format
Pass the storage path returned by upload, for example:
GET /api/v1/media/file/private/1740823335123456000.png
Recommended inputs:
public/<filename>private/<filename>
Success response
{
"status_code": 200,
"message": "Success",
"data": {
"url": "https://storage.example.com/private/1740823335123456000.png?signature=...",
"expire_in": 1800
}
}
URL behavior
- For
public/files, the backend returns a direct public URL andexpire_inisnull. - For
private/files, the backend returns a pre-signed URL that currently expires in1800seconds. - The handler first checks whether the object exists in S3 before generating the URL.
Integration Pattern
For most clients, the normal flow is:
- Upload the file with
POST /api/v1/media/file. - Save the returned
pathorkey. - Use
GET /api/v1/media/file/{path}whenever you need a fresh URL for a private object.
If the uploaded object is public, the original upload response already contains a reusable public URL.