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#

1

Initiate a session

Tell the API which files you plan to upload (names, sizes, MIME types). You get back a sessionId.

2

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.

3

Session completes automatically

When all declared files are uploaded, the session is marked complete and files are permanently stored.


Endpoints#

POST/api/user/upload/initJWT Required

Initiate a new upload session. Declare the files you intend to upload.

Request Body

NameTypeDescription
files*object[]Array of file descriptors to upload
files[].name*stringFile name including extension
files[].size*numberFile size in bytes
files[].mimeTypestringMIME type (e.g. image/jpeg)
201Session created
{
  "success": true,
  "data": {
    "sessionId": "sess_01j9xk2m",
    "expiresAt": "2025-05-29T15:00:00Z",
    "files": [
      { "fileId": "fid_abc", "name": "photo.jpg", "status": "pending" }
    ]
  }
}
POST/api/user/upload/:sessionId/fileJWT Required

Upload a single file to an existing session. Send as multipart/form-data.

Path Parameters

NameTypeDescription
sessionId*stringThe session ID returned by /init

Form Data

NameTypeDescription
file*FileThe file binary
200File uploaded
{
  "success": true,
  "data": { "fileId": "fid_abc", "name": "photo.jpg", "status": "complete" }
}
GET/api/user/upload/:sessionIdJWT Required

Get the current status of an upload session including per-file progress.

200Session status
{
  "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" }
    ]
  }
}
GET/api/user/upload/activeJWT Required

Get the user's currently active (in-progress) upload session, if any.

200Active session
{
  "success": true,
  "data": { "sessionId": "sess_01j9xk2m", "status": "in_progress" }
}
GET/api/user/upload/:sessionId/eventsJWT Required

Get a log of events for an upload session (created, file-uploaded, cancelled, completed).

200Session events
{
  "success": true,
  "data": [
    { "event": "session_created", "at": "2025-05-29T14:00:00Z" },
    { "event": "file_uploaded", "fileId": "fid_abc", "at": "2025-05-29T14:01:30Z" }
  ]
}
POST/api/user/upload/:sessionId/cancelJWT Required

Cancel an active upload session. All uploaded files in the session are reverted and storage is freed.

200Session cancelled
{ "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.