Files API
File management endpoints for TMA Cloud.
File management endpoints for TMA Cloud.
Note: All endpoints that accept ids arrays process multiple files in bulk operations. This includes move, copy, star, share, delete, restore, and download operations. One request accepts at most 500 IDs; split larger selections into batches.
Account Scope and Permissions
File endpoints operate on the caller's account, not on the individual login. An account owner and its sub-users read and write the same files, folders and storage quota.
Listing, searching, and reading file details are available to every member of an account. The remaining endpoints require a permission, which owners always hold and sub-users are granted individually:
| Permission | Endpoints |
|---|---|
files.download | GET /:id/download, POST /download/bulk |
files.upload | POST /folder, POST /upload, POST /upload/bulk, POST /upload/check, POST /copy, POST /:id/derived |
files.edit | POST /move, POST /rename, POST /star, POST /:id/replace |
files.share | POST /share, POST /share/links, POST /link-parent-share |
files.delete | POST /delete |
files.trash | POST /trash/restore, POST /trash/delete, POST /trash/empty |
A request without the required permission returns 403 with a message naming it:
{
"message": "You do not have permission to do that. Ask the account owner to enable \"Upload & create\"."
}The check runs before the request body is read, so a rejected upload does not transfer its file. See Authorization.
List Files
GET /api/files
List files and folders.
Query Parameters:
parentId- Parent folder ID (optional)sortBy- Sort field:name,size,modified,accessedAt,deletedAt(optional, defaults tomodified)order- Sort order:asc,desc(optional, defaults todesc)limit- Page size from 1 to 500 (optional, defaults to 200)cursor- Opaque value from the previous response'sX-Next-Cursorheader
Any other sortBy value is ignored and the default is used. deletedAt is only meaningful on /api/files/trash.
Pagination uses a stable folder-first cursor rather than row offsets. If another page exists, the response includes X-Next-Cursor; send that value unchanged as cursor. The web file list loads the next batch automatically near the bottom, keeps earlier items available, and renders only visible rows, so there are no Previous/Next pages. Size sorting uses exact folder totals maintained when files change and remains paginated.
Response:
An array of file and folder objects.
[
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": "folder_456",
"starred": false,
"shared": true,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z",
"sharedAt": "2024-01-03T10:30:00Z",
"expiresAt": "2024-01-10T10:30:00Z"
}
]accessedAt: When the item was last read. Listing a folder updates that folder's own accessedAt, not the entries returned. The value is approximate — it is written at most once per hour per item and may lag by the cache TTL of this response. See File System.
Share fields: sharedAt is when the item joined a share. expiresAt is when access ends, or null for a link with no expiration. Both are null when shared is false. For an item that belongs to more than one share, expiresAt is null if any link has no expiration; otherwise it is the latest expiration.
File Statistics
GET /api/files/stats
Get file statistics for the current user.
Response fields:
totalFiles— total number of files owned by the usertotalFolders— total number of folders owned by the usersharedCount— number of share links the user has created (not items shared with the user)starredCount— number of files/folders the user has starred
{
"totalFiles": 100,
"totalFolders": 50,
"sharedCount": 10,
"starredCount": 25
}Search Files
GET /api/files/search
Search files.
Query Parameters:
qorquery- Search query (required)limit- Result limit (optional)
Response:
An array of file and folder objects matching the search query.
[
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": "folder_456",
"starred": false,
"shared": true,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z",
"sharedAt": "2024-01-03T10:30:00Z",
"expiresAt": "2024-01-10T10:30:00Z"
}
]Searching does not count as reading. The accessedAt values returned are unchanged by the search itself.
Create Folder
POST /api/files/folder
Create a new folder.
Request Body:
{
"name": "New Folder",
"parentId": "parent_folder_id"
}Validation:
name: Required. Must not be empty after trimming. Max length 100. Any character is allowed except control characters and/ \ : * ? " < > |, so spaces, accents, and non-Latin scripts are accepted.parentId: Optional. Must be a string.
Response:
The created folder object.
{
"id": "folder_123",
"name": "New Folder",
"type": "folder",
"size": null,
"modified": "2024-01-01T00:00:00Z"
}Check Upload Storage
POST /api/files/upload/check
Check whether an upload would fit the storage quota, before sending the file. Only the size is checked; file content is not inspected, because any file is accepted regardless of whether its content matches its extension.
Request Body:
{
"fileSize": 1024
}Validation:
fileSize: Required. Must be a non-negative integer representing the total size in bytes.
Response (200, allowed):
{
"allowed": true
}Response (413, quota exceeded):
{
"message": "Storage limit exceeded. Required: 1 GB, available: 500 MB."
}Checking before the upload starts is what lets the client stop an over-quota upload without transferring the file first.
Recent Files
GET /api/files/recent
List the account's most recently read files, ordered by accessed_at descending. Folders and trashed items are excluded.
Query Parameters:
limit- Optional. Defaults to 10, clamped to the recent-files cache size.
Response:
An array of file objects, same shape as List Files. The result is served from the Redis cache when available.
Upload File
POST /api/files/upload
Upload a file.
Form Data:
file- File to upload (required)parentId- Parent folder ID (optional)lastModifiedTimes- Optional. The client's original modification time for the file. A value that fails validation is ignored and the row keeps the upload time.
MIME Type Handling:
- The stored MIME type is detected from the file's content (magic bytes), not the filename or the client-sent
Content-Typeas both of which can lie. When the content is not recognisable, the client-sent type is kept. - A mismatch between content and extension never rejects the upload so all files are stored.
- Normal downloads are served as attachments. The authenticated image viewer can request
inline=1; the server permits inline delivery only for a detected image type that is not executable content.
Duplicate names: If a file with the same name already exists in the parent folder, the server assigns a unique display name (e.g. document (1).pdf). The client can instead overwrite the existing file by calling POST /api/files/:id/replace with the existing file's ID.
Response:
The uploaded file object.
Client abort: If the client cancels the request, the server returns HTTP 499 with error: "REQUEST_ABORTED" and message: "Upload cancelled by client"
{
"id": "file_123",
"name": "uploaded_file.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": null,
"modified": "2024-01-01T00:00:00Z"
}Bulk Upload Files
POST /api/files/upload/bulk
Upload multiple files at once using multipart/form-data.
Form Data:
files- Files to upload (required)parentId- Parent folder ID (optional)relativePaths- Optional. One value per file, same order asfiles. When present, the server uses these relative paths (e.g."MyFolder/sub/file.txt") to recreate folder structure underparentId.clientIds- Optional. One value per file, same order asfiles. Echoed back in the response for easier client-side matching.lastModifiedTimes- Optional. One value per file, same order asfiles. The client's original modification times. Entries that fail validation are ignored individually rather than failing the batch.
relativePaths, clientIds and lastModifiedTimes are repeated fields joined to the file parts by ordinal, so all three must carry one entry per file in the same order.
Duplicate names: For each file, if a file with the same name already exists in the parent folder, the server assigns a unique display name (e.g. document (1).pdf).
Response:
On success, returns a summary object. files contains created file records; failed contains per-file errors (if any).
Client abort: If the client cancels the request (e.g. user clicks Cancel upload), the server returns HTTP 499 with error: "REQUEST_ABORTED" and message: "Upload cancelled by client"
{
"files": [
{
"id": "file_123",
"name": "file1.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": null,
"modified": "2024-01-01T00:00:00Z",
"clientId": "entry-0"
},
{
"id": "file_456",
"name": "file2.jpg",
"type": "file",
"size": 2048,
"mimeType": "image/jpeg",
"parentId": null,
"modified": "2024-01-01T00:00:00Z",
"clientId": "entry-1"
}
],
"failed": [
{
"fileName": "bad-file.exe",
"error": "Invalid file type",
"clientId": "entry-2"
}
],
"total": 3,
"successful": 2,
"failedCount": 1
}Move Files
POST /api/files/move
Move files and/or folders to a different location.
Request Body:
{
"ids": ["file_123", "file_456"],
"parentId": "target_folder_id"
}Validation:
ids: Required. Must be a non-empty array of strings.parentId: Optional. Must be a string.
Response:
{
"message": "Files moved successfully."
}Copy Files
POST /api/files/copy
Copy files and/or folders to a different location.
Request Body:
{
"ids": ["file_123", "file_456"],
"parentId": "target_folder_id"
}Validation:
ids: Required. Must be a non-empty array of strings.parentId: Optional. Must be a string.
Response:
{
"message": "Copy queued.",
"queued": true,
"jobId": "34d6ea50-2adb-4e4f-9dce-f97d1ab219db"
}Copy always returns 202. The account-file-operations worker copies the tree and publishes the normal file events after completion. If the queue is unavailable, the endpoint returns 503.
Rename File
POST /api/files/rename
Rename a file or folder.
Request Body:
{
"id": "file_123",
"name": "New Name"
}Validation:
id: Required. Must be a string.name: Required. Must not be empty after trimming. Max length 100. Any character is allowed except control characters and/ \ : * ? " < > |, so spaces, accents, and non-Latin scripts are accepted.
Response:
The updated file or folder object.
{
"id": "file_123",
"name": "New Name",
"type": "file",
"modified": "2024-01-01T00:00:00Z"
}Star Files
POST /api/files/star
Star or unstar one or more files/folders.
Request Body:
{
"ids": ["file_123", "file_456"],
"starred": true
}Validation:
ids: Required. Must be a non-empty array of strings.starred: Required. Must be a boolean.
Response:
{
"message": "File starred status updated."
}Get Starred Files
GET /api/files/starred
List all starred files and folders.
Query Parameters:
sortBy- Sort field (optional)order- Sort order (optional)limit- Page size from 1 to 500 (optional, defaults to 200)cursor- Value from the previousX-Next-Cursorresponse header
Response:
An array of file and folder objects.
[
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": "folder_456",
"starred": true,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z"
}
]Share Files
POST /api/files/share
Share or unshare files. Creates share links if they don't exist, or removes sharing if shared: false.
Request Body:
{
"ids": ["file_123", "file_456"],
"shared": true,
"expiry": "7d"
}Validation:
ids: Required. Must be a non-empty array of strings.shared: Required. Must be a boolean.expiry: Optional. One of"7d","30d", or"never".
Response (when sharing):
{
"links": {
"file_123": "http://example.com/s/token123",
"file_456": "http://example.com/s/token456"
}
}Response (when unsharing):
{
"message": "Files unshared successfully."
}Get Share Links
POST /api/files/share/links
Get existing share links for multiple files without creating new ones.
Request Body:
{
"ids": ["file_123", "file_456"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response:
{
"links": {
"file_123": "http://example.com/s/token123"
}
}Link to Parent Share
POST /api/files/link-parent-share
Link files to their parent folder's share link. If the parent folder is shared, the files will be added to that share.
Request Body:
{
"ids": ["file_123", "file_456"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response:
{
"links": {
"file_123": "http://example.com/s/parent_token"
}
}Get Shared Files
GET /api/files/shared
List files and folders shared by the current user. Includes share link expiry information.
Query Parameters:
sortBy- Sort field (optional)order- Sort order (optional)limit- Page size from 1 to 500 (optional, defaults to 200)cursor- Value from the previousX-Next-Cursorresponse header
Response:
An array of shared file and folder objects. Each object includes sharedAt and expiresAt. expiresAt is null if the link has no expiration.
[
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"starred": false,
"shared": true,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z",
"sharedAt": "2024-01-03T10:30:00Z",
"expiresAt": "2024-01-08T00:00:00Z"
}
]Progress Streaming
POST /api/files/delete and POST /api/files/trash/restore can stream progress for selections that remain in the web process. Send an Accept: application/x-ndjson request header and the response is application/x-ndjson — one JSON object per line — instead of a single JSON body.
The selected ids are processed in batches. One line is written after each batch completes, followed by a final line:
{"type":"progress","done":0,"total":40}
{"type":"progress","done":25,"total":40}
{"type":"progress","done":40,"total":40}
{"type":"done","message":"Files moved to trash."}progress—doneoftotalids processed so far. The first line reportsdone: 0.done— the operation finished. Its remaining fields are the same body the endpoint returns without streaming (for examplemessage).error— the operation failed partway;messagedescribes the failure. HTTP status is already200, so read the stream to detect this.
Without the Accept: application/x-ndjson header, each endpoint returns the single JSON object documented for it below. Trees over 1,000 items are queued before streaming starts. The streamed batches are not a single transaction: a failure after the first batch leaves earlier batches applied.
Delete Files
POST /api/files/delete
Move one or more files/folders to the trash.
Request Body:
{
"ids": ["file_123", "file_456"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response:
{
"message": "Files moved to trash."
}Progress streaming: With an Accept: application/x-ndjson request header, progress is streamed line by line. See Progress Streaming.
If the selected roots contain more than 1,000 items, Move to Trash returns 202 with queued: true and a jobId; the account-file-operations worker completes it.
Get Trash
GET /api/files/trash
List all files and folders currently in the trash.
Query Parameters:
sortBy- Sort field (optional)order- Sort order (optional)limit- Page size from 1 to 500 (optional, defaults to 200)cursor- Value from the previousX-Next-Cursorresponse header
Response:
An array of trashed file and folder objects.
[
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"mimeType": "application/pdf",
"parentId": "folder_456",
"starred": false,
"modified": "2024-01-01T00:00:00Z"
}
]Restore Files
POST /api/files/trash/restore
Restore one or more files/folders from the trash.
Request Body:
{
"ids": ["file_123", "file_456"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response:
{
"message": "Restored 2 file(s) from trash"
}Progress streaming: With an Accept: application/x-ndjson request header, progress is streamed line by line. See Progress Streaming.
If the selected roots contain more than 1,000 items, Restore returns 202 with queued: true and a jobId; the account-file-operations worker completes it.
Permanent Delete
POST /api/files/trash/delete
Permanently delete one or more files/folders from the trash. This action is irreversible.
Request Body:
{
"ids": ["file_123", "file_456"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response:
{
"message": "Permanent deletion queued.",
"queued": true,
"jobId": "34d6ea50-2adb-4e4f-9dce-f97d1ab219db"
}Permanent Delete always returns 202. The account-file-operations worker deletes storage objects and database rows in batches. If the queue is unavailable, the endpoint returns 503.
Empty Trash
POST /api/files/trash/empty
Permanently delete all files and folders in the trash.
Response:
{
"message": "Deletion of 5 item(s) queued",
"queued": true,
"jobId": "..."
}The account-file-operations worker deletes storage objects and database rows in batches. If the queue is unavailable, this endpoint returns 503 instead of doing the full purge in the web request. If trash is already empty, it returns {"message": "Trash is already empty"}.
Get Background File Job
GET /api/files/jobs/:jobId
Get the state or result of a queued copy, move-to-trash, restore, permanent-delete, or Empty Trash operation.
The job must belong to the authenticated account. A missing job or a job owned by another account returns 404.
Pending response (202):
{
"jobId": "34d6ea50-2adb-4e4f-9dce-f97d1ab219db",
"status": "active"
}Completed response (200):
{
"jobId": "34d6ea50-2adb-4e4f-9dce-f97d1ab219db",
"status": "completed",
"result": {
"count": 2,
"ids": ["file_789", "file_790"]
}
}ids is present for copy jobs. A failed or cancelled job returns 500. An invalid job ID returns 400, and an unavailable queue returns 503.
Download File
GET /api/files/:id/download
Download a single file or a folder (folders are returned as a ZIP archive).
Validation:
id: Required. Must be a string.
Query Parameters:
inline=1- Display a safe image inline. Other file types remain attachments.
Response: The raw file content or a ZIP archive.
Single-file downloads accept an HTTP Range header and reply 206 Partial Content with Content-Range for the requested bytes; the response advertises Accept-Ranges: bytes. Only the segments overlapping the range are read and decrypted. Folder/bulk downloads are streamed ZIP archives and do not support ranges.
Downloading marks the item as read. For a folder, the folder and every entry in the archive are marked. See Last Access Time.
Get File or Folder Info
GET /api/files/:id/info
Get basic metadata for a single file or folder.
Path Parameters:
id- File or folder ID (required)
Response (file):
{
"id": "file_123",
"name": "document.pdf",
"type": "file",
"size": 1024,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z",
"shared": true,
"sharedAt": "2024-01-03T10:30:00Z",
"expiresAt": "2024-01-10T10:30:00Z",
"parentId": "folder_456"
}Response (folder):
{
"id": "folder_123",
"name": "Reports",
"type": "folder",
"size": null,
"modified": "2024-01-01T00:00:00Z",
"accessedAt": "2024-01-02T09:15:00Z",
"parentId": null,
"folderInfo": {
"totalSize": 1048576,
"fileCount": 25,
"folderCount": 3
}
}sizeandfolderInfo.totalSizeare in bytes.- For folders,
sizemay benull; usefolderInfo.totalSizefor the recursive size. accessedAtis the last read time. Calling this endpoint does not update it.sharedAtis when the item joined a share.expiresAtis the effective share expiration, ornullwhen the item is unshared or at least one associated link has no expiration.
Errors:
404- File or folder not found or not owned by the user
Replace File Contents
POST /api/files/:id/replace
Replace the contents of an existing file. The file ID and name stay the same; size, modified time and last access time are updated. Used when the user chooses "Replace the File" for a duplicate-name upload, and by the Windows desktop app when syncing edits from Word, Excel, PowerPoint, or other desktop editors.
Path Parameters:
id- File ID (required)
Form Data:
file- New file content (required). Single file inmultipart/form-data.
Validation:
- The file must exist and belong to the authenticated user.
- MIME type is detected from file content. Stored MIME type is updated to match.
Response:
The updated file object.
{
"id": "file_123",
"name": "document.docx",
"type": "file",
"size": 2048,
"mimeType": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"parentId": "folder_456",
"starred": false,
"modified": "2024-01-01T12:00:00Z"
}Errors:
404- File not found or not owned by the user400- No file in request or invalid file type
Upload Derived File (e.g. Exported Document/PDF)
POST /api/files/:id/derived
Upload a new file that is derived from an existing one while keeping the original file unchanged. This is used by the Windows desktop app when a user opens a document via Open on desktop and then uses Save As or Export (for example, exporting a Word document to PDF or saving a copy as .docx, .xlsx, or .pptx).
The new file is created as a separate entry in the same folder as the original file.
Path Parameters:
id- Source file ID (required). The new file is created as a sibling of this file.
Form Data:
file- Derived file content (required). Single file inmultipart/form-data.
The server streams and encrypts the multipart upload directly to the configured bucket, then creates the file metadata.
Validation:
- The source file must exist and belong to the authenticated user.
- The derived file name must be a valid file name.
- MIME type is detected from file content and stored accordingly.
- Storage quota and max upload size checks apply in the same way as for normal uploads.
Response:
The created file object for the derived file. Common use cases include exported PDFs and alternate Office document formats saved from Word, Excel, or PowerPoint.
{
"id": "file_derived_123",
"name": "document.pdf",
"type": "file",
"size": 4096,
"mimeType": "application/pdf",
"parentId": "folder_456",
"starred": false,
"modified": "2024-01-01T12:05:00Z"
}Errors:
404- Source file not found or not owned by the user400- No file in request or invalid file type413- Storage limit exceeded
Bulk Download Files
POST /api/files/download/bulk
Download multiple files and/or folders as a single ZIP archive.
Request Body:
{
"ids": ["file_123", "file_456", "folder_789"]
}Validation:
ids: Required. Must be a non-empty array of strings.
Response: A ZIP archive containing all selected files and folders.
Note: For single file downloads, use the GET endpoint. This endpoint is optimized for downloading multiple items at once.
File Events
GET /api/files/events
Establish a real-time event stream (Server-Sent Events) for file system changes. Requires Redis to be configured.
Response: A Server-Sent Events stream.
Related Topics
- Sharing - Share link endpoints
- Authorization - Account scope and permissions
- File System Concepts - File system overview