Files
The following paths are relative to /api/v1 and require authentication.
Endpoints
Section titled “Endpoints”| Method | Path | Description |
|---|---|---|
POST | /files/upload | Ordinary multipart direct upload |
POST | /files/new | Create an empty file |
POST | /files/upload/init | Negotiate upload mode |
GET | /files/upload/sessions | List recoverable upload sessions |
PUT | /files/upload/{upload_id}/{chunk_number} | Upload one chunk |
POST | /files/upload/{upload_id}/presign-parts | Request object-storage / remote multipart part URLs |
POST | /files/upload/{upload_id}/complete | Assemble chunks or confirm presigned upload |
GET | /files/upload/{upload_id} | Read upload progress |
DELETE | /files/upload/{upload_id} | Cancel upload |
GET | /files/{id} | Read file metadata |
GET | /files/{id}/archive-preview | Read read-only archive preview manifest |
GET | /files/{id}/direct-link | Create direct-download token |
POST | /files/{id}/preview-link | Create short-lived preview link |
POST | /files/{id}/resource-handle | Resolve a frontend-consumable file resource contract |
POST | /files/{id}/wopi/open | Create WOPI launch session |
GET | /files/{id}/download | Download file content |
GET | /files/{id}/thumbnail | Get thumbnail |
GET | /files/{id}/image-preview | Get WebP image preview |
GET | /files/{id}/media-metadata | Get image / audio / video metadata |
PUT | /files/{id}/content | Overwrite content and write version history |
POST | /files/{id}/extract | Create archive extraction task |
PATCH | /files/{id} | Rename or move file |
DELETE | /files/{id} | Soft-delete to trash |
POST | /files/{id}/lock | Lock / unlock file |
POST | /files/{id}/copy | Copy file |
GET | /files/{id}/versions | List versions |
POST | /files/{id}/versions/{version_id}/restore | Restore a version |
DELETE | /files/{id}/versions/{version_id} | Delete a version |
Uploads
Section titled “Uploads”Primary upload entries:
POST /files/new: create a product-level empty filePOST /files/upload/init: negotiate mode firstPOST /files/upload: ordinary multipart uploadGET /files/upload/sessions: recover unfinished sessions after refresh
Creation and upload parameters include:
POST /files/new:folder_idandrelative_pathrequest body fieldsPOST /files/newand its team counterpart accept an optionalIdempotency-Keyheader. Keys are limited to 255 bytes and cannot contain ASCII whitespace.POST /files/upload:folder_id,relative_path, anddeclared_sizequery parametersPOST /files/upload/init:relative_pathandfrontend_client_id
folder_id = null means root. Missing directories in relative_path are created automatically. Empty path segments such as docs//bad.txt are rejected.
GET /files/upload/sessions is a separate recoverable-session listing endpoint. Its optional frontend_client_id query parameter filters sessions created by the same frontend instance; it does not accept folder_id or relative_path and does not perform directory uploads.
Negotiation returns one of four modes:
direct: small-file direct uploadchunked: resumable chunked uploadpresigned: single object-storage or remote presignedPUTpresigned_multipart: object-storage or remote multipart direct upload; the client must request part URLs separately
The frontend never sees an additional relay_stream mode. Actual transfer strategy is decided by storage connectors and policy options:
options.object_storage_upload_strategy: transfer strategy for S3-compatible, Azure Blob, and Tencent COS object-storage connectorsoptions.remote_upload_strategy- OneDrive uses Microsoft Graph native upload capabilities and follows the upload workflow exposed by the connector
relay_stream:initstill returnsdirect/chunked, but the server relays bytes straight to object storage / follower instead of writing a local temp filepresigned:initreturnspresigned/presigned_multipart
Object-storage and remote uploads fall back to relay_stream by default. Legacy {"presigned_upload":true} and {"s3_upload_strategy":"presigned"} are accepted as compatibility inputs for object-storage presigned upload; new clients should send {"object_storage_upload_strategy":"presigned"}.
Presigned browser uploads require usable CORS on the object storage or follower internal storage endpoint. Azure Blob presigned upload uses SAS URLs and requires x-ms-blob-type: BlockBlob; S3-compatible, Tencent COS, and Remote multipart parts usually require returned ETags. Remote presigned upload only works for directly reachable remote nodes; reverse-tunnel remote nodes reject remote_upload_strategy = "presigned".
Direct, chunked, and completion stages
Section titled “Direct, chunked, and completion stages”POST /files/new: the canonical empty-file API. The file picker and folder-upload queue call it directly for 0-byteFilevalues without initializing an upload session or running direct, chunked, presigned, or provider-resumable runners. Its body acceptsfolder_idandrelative_path.POST /files/upload: ordinary multipart upload. For legacy clients,declared_size = 0with an actually empty file field is fully consumed and delegated to the same empty-file use case. Declared zero with nonempty content returnsupload.request_size_mismatchbefore any storage-object mutation. Nonempty same-folder same-name files are not overwritten. With object-storage / Remoterelay_stream, the body is relayed directly to the target driver.GET /files/upload/sessions: lists unexpired, recoverable sessions inuploading/assembling/presignedstatus;frontend_client_idcan filter sessions created by the same frontend instancePUT /files/upload/{upload_id}/{chunk_number}: uploads one chunk, withchunk_numberstarting at0POST /files/upload/{upload_id}/presign-parts: used only forpresigned_multipartGET /files/upload/{upload_id}: returns upload progress used by resumable uploadPOST /files/upload/{upload_id}/complete: completeschunked,presigned, orpresigned_multipart
Recoverable session fields include:
upload_idmodestatusfilenametotal_sizechunk_sizetotal_chunksreceived_countfolder_idchunks_on_diskcompleted_partsexpires_atupdated_at
Completion behavior:
- local path: validates size and quota; if local
content_dedupis enabled, computes SHA-256 and deduplicates blobs - object-storage / OneDrive / Remote paths: validate size and quota but do not deduplicate; each upload creates an independent blob using an upload-session-derived opaque hash and
files/{upload_id}-style object path
POST /files/new is metadata-only empty-file creation. files.blob_id remains non-null and points to the current storage policy’s canonical virtual_empty blob. That blob has size = 0, the standard empty-content SHA-256, and storage_path = NULL; creation does not call connector exists, put, get, or delete. Empty files in one policy share the blob. Downloads and ranges return deterministic empty content from memory, and purging the last reference removes only blob metadata. The first nonempty overwrite switches the file to an ordinary stored blob through the existing upload-finalization transaction.
An Idempotency-Key is isolated by authenticated actor, workspace kind/id, and operation, and only its SHA-256 is persisted. Replaying the same normalized request within 24 hours returns the original file/blob IDs without republishing the file-created event or audit. A different filename, base folder, normalized relative path, or target-name mode returns 409. Missing-parent creation, the canonical virtual blob, file metadata, and the idempotency result commit in one writer transaction. A replay whose retained result file was already purged also returns stable 409. Requests without a key preserve automatic rename behavior.
The browser persists one key per zero-byte upload task for 23 hours. Retries, token refresh, request timeout, and page restore reuse it; success, cancellation, and explicit terminal-task cleanup remove the record.
Empty multipart compatibility still delegates to the same product-level empty-file use case. WebDAV PUT/LOCK staging, storage migration, internal-storage ingress, remote-follower writes, and prepared-blob restore/copy paths continue using zero-length object primitives when their protocols explicitly require a real object.
presigned_multipart completion must include object-storage returned parts; other modes may omit the body.
File operations
Section titled “File operations”GET /files/{id}: read metadata; trashed files behave as not foundGET /files/{id}/archive-preview: read archive manifest; returns202and queuesarchive_preview_generateif not readyGET /files/{id}/direct-link: returns a short token; real download is/d/{token}/{filename}POST /files/{id}/preview-link: returns a short preview link; real content is/pv/{token}/{filename}POST /files/{id}/resource-handle: resolves the resource URL, credentials, cache identity, and redirect policy for the requested purpose and representationPOST /files/{id}/wopi/open: creates a WOPI launch session for a configured WOPI previewerGET /files/{id}/download: streams file content or redirects to a presigned GET URL when policy says so; supportsIf-None-MatchGET /files/{id}/thumbnail: returns thumbnail, or202withRetry-Afterwhile generatingGET /files/{id}/image-preview: returns raw WebP withETag, or202withRetry-Afterwhile generatingGET /files/{id}/media-metadata: returns blob-cached metadata, or202while queuedPUT /files/{id}/content: overwrite existing content, check locks, create version history, and return a newETagPOST /files/{id}/extract: creates an archive extraction taskPATCH /files/{id}: rename or moveDELETE /files/{id}: soft-delete to trash
File info and list items include persisted classification fields:
extension: lowercase final extension without dotcompound_extension: lowercase compound extension such astar.gzfile_category:image,video,audio,document,spreadsheet,presentation,archive,code, orother
These fields are recalculated on create, upload, overwrite, and rename.
Detail responses from GET /files/{id} and GET /teams/{team_id}/files/{id} also include storage_used. This is the quota-accounting size for the file detail view: current size plus all historical version sizes. Directory list items omit this field.
Revision history
Section titled “Revision history”Every file has a canonical revision from creation. Successful overwrites through REST, uploads, WebDAV, or WOPI append immutable revisions; handlers do not maintain separate history tables.
GET /files/{id}/versionslists current and historical revisions in descending sequence order. Uselimit(default 100, maximum 1000) andafter_sequencefor stable keyset pagination.POST /files/{id}/versions/{version_id}/restoreappends a new current head whose content and user dead-property snapshot come from the target. Existing revisions remain intact.DELETE /files/{id}/versions/{version_id}applies only to a historical revision. It purges the content reference while preserving the public identity and sequence as a tombstone, and repairs the successor’s predecessor link.
Revision sequences increase monotonically and are never reused, so gaps are expected. COPY creates an independent history; MOVE/rename, trash, and trash restore retain the existing history. files is the current materialized projection and is counted once for logical quota and blob references.
POST /files/{id}/resource-handle
Section titled “POST /files/{id}/resource-handle”File details answer what a file is. A resource handle answers how the current client should consume it. Request example:
{ "purpose": "preview", "delivery_mode": "blob_url", "representation": "auto"}Allowed values:
purpose:preview,download,external_viewerdelivery_mode:blob_url,text,direct_url,media_stream,iframe_session,manifestrepresentation:auto,original,image_preview,thumbnail; omitted values default toauto
Core response contract:
{ "code": "success", "msg": "", "data": { "identity": { "cache_key": "/api/v1/files/42/download", "etag": "\"blob-hash\"", "scope": "personal" }, "request": { "url": "/api/v1/files/42/download?disposition=inline", "credentials": "include", "conditional_headers": "allowed", "redirect_policy": "same_origin_only" }, "delivery": { "mode": "blob_url", "mime_type": "image/jpeg" } }}identity.cache_key is the stable resource identity, while request.url is the concrete URL for this request. etag, conditional_headers, and credentials tell the frontend whether conditional requests and login credentials are appropriate. redirect_policy = "may_cross_origin" means the URL can be a short-lived OneDrive or object-storage direct URL.
For auto + preview + blob_url, browser-hostile image formats such as HEIC, RAW, and TIFF may resolve to a derived WebP image-preview path. Sandbox-sensitive MIME types such as HTML remain on the same-origin path rather than bypassing isolation through a provider direct URL.
PATCH /files/{id}
Section titled “PATCH /files/{id}”Request:
{ "name": "renamed.pdf", "folder_id": 5}Supports rename, move, and folder_id = null to move to root. Name conflicts at the destination are rejected, and locked files cannot be modified.
Thumbnails
Section titled “Thumbnails”Thumbnail support comes from the media processing registry and is exposed anonymously through /public/thumbnail-support. The built-in images processor covers common image formats. The built-in lofty processor can expose audio suffixes for embedded cover thumbnails. Optional vips_cli / ffmpeg_cli processors contribute additional extensions only when enabled and available.
Storage policies can also contribute storage-native thumbnail and image-preview support through the core policy behavior fields storage_native_thumbnail_enabled = true and storage_native_thumbnail_extensions. A connector descriptor only advertises whether this behavior is supported; it does not carry a second enablement switch. Setting storage_native_thumbnail_enabled = false disables only the provider-native candidate: files on the policy still continue through the global media-processing registry. The extension list remains stored as dormant configuration, so re-enabling restores the saved matching rules. Built-in tencent_cos policies can expose this through COS CI; built-in Local, S3-compatible, Azure Blob, OneDrive, and Remote policies do not expose native thumbnail or image-preview capabilities.
Thumbnails return WebP and reuse cache by blob, processor, processor version, and effective max dimension. The max source byte limit is controlled by thumbnail_max_source_bytes; the rendered longest edge is controlled by runtime config thumbnail_max_dimension.
The default thumbnail dimension keeps the legacy cache version namespace. Non-default dimensions add a -d{dimension} suffix to the derivative version, so changing the configured size does not overwrite or accidentally reuse a different-size cache entry.
Image previews
Section titled “Image previews”Image preview endpoints return larger WebP images for preview panels and are separate from thumbnails:
- thumbnails are list/card-oriented and may return
202 - image previews are previewer-oriented; cache hits return raw WebP, while cache misses enqueue
image_preview_generateand return202withRetry-After - image previews use runtime config
image_preview_max_dimensionfor the rendered longest edge - unsupported types return file/thumbnail-domain errors instead of falling back to original bytes
- the frontend can choose its default strategy from
/public/frontend-configfieldmedia.image_preview_preference
The supported image-preview extensions are the same public capability union advertised by /public/thumbnail-support under image thumbnail/preview support. They can come from backend media processors or a storage-native provider such as Tencent COS when the policy opts in. Image-preview caches follow the same dimension-aware derivative-version rule as thumbnails.
Media metadata
Section titled “Media metadata”Media metadata is cached by blob. Image metadata is read by the built-in images processor, audio by lofty, and video by ffprobe_cli. media_metadata_enabled is the master switch, while per-kind settings live in media_processing_registry_json.
Storage-native media metadata can be enabled per policy with the core policy behavior fields storage_native_media_metadata_enabled = true and storage_native_media_metadata_extensions. A connector descriptor only advertises support. Built-in tencent_cos policies can expose native audio/video metadata through COS CI; built-in Local, S3-compatible, Azure Blob, OneDrive, and Remote policies do not expose native media metadata. Setting the flag to false disables only provider-native extraction; the ordinary global metadata processors remain available. Extension lists are retained while a behavior is disabled, so temporarily disabling provider-native processing does not discard its matching configuration. An enabled behavior with an empty extension list is valid but matches no files.
Audio embedded cover art is exposed through the existing thumbnail path when the lofty processor has thumbnail:audio.
Archive preview
Section titled “Archive preview”GET /files/{id}/archive-preview returns a read-only manifest for supported archive files without extracting them into the workspace.
Optional filename_encoding controls ZIP entry-name decoding:
autoutf8gb18030cp437cp850shift_jisbig5euc_krwindows_1252
Explicit values override auto detection.
Response shape:
{ "code": "success", "msg": "", "data": { "schema_version": 2, "format": "zip", "source_blob_id": 42, "source_hash": "abc...", "generated_at": "2026-05-18T12:00:00Z", "entry_count": 2, "file_count": 1, "directory_count": 1, "total_uncompressed_size": 128, "truncated": false, "entries": [ { "path": "docs/readme.txt", "name": "readme.txt", "parent": "docs", "kind": "file", "size": 128, "compressed_size": 64, "modified_at": "2026-05-18T12:00:00Z" } ] }}Current implementation:
- supports
.zipand corresponding MIME types - disabled by default; requires both
archive_preview_enabledandarchive_preview_user_enabled - first uncached request queues or reuses
archive_preview_generateand returns202 - raw manifest is cached under
entity_propertiesassystem.archive_preview / zip_raw_manifest.v2 - success responses include
ETagand supportIf-None-Match - limits are controlled by archive-preview and archive-extraction runtime settings
- range-capable storage drivers are used for metadata scanning when possible
Direct and preview links
Section titled “Direct and preview links”GET /files/{id}/direct-link returns only a token. The actual URL is:
/d/{token}/{filename}POST /files/{id}/preview-link returns PreviewLinkInfo; actual content is served from:
/pv/{token}/{filename}These root-level endpoints return raw file data or redirects instead of wrapped JSON.