Upload API
Stashly uses a session-based upload model. You create an upload session first, then send files into it. Sessions survive network interruptions — if an upload fails midway, you can resume it without re-uploading completed files.
Active subscription required
Initiating an upload session requires an active subscription. Free plans have limited session storage.
How It Works#
Initiate a session
Tell the API which files you plan to upload (names, sizes, MIME types). You get back a sessionId.
Upload files to the session
POST each file as multipart/form-data to the session. Files are tracked individually — if one fails, retry only that file.
Session completes automatically
When all declared files are uploaded, the session is marked complete and files are permanently stored.
Endpoints#
/api/user/upload/initJWT RequiredInitiate a new upload session. Declare the files you intend to upload.
Request Body
| Name | Type | Description |
|---|---|---|
files* | object[] | Array of file descriptors to upload |
files[].name* | string | File name including extension |
files[].size* | number | File size in bytes |
files[].mimeType | string | MIME type (e.g. image/jpeg) |
{
"success": true,
"data": {
"sessionId": "sess_01j9xk2m",
"expiresAt": "2025-05-29T15:00:00Z",
"files": [
{ "fileId": "fid_abc", "name": "photo.jpg", "status": "pending" }
]
}
}
/api/user/upload/:sessionId/fileJWT RequiredUpload a single file to an existing session. Send as multipart/form-data.
Path Parameters
| Name | Type | Description |
|---|---|---|
sessionId* | string | The session ID returned by /init |
Form Data
| Name | Type | Description |
|---|---|---|
file* | File | The file binary |
{
"success": true,
"data": { "fileId": "fid_abc", "name": "photo.jpg", "status": "complete" }
}
/api/user/upload/:sessionIdJWT RequiredGet the current status of an upload session including per-file progress.
{
"success": true,
"data": {
"sessionId": "sess_01j9xk2m",
"status": "in_progress",
"filesTotal": 3,
"filesComplete": 1,
"files": [
{ "fileId": "fid_abc", "name": "photo.jpg", "status": "complete" },
{ "fileId": "fid_def", "name": "doc.pdf", "status": "pending" }
]
}
}
/api/user/upload/activeJWT RequiredGet the user's currently active (in-progress) upload session, if any.
{
"success": true,
"data": { "sessionId": "sess_01j9xk2m", "status": "in_progress" }
}
/api/user/upload/:sessionId/eventsJWT RequiredGet a log of events for an upload session (created, file-uploaded, cancelled, completed).
{
"success": true,
"data": [
{ "event": "session_created", "at": "2025-05-29T14:00:00Z" },
{ "event": "file_uploaded", "fileId": "fid_abc", "at": "2025-05-29T14:01:30Z" }
]
}
/api/user/upload/:sessionId/cancelJWT RequiredCancel an active upload session. All uploaded files in the session are reverted and storage is freed.
{ "success": true, "data": { "sessionId": "sess_01j9xk2m", "status": "cancelled" } }
Resumable Uploads#
If a file upload fails mid-way (network error, timeout), simply retry the same POST /upload/:sessionId/file request. The API identifies the file by name within the session. Already-completed files are skipped.
Sessions expire
Upload sessions expire 24 hours after creation. Cancel incomplete sessions early to free storage quota.