Storage Operations File and folder operations, locking, previews, and transforms
Storage endpoints are available on both workspaces and shares. The API patterns are identical — replace workspace/{workspace_id} with share/{share_id} in any path below unless noted as workspace-only or share-only.
All endpoints require JWT authentication unless otherwise noted. Include the header Authorization: Bearer {jwt_token} with every request.
Conventions
- Root folder: Use the literal string
"root"as the path parameter (e.g.,/storage/root/list/) - Trash folder: Use
"trash"to list trashed items (e.g.,/storage/trash/list/) - Node IDs: OpaqueIds — 30-character alphanumeric strings displayed with hyphens (e.g.,
f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4). Use as-is in API calls. - Node ID prefixes:
f= file,d= folder,n= note - Node types in responses:
"file","folder","note","link"(lowercase strings) - Parent field: Nodes at the storage root have
"parent": "root"; nested nodes show the parent's OpaqueId - Delete vs purge:
DELETE .../storage/{node_id}/delete/moves to trash.DELETE .../storage/trash/delete/empties the entire trash.DELETE .../storage/{node_id}/purge/permanently deletes a single trashed item. - Workspace folder shares: Shares that reference a workspace folder have their
rootmapped to the designated folder. All operations are scoped to that subtree. - Compact responses: Every storage endpoint that returns nodes (list, details, search, metadata, trash, quickshares) accepts an optional
?output=<token>query parameter with three detail levels:terse,standard, orfull. See the "Compact Responses" section below for the full contract, field lists, and the HTTP 400 rule for multi-level combinations.
Compact Responses (output=)
Every storage endpoint that returns node objects — folder listings, node details, search hits, metadata endpoints, trash listings, and quickshares — accepts an optional output query parameter that selects the shape of each node in the response. A single detail-level token may be combined with modifier tokens; specifying two detail levels (e.g. ?output=terse,standard) returns HTTP 400. When output= is omitted, responses are full and byte-for-byte unchanged.
| Level | Fields returned on each node (cumulative) |
|---|---|
terse | id, type, name, parent, mimetype, size, modified, recursive nodes for folders when the endpoint returns children, summary reduced to {title} only, previews reduced to a per-type {ready: bool} map (keys preserved: thumbnail, image, pdf, mp4, hlsstream, audio, spreadsheet), folder-only is_share_root/share_id |
standard | terse + version, created, restricted, dmca, locked, mimecategory, origin reduced to {creator, type}, ai reduced to {state}, metadata reduced to {title, short} (user-authored overrides), deleted, deleted_from, is_imported, link-only target_type/target_id |
full | standard + summary.short/summary.long, note-only summary.category, virus, full ai object, full file_attributes (including EXIF), all remaining origin.* fields, hash, hash_algo, lock_info, import_metadata, full previews state map |
Use terse for list rows, tree rendering, pickers, breadcrumb navigation, and drag-and-drop targets — it carries modified (so list rows can render the date column and "sort by modified" without a follow-up fetch), a per-type previews readiness map (so the thumbnail selector can pick the best available source), and the summary title. Use standard for most detail views, file-browser main lists, and any UI that shows AI-processing state, lock/restricted chips, DMCA chips, import-provider chips, or trash state — it adds the ai.state that drives the "summarizing…" spinner, timestamps, origin creator plus type, the dmca flag for DMCA chip rendering, is_imported for import-provider chips, link-node target_type/target_id discriminators, and the metadata.title/metadata.short user overrides that list rows render when a custom title is set. Use full (or omit the parameter) for the node detail pane, virus/AI inspection, version history, and any workflow that reads long-form summaries, EXIF, import provider metadata, or content hashes. Unknown tokens are silently ignored. Add the markdown modifier (e.g. ?output=standard,markdown) to receive the response as GitHub-flavored Markdown (Content-Type: text/markdown; charset=UTF-8) instead of JSON — see the cross-cutting ?output= reference for the full contract.
Node Object Schema
All endpoints that return node data use this format. Fields vary by node type.
| Field | Type | Present On | Description |
|---|---|---|---|
id | string | all | OpaqueId of the node |
name | string | all | File, folder, or note name |
type | string | all | "file", "folder", "note", or "link" |
parent | string | all | Parent folder OpaqueId or "root" |
size | integer | file | File size in bytes |
hash | string | file | Content hash of the file |
hash_algo | string | file | Hash algorithm (e.g., "md5") |
mimetype | string | file | MIME type (e.g., "application/pdf") |
mimecategory | string | file | MIME category (e.g., "document", "image") |
version | string | file, note | Current version identifier (e.g., "v1") |
created | string | all | Creation timestamp (YYYY-MM-DD HH:MM:SS) |
modified | string | all | Last-modified timestamp (YYYY-MM-DD HH:MM:SS) |
restricted | boolean | all | Whether the file has been restricted |
dmca | boolean | all | Whether the file has a DMCA flag |
locked | boolean | all | Whether the node has an active lock |
lock_info | object/null | all | Lock details when locked; null otherwise |
virus | object | file | Virus scan status: {"status": "scanned", "infected": false} |
file_attributes | object | file | Media metadata: width, height, duration (where applicable) |
summary | object | file | AI-generated summary: {"title": "...", "short": "...", "long": "..."} |
metadata | object/null | file | User-defined custom title and description overrides |
previews | object | file | Preview generation state per type (e.g., {"thumbnail": {"state": "ready"}}) |
ai | object | file | AI processing state: {"state": "...", "attach": true/false} |
origin | object | file | Origin info: {"type": "upload", "creator": "{user_id}"} |
AI States
| Value | Description |
|---|---|
disabled | AI processing is disabled for this file |
pending | Queued for AI processing |
inprogress | AI processing is running |
ready | AI processing complete |
failed | AI processing failed |
indexed | File has been indexed for search and RAG |
Keyset Pagination (Storage List)
Storage listing endpoints (list and recent) use cursor-based pagination, not offset-based.
Request Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| sort_by | string | name | One of: name, updated, created, type |
| sort_dir | string | asc | One of: asc, desc |
| page_size | int | 100 | One of: 100, 250, 500 (snapped to nearest) |
| cursor | string | — | Opaque cursor string from previous response |
Response Pagination Fields
| Field | Type | Description |
|---|---|---|
pagination.has_more | boolean | Whether more pages exist |
pagination.next_cursor | string/null | Cursor for the next page; null if last page |
pagination.page_size | integer | Effective page size used |
Notes:
- Cursors are HMAC-signed; tampered cursors are rejected with an error.
- When using a cursor, the page size from the cursor takes precedence over the request parameter.
- Only the first page of each sort configuration is cached (30-second TTL).
- The
recentendpoint ignoressort_byandsort_dir(always sorted byupdateddescending).
List Folder Contents
/current/workspace/{workspace_id}/storage/{parent_id}/list/
/current/share/{share_id}/storage/{parent_id}/list/
List the contents of a folder. Uses keyset pagination.
Auth required. Permission: View (workspace), Guest+ (share). Share list on public shares may not require JWT.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {parent_id} | string | Yes | Folder OpaqueId, "root", or "trash" |
Query parameters: See Keyset Pagination section above.
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/root/list/?sort_by=name&sort_dir=asc&page_size=100" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"nodes": {
"items": [
{
"id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz123",
"type": "folder",
"name": "Documents",
"parent": "root",
"created": "2025-01-01T00:00:00Z",
"modified": "2025-01-20T14:45:00Z"
},
{
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"type": "file",
"name": "photo.jpg",
"parent": "root",
"size": 2048000,
"mimetype": "image/jpeg",
"created": "2025-01-10T09:00:00Z",
"modified": "2025-01-10T09:00:00Z"
}
],
"path": "/",
"parent": null
},
"pagination": {
"has_more": true,
"next_cursor": "eyJwIjoiMmFiYzEyMy4uLiIsInMi...",
"page_size": 100
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
nodes.items | array | Array of node objects for current page |
nodes.path | string | Current folder path |
nodes.parent | string/null | Parent folder ID or null for root |
pagination.has_more | boolean | true if more pages exist |
pagination.next_cursor | string/null | Cursor for next page |
pagination.page_size | integer | Actual page size used |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Folder not found |
1605 (Invalid Input) | 400 | Node is not a folder |
1605 (Invalid Input) | 400 | Invalid pagination cursor (tampered or mismatched) |
Node Details
/current/workspace/{workspace_id}/storage/{node_id}/details/
/current/share/{share_id}/storage/{node_id}/details/
Get full details for a single node (file, folder, or note).
Auth required. Permission: View (workspace), View (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/details/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"node": {
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"type": "file",
"name": "document.pdf",
"parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz123",
"size": 5242880,
"hash": "d41d8cd98f00b204e9800998ecf8427e",
"hash_algo": "md5",
"mimetype": "application/pdf",
"mimecategory": "document",
"version": "v1",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-20T14:45:00Z",
"restricted": false,
"dmca": false,
"locked": false,
"lock_info": null,
"virus": { "status": "scanned", "infected": false },
"file_attributes": { "width": null, "height": null, "duration": null },
"summary": {
"title": "Quarterly Report",
"short": "Q4 financial summary",
"long": "Comprehensive financial report covering revenue and expenses."
},
"metadata": null,
"previews": {
"thumbnail": { "state": "ready" },
"pdf": { "state": "ready" }
},
"ai": { "state": "indexed", "attach": true },
"origin": { "type": "upload", "creator": "98765432109876543210" }
}
}
}
Response fields: See Node Object Schema above for complete field reference.
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1680 (Access Denied) | 403 | No permission to view details (share only) |
Add File from Upload
/current/workspace/{workspace_id}/storage/{parent_id}/addfile/
/current/share/{share_id}/storage/{parent_id}/addfile/
Add a previously uploaded file to storage.
Auth required. Permission: Guest (workspace), file creation permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {parent_id} | string | Yes | Parent folder OpaqueId or "root" |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Filename for the new file |
| from | string | Yes | JSON-encoded source object (see below) |
from format (workspace):
{"type": "upload", "upload": {"id": "{upload_id}"}}
from format (share — also supports hash source):
{"from_type": "upload", "upload": {"upload_id": "{upload_id}"}}
{"from_type": "hash", "hash": {"hash": "{file_hash}", "hash_type": "sha256"}}
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/root/addfile/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'name=document.pdf' \
-d 'from={"type":"upload","upload":{"id":"abc123opaqueid"}}'
Response
{
"result": "yes",
"response": {
"node": {
"id": "f4kn6-arybw-qxes3-ey9z6-cwoc4-sglhn5",
"type": "file",
"name": "document.pdf",
"parent": "root",
"size": 5242880,
"hash": "d41d8cd98f00b204e9800998ecf8427e",
"hash_algo": "md5",
"mimetype": "application/pdf",
"mimecategory": "document",
"version": "v1",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-15T10:30:00Z",
"restricted": false,
"dmca": false,
"locked": false
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 400 | Upload session not found or not associated with your account |
1605 (Invalid Input) | 400 | Upload is not complete |
1609 (Not Found) | 404 | Parent folder not found |
1605 (Invalid Input) | 400 | Parent node is not a folder |
1609 (Not Found) | 404 | Parent folder is in trash |
1605 (Invalid Input) | 400 | Name conflict (only when using FAIL strategy) |
Notes:
- The upload session must be in COMPLETE status before adding the file.
- Virus scanning occurs during upload assembly, not at this stage.
- Conflict resolution: If a file with the same name exists, the default behavior is to replace (overwrite) the existing file, creating a version for rollback. Folder or type-mismatch conflicts fall back to renaming.
Add Link (Workspace Only)
/current/workspace/{workspace_id}/storage/{parent_id}/addlink/
Add a share link node to workspace storage. Link nodes represent references to shares within the workspace tree.
Auth required. Permission: Guest.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} | string | Yes | 19-digit workspace profile ID |
| {parent_id} | string | Yes | Parent folder OpaqueId or "root" |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| link_target_type | string | Yes | Must be "share" |
| share | string | Yes | Share identifier to link |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/root/addlink/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'link_target_type=share' \
-d 'share=my-share-name'
Response
{"result": "yes"}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1683 (Resource Missing) | 404 | Share not found or not accessible |
1605 (Invalid Input) | 400 | Share does not belong to this workspace |
1605 (Invalid Input) | 400 | A link to this share already exists (only one per share) |
Create Folder
/current/workspace/{workspace_id}/storage/{parent_id}/createfolder/
/current/share/{share_id}/storage/{parent_id}/createfolder/
Create a new folder.
Auth required. Permission: Guest (workspace), folder creation permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {parent_id} | string | Yes | Parent folder OpaqueId or "root" |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Folder name |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/root/createfolder/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'name=Documents'
Response
{
"result": "yes",
"response": {
"node": {
"id": "d2bcd-efghi-jklmn-opqrs-tuvwx-yz1234",
"type": "folder",
"name": "Documents",
"parent": "root",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-15T10:30:00Z",
"restricted": false,
"dmca": false,
"locked": false
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 400 | Duplicate name in parent folder |
1609 (Not Found) | 404 | Parent folder not found |
1605 (Invalid Input) | 400 | Parent node is not a folder |
1609 (Not Found) | 404 | Parent folder is in trash |
1680 (Access Denied) | 403 | No folder creation permission (share only) |
Create Note (Workspace Only)
/current/workspace/{workspace_id}/storage/{parent_id}/createnote/
Create a markdown note. Notes are auto-indexed for AI when workspace intelligence is enabled.
Auth required. Permission: Guest.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} | string | Yes | 19-digit workspace profile ID |
| {parent_id} | string | Yes | Parent folder OpaqueId or "root" |
Request Body (form-encoded)
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| name | string | Yes | Must end in .md | Note name |
| content | string | Yes | Max 100 KB | Markdown content |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/root/createnote/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'name=meeting-notes.md' \
-d 'content=# Meeting Notes\n\nDiscussed project timeline.'
Response
{
"result": "yes",
"response": {
"note": {
"id": "n5mno-pqrst-uvwxy-z1234-abcde-fghij6",
"type": "note",
"name": "meeting-notes.md",
"parent": "root",
"mimetype": "text/markdown",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-15T10:30:00Z"
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 400 | Filename must end with .md |
1605 (Invalid Input) | 400 | Duplicate name in parent folder |
1609 (Not Found) | 404 | Parent folder not found |
Update Note (Workspace Only)
/current/workspace/{workspace_id}/storage/{node_id}/updatenote/
Update an existing note's name and/or content. Updating content creates a new version.
Auth required. Permission: Guest.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} | string | Yes | 19-digit workspace profile ID |
| {node_id} | string | Yes | Note OpaqueId |
Request Body (form-encoded)
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| name | string | No | Must end in .md | New note name |
| content | string | No | Max 100 KB | New markdown content |
At least one of name or content is required.
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghij6/updatenote/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'content=# Updated Notes\n\nRevised content here.'
Response
{
"result": "yes",
"response": {
"note": {
"id": "n5mno-pqrst-uvwxy-z1234-abcde-fghij6",
"type": "note",
"name": "meeting-notes.md",
"parent": "root",
"mimetype": "text/markdown",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-20T14:45:00Z"
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Note not found |
1605 (Invalid Input) | 400 | Node is not a note |
1609 (Not Found) | 404 | Note is in trash |
1605 (Invalid Input) | 400 | No content or name provided |
1605 (Invalid Input) | 400 | Duplicate name in parent folder |
Read Note
/current/workspace/{workspace_id}/storage/{node_id}/readnote/
/current/share/{share_id}/storage/{node_id}/readnote/
Read a note's content as JSON. Unlike the binary /read/ endpoint, this returns the sanitized markdown content as a string within the JSON response along with the full note resource.
Auth required. Permission: View (workspace), download permission or download token (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Note OpaqueId |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| version_id | string | No | Specific version OpaqueId to read |
| token | string | No | Download token (share only — bypasses JWT auth) |
curl Example
# Workspace
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghij6/readnote/" \
-H "Authorization: Bearer {jwt_token}"
# Share
curl -X GET "https://api.fast.io/current/share/12345678901234567890/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghij6/readnote/" \
-H "Authorization: Bearer {jwt_token}"
# Share with download token (no JWT needed)
curl -X GET "https://api.fast.io/current/share/12345678901234567890/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghij6/readnote/?token={download_token}"
Response
{
"result": "yes",
"response": {
"content": "# Meeting Notes\n\nDiscussed project timeline.",
"note": {
"id": "n5mno-pqrst-uvwxy-z1234-abcde-fghij6",
"type": "note",
"name": "meeting-notes.md",
"parent": "root",
"mimetype": "text/markdown",
"version": "v1",
"created": "2025-01-15T10:30:00Z",
"modified": "2025-01-15T10:30:00Z"
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
content | string | Sanitized markdown content of the note |
note | object | Full note node object (same shape as other node responses) |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 400 | Invalid node ID |
1609 (Not Found) | 404 | Note not found |
1605 (Invalid Input) | 400 | Node is not a note |
1609 (Not Found) | 404 | Note is in trash |
1609 (Not Found) | 404 | Version not found |
1605 (Invalid Input) | 400 | Version does not belong to this note |
1609 (Not Found) | 404 | Version data no longer available |
Update Node
/current/workspace/{workspace_id}/storage/{node_id}/update/
/current/share/{share_id}/storage/{node_id}/update/
Update a node: rename, replace content with a new upload, and/or update custom metadata. Cannot be used on Note nodes (use updatenote instead).
Auth required. Permission: Guest (workspace), file modification permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId |
Request Body (form-encoded)
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| name | string | No | — | New node name |
| from | string | No | JSON-encoded | New file content source (same format as addfile) |
| metadata_title | string | No | Max 50 chars | Custom title override |
| metadata_short | string | No | Max 2048 chars | Custom short description override |
At least one field should be provided.
curl Example (rename)
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/update/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'name=new-document-name.pdf'
curl Example (replace content and update metadata)
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/update/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'from={"type":"upload","upload":{"id":"upload_opaque_id"}}' \
-d 'metadata_title=Updated Report' \
-d 'metadata_short=Q1 2025 revision'
Response
{
"result": "yes",
"response": {
"node": {
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"type": "file",
"name": "new-document-name.pdf",
"parent": "root",
"size": 5242880,
"version": "v2",
"modified": "2025-01-22T11:00:00Z"
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1605 (Invalid Input) | 400 | Cannot update a Note with this endpoint |
1609 (Not Found) | 404 | Node is in trash |
Notes:
- Replacing content creates a new version.
- Renaming a link node propagates the rename to the linked share.
- Custom metadata overrides AI-generated summary values for display.
Move Node
/current/workspace/{workspace_id}/storage/{node_id}/move/
/current/share/{share_id}/storage/{node_id}/move/
Move a node to a different folder within the same storage instance.
Auth required. Permission: Guest (workspace), file modification permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId to move |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| parent | string | Yes | Destination folder OpaqueId or "root" |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/move/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'parent=d1abc-defgh-ijklm-nopqr-stuvw-xyz123'
Response
{"result": "yes"}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Source or destination node not found |
1609 (Not Found) | 404 | Source or destination is in trash |
1605 (Invalid Input) | 400 | Cannot move a folder into itself or its subfolders |
1680 (Access Denied) | 403 | No move permission (share only) |
Notes:
- Conflict resolution: If a file with the same name exists in the destination, the existing file is replaced (moved to trash for rollback). Folder or type-mismatch conflicts fall back to renaming.
Copy Node
/current/workspace/{workspace_id}/storage/{node_id}/copy/
/current/share/{share_id}/storage/{node_id}/copy/
Copy a node to another folder within the same storage instance. Folder copies are recursive.
Auth required. Permission: Guest (workspace), file/folder creation permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId to copy |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| parent | string | Yes | Destination folder OpaqueId or "root" |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/copy/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'parent=d1abc-defgh-ijklm-nopqr-stuvw-xyz123'
Response
{
"result": "yes",
"response": {
"node": {
"id": "f6qrs-tuvwx-yz123-abcde-fghij-klmno7",
"type": "file",
"name": "document.pdf",
"parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz123"
},
"job": null
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Source or destination not found |
1605 (Invalid Input) | 400 | Destination is not a folder |
1609 (Not Found) | 404 | Source or destination is in trash |
Notes:
- Creates a deep copy for folders (all children are copied recursively).
- The copied node gets a new OpaqueId.
- Conflict resolution: If a file with the same name exists in the destination, the existing file is replaced (moved to trash for rollback). Folder or type-mismatch conflicts fall back to renaming.
Transfer Node
/current/workspace/{workspace_id}/storage/{node_id}/transfer/
/current/share/{share_id}/storage/{node_id}/transfer/
Copy or move a node to a different storage instance (e.g., from workspace to share, share to workspace, or share to share). By default the original node remains in place (copy). Use mode=move to copy the node and then trash the source.
Auth required. Permission: Guest on source + write access on destination.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | Source profile ID |
| {node_id} | string | Yes | Node OpaqueId to transfer, or "root" for all |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| instance | string | Yes | 19-digit destination workspace or share profile ID |
| parent | string | Yes | Destination parent folder OpaqueId or "root" |
| mode | string | No | copy (default) or move. When move, the source node is trashed after copying. Cannot use mode=move when {node_id} is "root". |
curl Example (copy, default)
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/transfer/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'instance=98765432109876543210' \
-d 'parent=root'
curl Example (move)
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/transfer/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'instance=98765432109876543210' \
-d 'parent=root' \
-d 'mode=move'
Response (copy)
{
"result": "yes",
"response": {
"node": {
"id": "f7stu-vwxyz-12345-abcde-fghij-klmno8",
"type": "file",
"name": "document.pdf",
"parent": "root"
},
"job": null
}
}
Response (move)
{
"result": "yes",
"response": {
"node": {
"id": "f7stu-vwxyz-12345-abcde-fghij-klmno8",
"type": "file",
"name": "document.pdf",
"parent": "root"
},
"source_trashed": true,
"job": null
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Source node not found or outside scope |
1609 (Not Found) | 404 | Source or destination instance (workspace/share) not found |
1680 (Access Denied) | 403 | No write access to destination |
1605 (Invalid Input) | 400 | mode=move cannot be used with "root" as the source node |
Notes:
- Folder transfers are recursive.
- The user must have write access to both source and destination.
- When
mode=move, the source node is trashed in the source storage instance after the copy completes. The response includes"source_trashed": trueon success.
Delete Node (Move to Trash)
/current/workspace/{workspace_id}/storage/{node_id}/delete/
/current/share/{share_id}/storage/{node_id}/delete/
Move a node to trash. Pass "trash" as the {node_id} to empty the entire trash bin.
Auth required. Permission: Guest (workspace), file modification permission (share). Emptying trash on shares requires admin permission.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId, or "trash" to empty the entire trash |
curl Examples
# Delete a specific node
curl -X DELETE "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/delete/" \
-H "Authorization: Bearer {jwt_token}"
# Empty the trash
curl -X DELETE "https://api.fast.io/current/workspace/12345678901234567890/storage/trash/delete/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"job": null
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1609 (Not Found) | 404 | Node already in trash |
1680 (Access Denied) | 403 | No delete permission (share only) |
1680 (Access Denied) | 403 | No permission to empty trash (share, non-admin) |
Notes:
- Deleting a folder moves it and all children to trash recursively.
- Share delete permissions may be restricted to files the user created.
Purge Node (Permanent Delete)
/current/workspace/{workspace_id}/storage/{node_id}/purge/
/current/share/{share_id}/storage/{node_id}/purge/
Permanently delete a node that is already in trash. Irreversible.
Auth required. Permission: Member (workspace), admin (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | OpaqueId of the trashed node |
curl Example
curl -X DELETE "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/purge/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"job": null
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1605 (Invalid Input) | 400 | Node is not in trash |
1680 (Access Denied) | 403 | Insufficient permission (share, non-admin) |
Restore from Trash
/current/workspace/{workspace_id}/storage/{node_id}/restore/
/current/share/{share_id}/storage/{node_id}/restore/
Restore a trashed node to its original location.
Auth required. Permission: Guest (workspace), file modification + admin (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | OpaqueId of the trashed node |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/restore/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"job": null
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1605 (Invalid Input) | 400 | Node is not in trash |
1680 (Access Denied) | 403 | No restore permission (share only) |
List Versions
/current/workspace/{workspace_id}/storage/{node_id}/versions/
/current/share/{share_id}/storage/{node_id}/versions/
List all versions of a file or note.
Auth required. Permission: View (workspace), file view permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | Node OpaqueId |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/versions/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"versions": [
{
"id": "v8abc-defgh-ijklm-nopqr-stuvw-xyz901",
"version_number": 2,
"created": "2025-01-20T14:45:00Z",
"operation": "update",
"size": 5242880,
"hash": "abc123def456789..."
},
{
"id": "v9bcd-efghi-jklmn-opqrs-tuvwx-yz0123",
"version_number": 1,
"created": "2025-01-15T10:30:00Z",
"operation": "create",
"size": 4194304,
"hash": "def456abc789012..."
}
]
}
}
Response Fields (per version)
| Field | Type | Description |
|---|---|---|
id | string | Version OpaqueId |
version_number | integer | Incrementing version number |
created | string | Version creation timestamp (YYYY-MM-DD HH:MM:SS) |
operation | string | How this version was created: "create", "update", "restore" |
size | integer | File size in bytes for this version |
hash | string | Content hash for this version |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1605 (Invalid Input) | 400 | Unsupported node type |
1665 (Object Init Failed) | 500 | Node data is corrupted |
Restore Version
/current/workspace/{workspace_id}/storage/{node_id}/restore-version/
/current/share/{share_id}/storage/{node_id}/restore-version/
Restore a file to a previous version. Creates a new version pointing to the historical version's content. Both filename and content are restored.
Auth required. Permission: Guest (workspace), file modification permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | File OpaqueId |
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| version_id | string | Yes | OpaqueId of the version to restore |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/restore-version/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'version_id=v9bcd-efghi-jklmn-opqrs-tuvwx-yz0123'
Response
{
"result": "yes",
"response": {
"new_version": {
"id": "v0cde-fghij-klmno-pqrst-uvwxy-z12345",
"version_number": 3,
"created": "2025-01-25T09:00:00Z",
"operation": "restore"
},
"node": {
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"type": "file",
"name": "original-name.pdf",
"version": "v3"
}
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node not found |
1605 (Invalid Input) | 400 | Can only restore file versions (not folders) |
1605 (Invalid Input) | 400 | Cannot restore version of trashed file |
1609 (Not Found) | 404 | Version not found |
1605 (Invalid Input) | 400 | Version does not belong to this file |
1609 (Not Found) | 404 | Version data no longer available |
1680 (Access Denied) | 403 | No permission to restore versions (share only) |
Notes:
- Original versions are preserved; a new version with operation type
"restore"is created. - Both filename and content are restored to the historical version's state.
Download File (Read)
/current/workspace/{workspace_id}/storage/{node_id}/read/
/current/share/{share_id}/storage/{node_id}/read/
Download file content as binary. For notes, returns raw markdown. Supports byte-range requests for partial downloads and video streaming.
Auth: JWT or download token. Permission: View (workspace), download permission (share). With a valid token query parameter, no JWT is required.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | File or note OpaqueId |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| token | string | No | Download token from requestread (bypasses JWT auth) |
| version_id | string | No | Specific version OpaqueId to download |
curl Examples
# Download with JWT auth
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/read/" \
-H "Authorization: Bearer {jwt_token}" \
-o output.pdf
# Download with token (no JWT needed)
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/read/?token={download_token}" \
-o output.pdf
# Download specific version
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/read/?version_id=v9bcd-efghi-jklmn-opqrs-tuvwx-yz0123" \
-H "Authorization: Bearer {jwt_token}" \
-o output_v1.pdf
# Byte-range request (streaming)
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/read/" \
-H "Authorization: Bearer {jwt_token}" \
-H "Range: bytes=0-1023"
Response: Binary file content streamed directly.
- Status
200 OKfor full file,206 Partial Contentfor range requests. - Headers:
Content-Type,Content-Length,Content-Disposition,Accept-Ranges: bytes.
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | File not found |
1605 (Invalid Input) | 400 | Can only read file or note (not folder) |
1609 (Not Found) | 404 | File is in trash |
1609 (Not Found) | 404 | Version not found |
1605 (Invalid Input) | 400 | Version does not belong to this file |
1609 (Not Found) | 404 | Version data no longer available |
1680 (Access Denied) | 403 | File flagged as virus-infected (share only) |
Request Download Token
/current/workspace/{workspace_id}/storage/{node_id}/requestread/
/current/share/{share_id}/storage/{node_id}/requestread/
Generate a temporary auth-free download token.
Auth required. Permission: View (workspace), download permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {node_id} | string | Yes | File or note OpaqueId |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/requestread/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
Usage: Append ?token={token} to the read endpoint to download without an Authorization header. Useful for opening files in browser tabs.
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | File not found |
1605 (Invalid Input) | 400 | Can only read file or note |
1609 (Not Found) | 404 | File is in trash |
1680 (Access Denied) | 403 | No download permission (share only) |
Download Folder as ZIP
/current/workspace/{workspace_id}/storage/{folder_id}/zip/
/current/share/{share_id}/storage/{folder_id}/zip/
Download an entire folder as a streaming ZIP archive.
Auth required. Permission: View (workspace), download permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} or {share_id} | string | Yes | 19-digit profile ID |
| {folder_id} | string | Yes | Folder OpaqueId, or "root" for entire storage |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/root/zip/" \
-H "Authorization: Bearer {jwt_token}" \
-o workspace.zip
Response: Binary ZIP archive streamed directly with Content-Type and Content-Disposition headers.
Notes:
- Uses ZIP64 format — supports archives up to 50GB (plan-dependent limits may be lower).
- Compatible with all modern extraction tools (WinRAR, 7-Zip, macOS Archive Utility, Windows Explorer).
- The archive is streamed; it is not buffered in memory.
- Files are stored without compression for immediate streaming.
- Maximum 10,000 files per archive.
- Rate limited more aggressively than other endpoints due to resource cost.
Recent Files
/current/workspace/{workspace_id}/storage/recent/
/current/share/{share_id}/storage/recent/
List recently modified nodes across all folders, sorted by updated descending. Unlike list which is scoped to a single folder, this endpoint returns nodes from the entire storage tree.
Auth required. Permission: View (workspace), Guest+ (share). Public shares may allow password-only access.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
| page_size | int | 100 | One of: 100, 250, 500 (snapped to nearest) |
| cursor | string | — | Opaque cursor string from previous response |
| type | string | — | Filter by node type: file, folder, link, note |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/recent/?type=file&page_size=250" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"nodes": {
"count": 3,
"items": [
{
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"name": "report.pdf",
"type": "file",
"parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz123",
"size": 2048576,
"updated": "2025-02-18T14:30:00Z",
"created": "2025-02-17T10:00:00Z"
}
]
},
"pagination": {
"has_more": true,
"next_cursor": "eyJsYXN0X3VwZGF0ZWQiOiIyMDI1LTAyLTE4...",
"page_size": 100
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
nodes.count | integer | Number of nodes in this page |
nodes.items | array | Array of node resources |
pagination.has_more | boolean | Whether more pages exist |
pagination.next_cursor | string/null | Cursor for the next page |
pagination.page_size | integer | Effective page size used |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 406 | Invalid pagination cursor |
1680 (Access Denied) | 401 | Insufficient permissions to view files (share only) |
1609 (Not Found) | 404 | Orphaned workspace folder share |
Notes:
- Sort order is always
updated DESCand is not configurable. - Uses cursor-based (keyset) pagination, same as the
listendpoint. - For workspace folder shares, results are post-filtered to the share's subtree.
Search
/current/workspace/{workspace_id}/storage/search/
/current/share/{share_id}/storage/search/
Search files by keyword. Uses keyword and semantic search.
Auth required. Permission: View (workspace), search + file view permissions (share).
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| search | string | Yes | Search query string |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/search/?search=quarterly+report" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"files": {
"f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4": {
"name": "Q4 Report.pdf",
"parent_id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz123",
"type": "file"
},
"f6qrs-tuvwx-yz123-abcde-fghij-klmno7": {
"name": "Quarterly Summary.docx",
"parent_id": "root",
"type": "file"
}
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
files | object | Map of node OpaqueIds to file info |
files.{id}.name | string | File name |
files.{id}.parent_id | string | Parent folder node ID |
files.{id}.type | string | Node type |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Search not available for workspace folder shares (share only) |
1680 (Access Denied) | 403 | No search permission (share only) |
Notes:
- Share search is not available for workspace-backed shares (shared folders).
- Search results are filtered by the user's file view permissions.
QuickShare (Workspace Only)
/current/workspace/{workspace_id}/storage/{node_id}/quickshare/
/current/workspace/{workspace_id}/storage/{node_id}/quickshare/
/current/workspace/{workspace_id}/storage/{node_id}/quickshare/
Create, retrieve, or delete a temporary public link for a single file.
Auth required. Permission: Member.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {workspace_id} | string | Yes | 19-digit workspace profile ID |
| {node_id} | string | Yes | File OpaqueId |
POST — Create or Update QuickShare
Request Body (form-encoded)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| expires | string (digits) | No | 10800 (3 hours) | Expiration in seconds from now (max 604800 = 7 days) |
| expires_at | string | No | — | Absolute expiration datetime. Accepts ISO 8601 (e.g., 2026-03-12T14:30:00Z) or YYYY-MM-DD HH:MM:SS format. Takes precedence over expires. Must be in the future and within 7 days. |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/quickshare/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'expires=604800'
Response
{
"result": "yes",
"response": {
"quickshare": {
"id": "qs_abc123def456",
"node": {
"id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"type": "file",
"name": "presentation.pdf",
"size": 5242880,
"mimetype": "application/pdf"
},
"creator_uid": {
"id": "12345678901234567890",
"email_address": "john@example.com",
"first_name": "John",
"last_name": "Doe"
},
"limit_exceeded": false,
"expires": "2025-01-22 10:30:00",
"created": "2025-01-15 10:30:00"
}
}
}
Error Responses (POST)
| Error Code | HTTP Status | Description |
|---|---|---|
1680 (Access Denied) | 403 | File too large for quickshare |
1605 (Invalid Input) | 400 | Expiration too far in the future |
1685 (Feature Limit) | 403 | Per-workspace QuickShare creation rate limit reached (limits scale with plan tier) |
GET — Get QuickShare Details
Returns the same format as POST. Returns 1609 (Not Found) (404) if no quickshare exists.
DELETE — Delete QuickShare
Returns {"result": "yes"}. Returns 1609 (Not Found) (404) if no quickshare exists.
Public Access Endpoints (No Auth Required)
Once a quickshare is created, these endpoints are accessible without authentication:
GET /current/quickshare/{quickshare_id}/details/ -- metadata and file info
GET /current/quickshare/{quickshare_id}/storage/read/ -- download the file
GET /current/quickshare/{quickshare_id}/storage/preview/{preview_type}/read/ -- preview
GET /current/quickshare/{quickshare_id}/storage/preview/{preview_type}/read/file/{filename} -- preview sub-file
List QuickShares in Workspace
/current/workspace/{workspace_id}/storage/quickshares/list/
Returns an array of all active quickshares in the workspace.
Auth required. Permission: Member.
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/quickshares/list/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"quickshares": [
{
"id": "qs_abc123def456",
"node": { "id": "...", "type": "file", "name": "presentation.pdf" },
"creator_uid": { "id": "...", "email_address": "john@example.com" },
"limit_exceeded": false,
"expires": "2025-01-22 10:30:00",
"created": "2025-01-15 10:30:00"
}
]
}
}
File Locking
Lock a file to prevent concurrent edits. Locks expire automatically if not renewed via heartbeat. Available on both workspace and share storage.
Acquire Lock
/current/workspace/{workspace_id}/storage/{node_id}/lock/
/current/share/{share_id}/storage/{node_id}/lock/
Acquire a lock on a file.
Auth required. Permission: Guest (workspace), file modification permission (share).
Request Body (form-encoded)
| Parameter | Type | Required | Constraints | Description |
|---|---|---|---|---|
| duration | integer | No | 60-3600 seconds | Lock duration (default varies) |
| client_info | string | No | JSON object | Client metadata: device_name (max 255), client_version (max 50) |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/lock/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'duration=300' \
-d 'client_info={"device_name":"My Laptop","client_version":"2.1.0"}'
Response
{
"result": "yes",
"response": {
"lock_token": "unique_lock_token_string",
"locked_at": "2025-01-28T10:00:00+00:00",
"expires_at": "2025-01-28T10:05:00+00:00",
"node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
lock_token | string | Token required for heartbeat and release operations |
locked_at | string | Lock acquisition time, ISO 8601 (YYYY-MM-DDTHH:MM:SS+00:00) |
expires_at | string | Lock expiration time, ISO 8601 (YYYY-MM-DDTHH:MM:SS+00:00) |
node_id | string | OpaqueId of the locked node |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Node does not exist |
1609 (Not Found) | 404 | Cannot lock a deleted node |
1660 (Conflict) | 409 | Node already locked by another user |
Heartbeat (Extend Lock)
/current/workspace/{workspace_id}/storage/{node_id}/lock/heartbeat/
/current/share/{share_id}/storage/{node_id}/lock/heartbeat/
Extend the lock duration.
Auth required.
Request Body (form-encoded)
| Parameter | Type | Required | Description |
|---|---|---|---|
| lock_token | string | Yes | Token from acquire response |
curl Example
curl -X POST "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/lock/heartbeat/" \
-H "Authorization: Bearer {jwt_token}" \
-d 'lock_token=unique_lock_token_string'
Response
{
"result": "yes",
"response": {
"expires_at": "2025-01-28T10:10:00+00:00",
"time_remaining": 300
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | No lock exists on this node |
1609 (Not Found) | 404 | Lock has expired |
1680 (Access Denied) | 403 | Lock token does not match |
Notes:
- Send heartbeats well before the lock expires (e.g., at 50% of lock duration).
- Heartbeats can recreate an expired lock if the token and user match.
Release Lock
/current/workspace/{workspace_id}/storage/{node_id}/lock/
/current/share/{share_id}/storage/{node_id}/lock/
Release a lock on a file.
Auth required.
Request Body/Query
| Parameter | Type | Required | Description |
|---|---|---|---|
| lock_token | string | Yes | Token from acquire response |
Response
{
"result": "yes",
"response": {
"released": true
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | No lock exists on this node |
1680 (Access Denied) | 403 | Lock token does not match |
Lock Status
/current/workspace/{workspace_id}/storage/{node_id}/lock/
/current/share/{share_id}/storage/{node_id}/lock/
Check the lock status of a node.
Auth required.
Response (locked)
{
"result": "yes",
"response": {
"locked": true,
"locked_at": "2025-01-28T10:00:00+00:00",
"expires_at": "2025-01-28T10:05:00+00:00",
"node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4",
"time_remaining": 245,
"locker_uid": "12345678901234567890"
}
}
Response (unlocked)
{
"result": "yes",
"response": {
"locked": false
}
}
Previews
File previews provide rendered views of documents, images, video, and other content without downloading the original file.
Preview Types
| Value | Description |
|---|---|
thumbnail | Small thumbnail image |
image | Full-size image preview |
hlsstream | HLS video/audio stream |
pdf | PDF document preview |
spreadsheet | Spreadsheet preview |
Preview States
Returned in node details responses under previews.{type}.state:
| State | Description |
|---|---|
unknown | Preview status not yet determined |
not possible | File type cannot be previewed |
not generated | Preview not yet generated |
error | Preview generation failed |
in progress | Preview is being generated |
ready | Preview is available |
Preauthorize Preview
/current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/preauthorize/
/current/share/{share_id}/storage/{node_id}/preview/{preview_type}/preauthorize/
Get a preview download URL with an embedded token.
Auth required. Permission: View (workspace), file view permission (share).
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {preview_type} | string | Yes | One of: thumbnail, image, hlsstream, pdf, spreadsheet |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/preview/thumbnail/preauthorize/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"downloadToken": "eyJhbGciOiJIUzI1NiJ9...",
"path": "/current/workspace/.../preview/thumbnail/read/eyJhbGci.../file/preview.png",
"primaryFilename": "preview.png"
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
downloadToken | string | JWT token for preview access |
path | string | Full API path to read the preview file |
primaryFilename | string | Name of the primary preview file |
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | File not found |
1605 (Invalid Input) | 400 | Can only preview file or note |
1609 (Not Found) | 404 | File is in trash |
1652 (Resource Not Found) | 404 | Preview not available |
Read Preview
/current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/read/
/current/share/{share_id}/storage/{node_id}/preview/{preview_type}/read/
Read or redirect to a preview. For single-file previews, streams content directly. For multi-file previews, returns a 307 Temporary Redirect to the file-specific endpoint with a generated token.
If the source file is corrupt, truncated, or otherwise unreadable by the render pipeline, returns HTTP 422 Unprocessable Entity. Clients should not retry — the source file itself is the problem.
Auth required.
Token-Based Preview Read
/current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/read/{token}/file/{filename}
/current/share/{share_id}/storage/{node_id}/preview/{preview_type}/read/{download_token}/file/{filename}
Read a specific preview file using a token (from preauthorize). Token-based auth — no Authorization header needed.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| {token} | string | Yes | Download token from preauthorize |
| {filename} | string | Yes | Preview filename |
Transforms
Image transforms allow on-the-fly resizing, cropping, rotating, and format conversion.
Get Transform Status
/current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/
/current/share/{share_id}/storage/{node_id}/transform/{transform_name}/
Check if a transformation is available without triggering it.
Auth required. Currently the supported {transform_name} is image.
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/transform/image/" \
-H "Authorization: Bearer {jwt_token}"
Response
{
"result": "yes",
"response": {
"state": "rendered"
}
}
Transformation States
| State | Description |
|---|---|
rendered | Transform is ready |
rendering | Transform in progress |
unrendered | Transform not yet requested |
unable to render | Transform failed or unsupported |
Request Transform
/current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/request/
/current/share/{share_id}/storage/{node_id}/transform/{transform_name}/request/
Request a transformation. If not yet rendered, triggers the transformation. If already rendered, returns immediately.
Auth required.
Response
{
"result": "yes",
"response": {
"state": "rendered"
}
}
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1609 (Not Found) | 404 | Unknown transformation name |
1609 (Not Found) | 404 | Unable to transform (failed or unsupported) |
Read Transformed File
/current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/read/
/current/share/{share_id}/storage/{node_id}/transform/{transform_name}/read/
Download the transformed file. Supports byte-range requests and token auth.
Auth: JWT or download token.
Image Transform Parameters
Pass as query parameters on transform read endpoints:
| Parameter | Type | Values |
|---|---|---|
| output-format | string | png, jpg |
| width | int | Target width in pixels |
| height | int | Target height in pixels |
| cropwidth | int | Crop region width |
| cropheight | int | Crop region height |
| cropx | int | Crop region X offset |
| cropy | int | Crop region Y offset |
| rotate | int | 0, 90, 180, 270 |
| size | string | Predefined: IconSmall, IconMedium, Preview |
curl Example
curl -X GET "https://api.fast.io/current/workspace/12345678901234567890/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjfm4/transform/image/read/?width=200&height=200&output-format=jpg" \
-H "Authorization: Bearer {jwt_token}" \
-o thumbnail.jpg
Request Transform Download Token (Workspace Only)
/current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/requestread/
Get a temporary download token for the transformed file.
Auth required.
Response
{
"result": "yes",
"response": {
"token": "eyJhbGciOiJIUzI1NiJ9..."
}
}
Download Tokens Pattern
The requestread endpoint generates temporary, auth-free download tokens for files, previews, and transforms.
Flow:
GET .../storage/{node_id}/requestread/— returns{"token": "..."}GET .../storage/{node_id}/read/?token={token}— download without Authorization header
Useful for opening files in browser tabs or embedding in pages without exposing auth headers.
In medium security mode, file previews are available for guests but direct downloads are restricted. Owners and admins can still download normally.
Metadata Saved Views
Each user can persist per-template column order, column visibility, and sort preferences. Saved views are private to the user — two users can independently customize the same template. Identity is the tuple (workspace, user, template); there is at most one saved view per user per template per workspace.
/current/workspace/{workspace_id}/metadata/view/?template_id={template_id}
/current/workspace/{workspace_id}/metadata/view/
/current/workspace/{workspace_id}/metadata/view/?template_id={template_id}
/current/workspace/{workspace_id}/metadata/views/
Retrieve, upsert, delete, or list the caller's saved views.
Auth required. Permission: Member. Metadata billing feature required.
GET — Retrieve Saved View
Returns the caller's saved view for the supplied template. Responds 1609 (Not Found) (404) when the caller has no view saved for that template.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| template_id | string | Yes | Template OpaqueId to resolve the view against. |
POST — Create or Update Saved View
Upserts the caller's saved view. If a view already exists for this template, the row is replaced in place (its id and created timestamp are preserved; updated is refreshed). Otherwise a new row is created.
Send as application/x-www-form-urlencoded. The config field holds a JSON-encoded string (not a nested JSON body) — sending Content-Type: application/json is rejected with 406 (This field is missing).
Request Body
| Parameter | Type | Required | Description |
|---|---|---|---|
| Content-Type | header | Yes | Must be application/x-www-form-urlencoded. application/json bodies are rejected. |
| template_id | string | Yes | Template OpaqueId the view belongs to. Accepted in the body or the query string. |
| config | string (URL-encoded JSON) | Yes | Form-encoded field whose value is a JSON-serialized string of the view's config object. See Config Schema below for the JSON shape that must be serialized into this field. |
Example
curl -X POST "https://api.fast.io/current/workspace/{workspace_id}/metadata/view/" \
-H "Authorization: Bearer {jwt_token}" \
--data-urlencode "template_id={template_id}" \
--data-urlencode 'config={"version":1,"columns":[{"field":"title","visible":true,"width":180}],"sort":{"field":"updated","dir":"desc"},"filters":[]}'
DELETE — Remove Saved View
Deletes only the caller's own view; other users' views are untouched. Responds 1609 (Not Found) (404) when the caller has no view saved for that template.
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| template_id | string | Yes | Template OpaqueId whose saved view should be removed. |
GET Views — List Saved Views
Lists every saved view the caller owns in this workspace (one entry per template the caller has customized). Views belonging to other users are never returned.
Config Schema
{
"version": 1,
"columns": [
{ "field": "title", "visible": true, "width": 180 },
{ "field": "status", "visible": true, "width": 120 }
],
"sort": { "field": "updated", "dir": "desc" },
"filters": [
{ "field": "status", "operator": "=", "value_type": "string", "value": "active" },
{ "field": "priority", "operator": ">=", "value_type": "int", "value": 3 }
]
}
| Field | Type | Required | Description |
|---|---|---|---|
| version | int | Yes | Schema version. Must equal 1. |
| columns | array | Yes | Ordered list of column entries. The array order IS the display order. |
| columns[].field | string | Yes | Template field name. Non-empty. |
| columns[].visible | bool | No | Whether the column is visible. |
| columns[].width | int | No | Column width in pixels. Positive. |
| sort | object | Yes | Default sort for this view. |
| sort.field | string | Yes | Template field to sort by. |
| sort.dir | string | Yes | asc or desc. |
| filters | array | Yes | Ordered list of filter entries (may be empty). AND-chained; order preserved round-trip. See Filters below. |
| filters[].field | string | Yes | Template field name. Non-empty. Matches the naming used by columns[].field and sort.field. |
| filters[].operator | string | Yes | One of =, !=, <, <=, >, >=. |
| filters[].value_type | string | Yes | One of string, int, float, bool. json is rejected. |
| filters[].value | mixed | Yes | Non-null scalar matching value_type. Cannot be an array or object. |
Unknown top-level keys, unknown columns[] keys, unknown sort keys, or unknown filters[] keys cause the request to be rejected with 1605 (Invalid Input).
Filters
Filter entries in a saved view use {field, operator, value_type, value} — the per-entry field name matches the naming used by columns[].field and sort.field for a uniform identifier across the config.
- Max 5 filter entries per view.
- AND-chained; insertion order is preserved round-trip.
- Numeric value types (
int,float) accept the stringy form ("42") via numeric validation. - For relational operators (
<,<=,>,>=) withvalue_type: string, the server performs lexical comparison. Numeric value types use numeric comparison.
Value coercion. Values follow standard scalar coercion — strings like "true", "1", "yes", "on" (and their falsy counterparts) are accepted when value_type is bool; numeric-form strings like "42" or "1.5" are accepted when value_type is int or float. Sending the typed value directly (e.g. 3 instead of "3") is preferred and round-trips unchanged.
Stateless filters query parameter shape. The template nodes listing endpoint accepts a stateless filters query parameter whose per-entry identifier is still key (not field); that pre-existing query-layer contract is unchanged. Saved View configs use field for internal consistency with columns[].field and sort.field. When the server auto-applies a saved view on the nodes endpoint, it handles the translation internally. Use field when writing a Saved View; use key only when passing a stateless filters query parameter directly on the nodes endpoint.
Response Shape
{
"result": "yes",
"response": {
"id": "{view_id}",
"user_id": "{user_id}",
"template_id": "{template_id}",
"config": { "version": 1, "columns": [], "sort": { "field": "updated", "dir": "desc" }, "filters": [] },
"created": "2026-04-23T11:02:00+00:00",
"updated": "2026-04-23T11:02:00+00:00"
}
}
The list endpoint wraps rows under response.items[] and adds a response.count field.
Auto-Apply on Template Nodes Listing
GET /current/workspace/{workspace_id}/metadata/templates/{template_id}/nodes inspects the caller's saved view for the given template.
- Sort. When the request does not include an explicit
sort_fieldand the caller has a saved view whose config carries asort.field, the server appliessort.field+sort.dirto the listing automatically. Passingsort_fieldin the query always wins. - Filters. When the request does not include a
filtersquery parameter and the caller's saved view carries a non-emptyfiltersarray, the server applies those filters to the listing automatically. Passingfiltersexplicitly (including an empty string or empty array) always wins and bypasses the saved view — no server-side merge is performed. - Partial override.
sort_fieldandfiltersfall back independently. A request that suppliessort_fieldbut omitsfiltersuses the caller's sort with the saved view's filters (and vice versa). Callers that want a stateless listing with neither the saved sort nor the saved filters must supply both explicitly.
Column order and visibility are not applied server-side — clients use the saved view's columns array to decide which columns to render.
Error Responses
| Error Code | HTTP Status | Description |
|---|---|---|
1605 (Invalid Input) | 400 | Missing or malformed parameters, or config failed validation (unknown keys, bad types, bad operator / value_type, or more than 5 filters) |
1609 (Not Found) | 404 | Caller has no saved view for this template (GET / DELETE only) |
1664 (Datastore Error) | 500 | Failed to read or persist the view |
1680 (Access Denied) | 403 | Not a workspace member or metadata feature not enabled |
Workspace-Only Features
These endpoints are only available on workspaces, not shares:
addlink— add a share link to storagecreatenote/readnote/updatenote— markdown notesquickshare— temporary public file linksquickshares/list— list all quicksharestransform/.../requestread/— transform download tokens
Share-Specific Notes
- Share storage follows identical patterns to workspace storage for all common operations
- Shares support file locking (acquire, heartbeat, release, status)
- Shares support previews and transforms (status, request, read)
- Share permissions are granular: separate permissions for file view, download, creation, modification, and administration
- Shares may restrict operations to files the user created (creator-only restrictions)
- Workspace folder shares map
rootto the designated folder and scope all operations to that subtree - Search is not available for workspace folder shares (files are indexed by workspace, not share)
- Public shares may allow listing and downloading without JWT authentication