Storage Operations File and folder operations, locking, previews, and transforms

Base URL: https://api.fast.io/current/ Auth: Bearer JWT

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

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.

LevelFields returned on each node (cumulative)
terseid, 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
standardterse + 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
fullstandard + 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.

FieldTypePresent OnDescription
idstringallOpaqueId of the node
namestringallFile, folder, or note name
typestringall"file", "folder", "note", or "link"
parentstringallParent folder OpaqueId or "root"
sizeintegerfileFile size in bytes
hashstringfileContent hash of the file
hash_algostringfileHash algorithm (e.g., "md5")
mimetypestringfileMIME type (e.g., "application/pdf")
mimecategorystringfileMIME category (e.g., "document", "image")
versionstringfile, noteCurrent version identifier (e.g., "v1")
createdstringallCreation timestamp (YYYY-MM-DD HH:MM:SS UTC)
modifiedstringallLast-modified timestamp (YYYY-MM-DD HH:MM:SS UTC)
restrictedbooleanallWhether the file has been restricted
dmcabooleanallWhether the file has a DMCA flag
lockedbooleanallWhether the node has an active lock
lock_infoobject/nullallLock details when locked; null otherwise
virusobjectfileVirus scan status: {"status": "scanned", "infected": false}
file_attributesobjectfileMedia metadata: width, height, duration (where applicable)
summaryobjectfileAI-generated summary: {"title": "...", "short": "...", "long": "..."}
metadataobject/nullfileUser-defined custom title and description overrides
previewsobjectfilePreview generation state per type (e.g., {"thumbnail": {"state": "ready"}})
aiobjectfileAI processing state: {"state": "...", "attach": true/false}
originobjectfileOrigin info: {"type": "upload", "creator": "{user_id}"}

AI States

ValueDescription
disabledAI processing is disabled for this file
pendingQueued for AI processing
inprogressAI processing is running
readyAI processing complete
failedAI processing failed
indexedFile 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

ParameterTypeDefaultDescription
sort_bystringnameOne of: name, updated, created, type
sort_dirstringascOne of: asc, desc
page_sizeint100One of: 100, 250, 500 (snapped to nearest)
cursorstringOpaque cursor string from previous response

Response Pagination Fields

FieldTypeDescription
pagination.has_morebooleanWhether more pages exist
pagination.next_cursorstring/nullCursor for the next page; null if last page
pagination.page_sizeintegerEffective page size used

Notes:

List Folder Contents

GET /current/workspace/{workspace_id}/storage/{parent_id}/list/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{parent_id}stringYesFolder OpaqueId, "root", or "trash"

Query parameters: See Keyset Pagination section above.

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/root/list/?sort_by=name&sort_dir=asc&page_size=100" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "nodes": {
    "items": [
      {
        "id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
        "type": "folder",
        "name": "Documents",
        "parent": "root",
        "created": "2025-01-01 00:00:00 UTC",
        "modified": "2025-01-20 14:45:00 UTC"
      },
      {
        "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
        "type": "file",
        "name": "photo.jpg",
        "parent": "root",
        "size": 2048000,
        "mimetype": "image/jpeg",
        "created": "2025-01-10 09:00:00 UTC",
        "modified": "2025-01-10 09:00:00 UTC"
      }
    ],
    "path": "/",
    "parent": null
  },
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJwIjoiMmFiYzEyMy4uLiIsInMi...",
    "page_size": 100
  }
}

Response Fields

FieldTypeDescription
nodes.itemsarrayArray of node objects for current page
nodes.pathstringCurrent folder path
nodes.parentstring/nullParent folder ID or null for root
pagination.has_morebooleantrue if more pages exist
pagination.next_cursorstring/nullCursor for next page
pagination.page_sizeintegerActual page size used

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Folder not found
1605 (Invalid Input)400Node is not a folder
1605 (Invalid Input)400Invalid pagination cursor (tampered or mismatched)

Node Details

GET /current/workspace/{workspace_id}/storage/{node_id}/details/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/details/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "format": "single",
  "node": {
    "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
    "type": "file",
    "name": "document.pdf",
    "parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
    "size": 5242880,
    "hash": "d41d8cd98f00b204e9800998ecf8427e",
    "hash_algo": "md5",
    "mimetype": "application/pdf",
    "mimecategory": "document",
    "version": "v1",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-20 14:45:00 UTC",
    "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": "9876543210987654321" }
  }
}

Response fields: See Node Object Schema above for complete field reference.

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1680 (Access Denied)403No permission to view details (share only)

Bulk Form

The details endpoint accepts a comma-separated list of node ids in place of a single {node_id}:

GET /current/workspace/{workspace_id}/storage/{id1},{id2},{id3}/details/

Up to 25 ids per call. Duplicate ids are silently deduplicated. Empty segments (e.g. trailing comma) return 400. The bulk form is currently only available for the workspace path.

The bulk response shape differs from the single-id form. Successfully resolved nodes appear as an array under nodes; per-id failures appear in a parallel errors array. HTTP status is 200 when at least one node resolves and 404 when every requested id errored.

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/abc123,def456,ghi789/details/" \
  -H "Authorization: Bearer {jwt_token}"

Response (HTTP 200)

{
  "result": true,
  "format": "multi",
  "nodes": [
    { "id": "abc123", "type": "file", "name": "report.pdf" },
    { "id": "def456", "type": "folder", "name": "Documents" }
  ],
  "errors": [
    { "node_id": "ghi789", "code": 133123, "message": "No such file or folder exists" }
  ]
}

Every details response carries a top-level format field — "single" (single-id form, paired with a node object) or "multi" (bulk form, paired with nodes + errors arrays). Clients can branch on format instead of inferring shape from key presence.

Bulk Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400Empty segment between commas
1605 (Invalid Input)400More than 25 unique ids in one request
1609 (Not Found)404Every requested id errored (errors array is populated)

Add File from Upload

POST /current/workspace/{workspace_id}/storage/{parent_id}/addfile/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{parent_id}stringYesParent folder OpaqueId or "root"

Request Body (form-encoded)

ParameterTypeRequiredDescription
namestringYesFilename for the new file
fromstringYesJSON-encoded source object (see below)

from format:

Both workspace and share variants accept the same JSON shape. Two source types are supported: upload (a completed upload session) and hash (deduplicate against an existing object by content hash).

{"type": "upload", "upload": {"id": "{upload_id}"}}
{"type": "hash", "hash": {"hash": "{file_hash}", "hash_type": "sha256"}}

A hash source adds the file instantly only when its content already exists in that storage; if the content is not present, the call returns 404 Not Found ("The specified file content was not found in this storage. Upload the file instead.") and the client should upload the file normally instead.

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/root/addfile/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'name=document.pdf' \
  -d 'from={"type":"upload","upload":{"id":"abc123opaqueid"}}'

Response

{
  "result": true,
  "node": {
    "id": "f4kn6-arybw-qxes3-ey9z6-cwoc4-sglh",
    "type": "file",
    "name": "document.pdf",
    "parent": "root",
    "size": 5242880,
    "hash": "d41d8cd98f00b204e9800998ecf8427e",
    "hash_algo": "md5",
    "mimetype": "application/pdf",
    "mimecategory": "document",
    "version": "v1",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-15 10:30:00 UTC",
    "restricted": false,
    "dmca": false,
    "locked": false
  }
}

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400Upload session not found or not associated with your account
1605 (Invalid Input)400Upload is not complete
1609 (Not Found)404Parent folder not found
1605 (Invalid Input)400Parent node is not a folder
1609 (Not Found)404Parent folder is in trash
1605 (Invalid Input)400Name conflict (only when using FAIL strategy)
1693 (Temporarily Unavailable)503Chunk manifest not yet durable — retry after a brief delay

Notes:

POST /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

ParameterTypeRequiredDescription
{workspace_id}stringYes19-digit workspace profile ID
{parent_id}stringYesParent folder OpaqueId or "root"

Request Body (form-encoded)

ParameterTypeRequiredDescription
link_target_typestringYesMust be "share"
sharestringYesShare identifier to link

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/root/addlink/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'link_target_type=share' \
  -d 'share=my-share-name'

Response

{"result": true}

Error Responses

Error CodeHTTP StatusDescription
1683 (Resource Missing)404Share not found or not accessible
1605 (Invalid Input)400Share does not belong to this workspace
1605 (Invalid Input)400A link to this share already exists (only one per share)

Create Folder

POST /current/workspace/{workspace_id}/storage/{parent_id}/createfolder/
POST /current/share/{share_id}/storage/{parent_id}/createfolder/

Create a new folder.

This endpoint is idempotent by default: if a folder with the same name already exists in the parent, the existing folder is returned (HTTP 200) instead of creating a duplicate. Pass force=true to bypass this and always create a new folder (auto-renamed on a name collision, e.g. Documents (2)).

Auth required. Permission: Guest (workspace), folder creation permission (share).

Path Parameters

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{parent_id}stringYesParent folder OpaqueId or "root"

Request Body (form-encoded)

ParameterTypeRequiredDescription
namestringYesFolder name
forcebooleanNoWhen true, always create a new folder even if one with this name already exists (auto-renamed on collision). Defaults to false (idempotent — returns the existing folder).

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/root/createfolder/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'name=Documents'

Response

{
  "result": true,
  "node": {
    "id": "d2bcd-efghi-jklmn-opqrs-tuvwx-yz12",
    "type": "folder",
    "name": "Documents",
    "parent": "root",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-15 10:30:00 UTC",
    "restricted": false,
    "dmca": false,
    "locked": false
  }
}

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400A non-folder item already uses this name in the parent folder
1609 (Not Found)404Parent folder not found
1605 (Invalid Input)400Parent node is not a folder
1609 (Not Found)404Parent folder is in trash
1680 (Access Denied)403No folder creation permission (share only)

Create Note (Workspace Only)

POST /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

ParameterTypeRequiredDescription
{workspace_id}stringYes19-digit workspace profile ID
{parent_id}stringYesParent folder OpaqueId or "root"

Request Body (form-encoded)

ParameterTypeRequiredConstraintsDescription
namestringYesMust end in .mdNote name
contentstringYesMax 100 KBMarkdown content

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/root/createnote/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'name=meeting-notes.md' \
  -d 'content=# Meeting Notes\n\nDiscussed project timeline.'

Response

{
  "result": true,
  "note": {
    "id": "n5mno-pqrst-uvwxy-z1234-abcde-fghi",
    "type": "note",
    "name": "meeting-notes.md",
    "parent": "root",
    "mimetype": "text/markdown",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-15 10:30:00 UTC"
  }
}

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400Filename must end with .md
1605 (Invalid Input)400Duplicate name in parent folder
1609 (Not Found)404Parent folder not found

Update Note (Workspace Only)

POST /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.

This workspace endpoint also accepts a realtime-note bearer token (minted by GET /current/realtime/note-auth/{profile_id}/{note_id}) in place of a user JWT, for the collaborative-editing backend to save on the user's behalf. The token is bound to a specific note and workspace and is rejected (403 Forbidden) if it does not match the requested note or workspace, or if it carries only view permission (a view token cannot update). The share note endpoints do not accept this token.

Path Parameters

ParameterTypeRequiredDescription
{workspace_id}stringYes19-digit workspace profile ID
{node_id}stringYesNote OpaqueId

Request Body (form-encoded)

ParameterTypeRequiredConstraintsDescription
namestringNoMust end in .mdNew note name
contentstringNoMax 100 KB, non-blankNew markdown content (an empty or whitespace-only value is rejected)
if_version_idstringNoVersion OpaqueIdCompare-and-swap precondition. When supplied, the update only proceeds if the note's current version matches this value; otherwise the request is rejected with 409 Conflict and no change is made. Use the version returned by a prior read or update to guard against overwriting concurrent edits.

At least one of name or content is required.

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghi/updatenote/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'content=# Updated Notes\n\nRevised content here.'

Response

{
  "result": true,
  "note": {
    "id": "n5mno-pqrst-uvwxy-z1234-abcde-fghi",
    "type": "note",
    "name": "meeting-notes.md",
    "parent": "root",
    "mimetype": "text/markdown",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-20 14:45:00 UTC"
  }
}

Conflict Response (if_version_id mismatch)

When if_version_id is supplied and does not match the note's current version, the request is rejected with HTTP 409 Conflict, no version is created, and the current state is returned under error.params.current so the client can rebase its edit:

{
  "error": {
    "code": 166226,
    "text": "The note was modified since the supplied version",
    "params": {
      "current": {
        "version_id": "v9abc-defgh-ijklm-nopqr-stuvw-xyz0",
        "hash": "d41d8cd98f00b204e9800998ecf8427e"
      }
    }
  }
}

If the current state cannot be resolved at the time of the conflict, the affected field(s) under current may be omitted.

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Note not found
1605 (Invalid Input)400Node is not a note
1609 (Not Found)404Note is in trash
1605 (Invalid Input)400No content or name provided
1605 (Invalid Input)400Duplicate name in parent folder
1660 (Conflict)409if_version_id did not match the note's current version
1700 (Forbidden)403realtime-note token does not match the requested note or workspace
1700 (Forbidden)403realtime-note token lacks edit permission (a view token cannot update)

Read Note

GET /current/workspace/{workspace_id}/storage/{node_id}/readnote/
GET /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).

The workspace endpoint also accepts a realtime-note bearer token (minted by GET /current/realtime/note-auth/{profile_id}/{note_id}) in place of a user JWT, for the collaborative-editing backend to read on the user's behalf. The token is bound to a specific note and workspace and is rejected (403 Forbidden) if it does not match the requested note or workspace. The share note endpoint does not accept this token.

Path Parameters

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNote OpaqueId

Query Parameters

ParameterTypeRequiredDescription
version_idstringNoSpecific version OpaqueId to read
tokenstringNoDownload token (share only — bypasses JWT auth)

curl Example

# Workspace
curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghi/readnote/" \
  -H "Authorization: Bearer {jwt_token}"

# Share
curl -X GET "https://api.fast.io/current/share/1234567890123456789/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghi/readnote/" \
  -H "Authorization: Bearer {jwt_token}"

# Share with download token (no JWT needed)
curl -X GET "https://api.fast.io/current/share/1234567890123456789/storage/n5mno-pqrst-uvwxy-z1234-abcde-fghi/readnote/?token={download_token}"

Response

{
  "result": true,
  "content": "# Meeting Notes\n\nDiscussed project timeline.",
  "note": {
    "id": "n5mno-pqrst-uvwxy-z1234-abcde-fghi",
    "type": "note",
    "name": "meeting-notes.md",
    "parent": "root",
    "mimetype": "text/markdown",
    "version": "v1",
    "created": "2025-01-15 10:30:00 UTC",
    "modified": "2025-01-15 10:30:00 UTC"
  }
}

Response Fields

FieldTypeDescription
contentstringSanitized markdown content of the note
noteobjectFull note node object (same shape as other node responses)

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400Invalid node ID
1609 (Not Found)404Note not found
1605 (Invalid Input)400Node is not a note
1609 (Not Found)404Note is in trash
1609 (Not Found)404Version not found
1605 (Invalid Input)400Version does not belong to this note
1609 (Not Found)404Version data no longer available
1680 (Access Denied)403No permission to read notes (share only)
1680 (Access Denied)403No permission to read notes you did not create (share, creator-only restriction)
1700 (Forbidden)403realtime-note token does not match the requested note or workspace
1700 (Forbidden)403realtime-note token lacks read permission

Update Node

POST /current/workspace/{workspace_id}/storage/{node_id}/update/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId

Request Body (form-encoded)

ParameterTypeRequiredConstraintsDescription
namestringNoNew node name
fromstringNoJSON-encodedNew file content source (same format as addfile)
metadata_titlestringNoMax 50 charsCustom title override
metadata_shortstringNoMax 2048 charsCustom short description override

At least one field should be provided.

curl Example (rename)

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/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/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/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": true,
  "node": {
    "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
    "type": "file",
    "name": "new-document-name.pdf",
    "parent": "root",
    "size": 5242880,
    "version": "v2",
    "modified": "2025-01-22 11:00:00 UTC"
  }
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1605 (Invalid Input)400Cannot update a Note with this endpoint
1605 (Invalid Input)400No update parameters were specified
1605 (Invalid Input)400Cannot update a folder or link with file data
1605 (Invalid Input)400Name conflict — a file or folder with that name already exists in this location
1693 (Temporarily Unavailable)503Chunk manifest not yet durable when replacing content — retry after a brief delay
1609 (Not Found)404Node is in trash
1680 (Access Denied)401No modify permission (share only)
1680 (Access Denied)401No permission to modify files you did not create (share, creator-only restriction)

Notes:

Move Node

POST /current/workspace/{workspace_id}/storage/{node_id}/move/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId to move

Request Body (form-encoded)

ParameterTypeRequiredDescription
parentstringYesDestination folder OpaqueId or "root"

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/move/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'parent=d1abc-defgh-ijklm-nopqr-stuvw-xyz1'

Response

{"result": true}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Source or destination node not found
1609 (Not Found)404Source or destination is in trash
1605 (Invalid Input)400Cannot move a folder into itself or its subfolders
1680 (Access Denied)401No move permission (share only)
1680 (Access Denied)401No permission to move a folder containing files you cannot modify (recursive move requires modify-all, share only)

Notes:

Copy Node

POST /current/workspace/{workspace_id}/storage/{node_id}/copy/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId to copy

Request Body (form-encoded)

ParameterTypeRequiredDescription
parentstringYesDestination folder OpaqueId or "root"

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/copy/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'parent=d1abc-defgh-ijklm-nopqr-stuvw-xyz1'

Response

{
  "result": true,
  "node": {
    "id": "f6qrs-tuvwx-yz123-abcde-fghij-klmn",
    "type": "file",
    "name": "document.pdf",
    "parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1"
  },
  "job": null
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Source or destination not found
1605 (Invalid Input)400Destination is not a folder
1609 (Not Found)404Source or destination is in trash
1680 (Access Denied)401No permission to view or copy files you did not create (share, creator-only restriction)
1680 (Access Denied)401No permission to copy a folder containing files you cannot view (recursive copy requires view-all, share only)

Notes:

Transfer Node

POST /current/workspace/{workspace_id}/storage/{node_id}/transfer/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYesSource profile ID
{node_id}stringYesNode OpaqueId to transfer, or "root" for all

Request Body (form-encoded)

ParameterTypeRequiredDescription
instancestringYes19-digit destination workspace or share profile ID
parentstringYesDestination parent folder OpaqueId or "root"
modestringNocopy (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/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/transfer/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'instance=9876543210987654321' \
  -d 'parent=root'

curl Example (move)

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/transfer/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'instance=9876543210987654321' \
  -d 'parent=root' \
  -d 'mode=move'

Response (copy)

{
  "result": true,
  "node": {
    "id": "f7stu-vwxyz-12345-abcde-fghij-klmp",
    "type": "file",
    "name": "document.pdf",
    "parent": "root"
  },
  "job": null
}

Response (move)

{
  "result": true,
  "node": {
    "id": "f7stu-vwxyz-12345-abcde-fghij-klmp",
    "type": "file",
    "name": "document.pdf",
    "parent": "root"
  },
  "source_trashed": true,
  "job": null
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Source node not found or outside scope
1609 (Not Found)404Source or destination instance (workspace/share) not found
1680 (Access Denied)401No modify permission on the source node (share only)
1680 (Access Denied)401No permission to modify source files you did not create (share, creator-only restriction)
1680 (Access Denied)401No permission to transfer a whole folder or root (recursive copy requires view-all, move requires modify-all, share only)
1680 (Access Denied)401No write access to destination -- the caller lacks file-creation permission on the destination share (e.g. a send-type share where guests cannot create)
1680 (Access Denied)401Destination is a password-protected public share and a valid share password was not supplied
1680 (Access Denied)401Writing into a personal (user-owned) share requires an identified caller with creation permission (an unauthenticated caller is denied)
1680 (Access Denied)401The request token's scope does not cover the destination share (a scoped token must include the destination share, or its parent workspace/org)
1605 (Invalid Input)400Name conflict at destination, or unsupported/not-allowed transfer
1605 (Invalid Input)400mode=move cannot be used with "root" as the source node

Notes:

Delete Node (Move to Trash)

DELETE /current/workspace/{workspace_id}/storage/{node_id}/delete/
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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId, or "trash" to empty the entire trash

curl Examples

# Delete a specific node
curl -X DELETE "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/delete/" \
  -H "Authorization: Bearer {jwt_token}"

# Empty the trash
curl -X DELETE "https://api.fast.io/current/workspace/1234567890123456789/storage/trash/delete/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "job": null
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1609 (Not Found)404Node already in trash
1680 (Access Denied)403No delete permission (share only)
1680 (Access Denied)403No permission to empty trash (share, non-admin)

Notes:

Purge Node (Permanent Delete)

DELETE /current/workspace/{workspace_id}/storage/{node_id}/purge/
DELETE /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesOpaqueId of the trashed node

curl Example

curl -X DELETE "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/purge/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "job": null
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1605 (Invalid Input)400Node is not in trash
1680 (Access Denied)403Insufficient permission (share, non-admin)

Restore from Trash

POST /current/workspace/{workspace_id}/storage/{node_id}/restore/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesOpaqueId of the trashed node

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/restore/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "job": null
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1605 (Invalid Input)400Node is not in trash
1605 (Invalid Input)400Node is inside a trashed folder (restore the parent folder instead)
1680 (Access Denied)403No restore permission (share only)

List Versions

GET /current/workspace/{workspace_id}/storage/{node_id}/versions/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesNode OpaqueId

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/versions/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "versions": [
    {
      "id": "v8abc-defgh-ijklm-nopqr-stuvw-xyz9",
      "version_number": 2,
      "created": "2025-01-20 14:45:00 UTC",
      "operation": "update",
      "size": 5242880,
      "hash": "abc123def456789..."
    },
    {
      "id": "v9bcd-efghi-jklmn-opqrs-tuvwx-yz01",
      "version_number": 1,
      "created": "2025-01-15 10:30:00 UTC",
      "operation": "create",
      "size": 4194304,
      "hash": "def456abc789012..."
    }
  ]
}

Response Fields (per version)

FieldTypeDescription
idstringVersion OpaqueId
version_numberintegerIncrementing version number
createdstringVersion creation timestamp (YYYY-MM-DD HH:MM:SS UTC)
operationstringHow this version was created: "create", "update", "restore"
sizeintegerFile size in bytes for this version
hashstringContent hash for this version

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1605 (Invalid Input)400Unsupported node type
1665 (Object Init Failed)500Node data is corrupted
1680 (Access Denied)401No permission to view file versions (share only)
1680 (Access Denied)401No permission to view versions of files you did not create (share, creator-only restriction)

Notes:

Restore Version

POST /current/workspace/{workspace_id}/storage/{node_id}/restore-version/
POST /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesFile OpaqueId

Request Body (form-encoded)

ParameterTypeRequiredDescription
version_idstringYesOpaqueId of the version to restore

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/restore-version/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'version_id=v9bcd-efghi-jklmn-opqrs-tuvwx-yz01'

Response

{
  "result": true,
  "new_version": {
    "id": "v0cde-fghij-klmno-pqrst-uvwxy-z123",
    "version_number": 3,
    "created": "2025-01-25 09:00:00 UTC",
    "operation": "restore"
  },
  "node": {
    "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
    "type": "file",
    "name": "original-name.pdf",
    "version": "v3"
  }
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found
1605 (Invalid Input)400Can only restore file versions (not folders)
1605 (Invalid Input)400Cannot restore version of trashed file
1609 (Not Found)404Version not found
1605 (Invalid Input)400Version does not belong to this file
1609 (Not Found)404Version data no longer available
1680 (Access Denied)403No permission to restore versions (share only)

Notes:

Download File (Read)

GET /current/workspace/{workspace_id}/storage/{node_id}/read/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesFile or note OpaqueId

Query Parameters

ParameterTypeRequiredDescription
tokenstringNoDownload token from requestread (bypasses JWT auth)
version_idstringNoSpecific version OpaqueId to download

curl Examples

# Download with JWT auth
curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/read/" \
  -H "Authorization: Bearer {jwt_token}" \
  -o output.pdf

# Download with token (no JWT needed)
curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/read/?token={download_token}" \
  -o output.pdf

# Download specific version
curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/read/?version_id=v9bcd-efghi-jklmn-opqrs-tuvwx-yz01" \
  -H "Authorization: Bearer {jwt_token}" \
  -o output_v1.pdf

# Byte-range request (streaming)
curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/read/" \
  -H "Authorization: Bearer {jwt_token}" \
  -H "Range: bytes=0-1023"

Response: Binary file content streamed directly.

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404File not found
1605 (Invalid Input)400Can only read file or note (not folder)
1609 (Not Found)404File is in trash
1609 (Not Found)404Version not found
1605 (Invalid Input)400Version does not belong to this file
1609 (Not Found)404Version data no longer available
1680 (Access Denied)403File flagged as virus-infected (share only)

Request Download Token

GET /current/workspace/{workspace_id}/storage/{node_id}/requestread/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{node_id}stringYesFile or note OpaqueId

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/requestread/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "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 CodeHTTP StatusDescription
1609 (Not Found)404File not found
1605 (Invalid Input)400Can only read file or note
1609 (Not Found)404File is in trash
1680 (Access Denied)403No download permission (share only)

Download Folder as ZIP

GET /current/workspace/{workspace_id}/storage/{folder_id}/zip/
GET /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

ParameterTypeRequiredDescription
{workspace_id} or {share_id}stringYes19-digit profile ID
{folder_id}stringYesFolder OpaqueId, or "root" for entire storage

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/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:

Recent Files

GET /current/workspace/{workspace_id}/storage/recent/
GET /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

ParameterTypeDefaultDescription
page_sizeint100One of: 100, 250, 500 (snapped to nearest)
cursorstringOpaque cursor string from previous response
typestringFilter by node type: file, folder, link, note

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/recent/?type=file&page_size=250" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "nodes": {
    "count": 3,
    "items": [
      {
        "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
        "name": "report.pdf",
        "type": "file",
        "parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
        "size": 2048576,
        "updated": "2025-02-18 14:30:00 UTC",
        "created": "2025-02-17 10:00:00 UTC"
      }
    ]
  },
  "pagination": {
    "has_more": true,
    "next_cursor": "eyJsYXN0X3VwZGF0ZWQiOiIyMDI1LTAyLTE4...",
    "page_size": 100
  }
}

Response Fields

FieldTypeDescription
nodes.countintegerNumber of nodes in this page
nodes.itemsarrayArray of node resources
pagination.has_morebooleanWhether more pages exist
pagination.next_cursorstring/nullCursor for the next page
pagination.page_sizeintegerEffective page size used

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400Invalid pagination cursor
1680 (Access Denied)403Insufficient permissions to view files (share only)
1609 (Not Found)404Orphaned workspace folder share

Notes:

GET /current/workspace/{workspace_id}/storage/search/
GET /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

ParameterTypeRequiredDescription
searchstringYesSearch query string
outputstringNoVerbosity: terse, standard, or full (default). Trims content_snippet to a byte budget when intelligence is enabled. See Verbosity below.

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/search/?search=quarterly+report" \
  -H "Authorization: Bearer {jwt_token}"

Response (keyword-only, intelligence disabled)

{
  "result": true,
  "files": {
    "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf": {
      "name": "Q4 Report.pdf",
      "parent_id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
      "type": "file"
    }
  }
}

Response (hybrid, intelligence enabled)

{
  "result": true,
  "files": {
    "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf": {
      "name": "Q4 Report.pdf",
      "parent_id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
      "type": "file",
      "relevance_score": 0.93,
      "content_snippet": "Quarterly revenue grew 18% year-over-year …",
      "match_source": "both",
      "media_segment": null,
      "mimetype": "application/pdf",
      "page": { "start_page": 3, "end_page": 3 }
    }
  },
  "search_metadata": {
    "intelligence_enabled": true,
    "semantic_available": true,
    "scoped": false
  }
}

Response Fields

FieldTypeDescription
filesobjectMap of node OpaqueIds to file info
files.{id}.namestringFile name
files.{id}.parent_idstringParent folder node ID
files.{id}.typestringNode type
files.{id}.relevance_scorefloatRelevance score (0.0–1.0). Hybrid mode only.
files.{id}.content_snippetstring|nullMatching text from the semantic chunk; null for keyword-only matches. Trimmed per output. Hybrid mode only.
files.{id}.match_sourcestringkeyword, semantic, or both. Hybrid mode only.
files.{id}.media_segmentobject|null{start_seconds, end_seconds} for audio/video matches. Hybrid mode only.
files.{id}.mimetypestring|nullFile MIME type. Hybrid mode only.
files.{id}.pageobject|null{start_page, end_page} for paginated documents. Hybrid mode only.
search_metadataobject{intelligence_enabled, semantic_available, scoped}. Hybrid mode only.

Verbosity (output)

content_snippet is the only field affected by output=. All other fields are returned unchanged at every level.

outputcontent_snippet shape
terseFirst ~200 bytes of the matching chunk, UTF-8 safe. Truncated values end with .
standardFirst ~600 bytes (roughly one paragraph). Same suffix when truncated.
full (default)Full matching chunk, untrimmed.

Snippets shorter than the budget are returned unchanged (no padding, no ). Null/empty snippets are returned unchanged at every level. The byte budget is inclusive of the trailing so the wire payload never exceeds the cap. Default output=full preserves prior behavior for callers that omit the parameter.

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Search not available for workspace folder shares (share only)
1680 (Access Denied)403No search permission (share only)

Notes:

GET /current/workspace/{workspace_id}/metadata/search/

Keyword search across the metadata key-value pairs stored on workspace files (template fields plus custom fields). Returns matching nodes ordered by relevance. Trashed nodes are filtered out automatically. The endpoint is workspace-scoped — results never cross workspace boundaries regardless of caller input.

Auth required. Permission: View (workspace member).

Query Parameters

ParameterTypeRequiredDefaultDescription
qstringYesKeyword query. Whitespace-trimmed; an empty value is rejected.
template_idstringNoRestrict matches to nodes that have at least one metadata value contributed by this template. Custom (template-independent) fields are not included when this filter is set.
limitintNo100Maximum number of results (1-100).
offsetintNo0Number of results to skip. Requests beyond the supported result window return an input error.

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/metadata/search/?q=invoice&limit=25" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "results": [
    {
      "node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
      "score": 4.215,
      "template_ids": ["mt_abc123def456"],
      "node": {
        "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
        "type": "file",
        "name": "Invoice-2026-0042.pdf",
        "parent": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
        "size": 524288,
        "mimetype": "application/pdf",
        "mimecategory": "document",
        "version": "v1",
        "created": "2026-04-20 14:12:08 UTC",
        "modified": "2026-04-22 09:30:55 UTC",
        "restricted": false,
        "dmca": false,
        "locked": false
      }
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 25,
    "offset": 0,
    "has_more": false
  }
}

Response Fields

FieldTypeDescription
resultsarrayMatching nodes, ordered by relevance score (highest first)
results[].node_idstringOpaqueId of the matching node
results[].scorenumberRelevance score for the match
results[].template_idsarray of stringTemplates contributing to the matching values for this node. Custom (template-independent) values do not appear here.
results[].nodeobjectStandard node resource (same shape as storage list/details)
pagination.totalintTotal number of matching nodes
pagination.has_morebooltrue when more results exist past the current window

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400q missing/blank, template_id malformed, or a request beyond the supported result window
1680 (Access Denied)403Caller is not a member of the workspace
1654 (Internal Error)500Search backend transient failure

Notes:

GET /current/workspace/{workspace_id}/search/
GET /current/share/{share_id}/search/

One search call across everything in a workspace or share, with results grouped by type into buckets. Instead of calling the per-type search endpoints separately, you issue a single query and get back a set of buckets — each with its own results and its own pagination. A workspace search returns up to four buckets: files, metadata, comments, and workflows. A share search returns the subset that applies to shares (typically files, plus comments when commenting is enabled on the share; metadata is workspace-only).

Each bucket is independently paginated and independently health-reported, so a transient problem affecting one bucket never blocks the others — that bucket comes back degraded with an empty result set while the rest return normally.

Auth required. Permission: View (workspace), search + file view permissions (share). All buckets are permission-filtered — see Permission Model below.

Query Parameters

ParameterTypeRequiredDefaultDescription
searchstringYesSearch query string (max 1024 characters; an empty value is rejected).
files_offsetintNo0Result offset for the files bucket.
files_limitintNo25Page size for the files bucket.
metadata_offsetintNo0Result offset for the metadata bucket (workspace only).
metadata_limitintNo25Page size for the metadata bucket (workspace only).
comments_offsetintNo0Result offset for the comments bucket.
comments_limitintNo25Page size for the comments bucket.
workflows_offsetintNo0Result offset for the workflows bucket.
workflows_limitintNo25Page size for the workflows bucket.

Pagination is per bucket: files_offset/files_limit page the files bucket, comments_offset/comments_limit page the comments bucket, and so on. Each pair is optional; an omitted offset defaults to 0 and an omitted limit to 25. A request beyond the supported result window for any bucket returns a 400 input error. Every applicable bucket is always searched (the share endpoint omits metadata).

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/search/?search=quarterly+report&files_limit=10&comments_limit=5" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "buckets": {
    "files": {
      "items": [
        {
          "node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
          "name": "Q4 Report.pdf",
          "parent_id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
          "type": "file",
          "relevance_score": 0.93,
          "content_snippet": "Quarterly revenue grew 18% year-over-year …",
          "match_source": "both",
          "media_segment": null,
          "page": { "start_page": 3, "end_page": 3 },
          "mimetype": "application/pdf",
          "updated": "2026-04-22 09:30:55 UTC"
        }
      ],
      "offset": 0,
      "limit": 10,
      "total": 1,
      "total_relation": "eq",
      "has_more": false,
      "status": "ok"
    },
    "metadata": {
      "items": [
        {
          "node_id": "f7kp2-aaaaa-bbbbb-ccccc-ddddd-eeee",
          "name": "Invoice-2026-0042.pdf",
          "parent_id": "d1abc-defgh-ijklm-nopqr-stuvw-xyz1",
          "type": "file",
          "relevance_score": 4.215,
          "template_ids": ["mt_abc123def456"],
          "updated": "2026-04-22 09:30:55 UTC"
        }
      ],
      "offset": 0,
      "limit": 25,
      "total": 1,
      "total_relation": "eq",
      "has_more": false,
      "status": "ok"
    },
    "comments": {
      "items": [
        {
          "comment_id": "c9xy1-zzzzz-yyyyy-xxxxx-wwwww-vvvv",
          "entity": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
          "author_profile_id": "1234567890123456789",
          "snippet": "Can we double-check the Q4 totals …",
          "reference_type": null,
          "relevance_score": 2.118,
          "created": "2026-04-21 08:02:11 UTC",
          "updated": "2026-04-21 08:02:11 UTC"
        }
      ],
      "offset": 0,
      "limit": 5,
      "total": 1,
      "total_relation": "eq",
      "has_more": false,
      "status": "ok"
    },
    "workflows": {
      "items": [
        {
          "entity_type": "task",
          "entity_id": "t4aa0-bbbbb-ccccc-ddddd-eeeee-ffff",
          "relevance_score": 1.904,
          "title": "Review Q4 report",
          "description": "Confirm the revenue figures before sign-off …",
          "status": "open",
          "updated": "2026-04-21 12:30:00 UTC"
        }
      ],
      "offset": 0,
      "limit": 25,
      "total": 1,
      "total_relation": "eq",
      "has_more": false,
      "status": "ok"
    }
  }
}

Response Fields

FieldTypeDescription
bucketsobjectMap of bucket type → bucket object. Only applicable buckets are present (e.g. no metadata on a share).
buckets.{type}.itemsarrayResult items for this bucket, ordered by relevance (relevance_score descending). Item shape is type-specific — see Bucket Item Shapes below.
buckets.{type}.offsetintThe offset applied to this bucket.
buckets.{type}.limitintThe page size applied to this bucket.
buckets.{type}.totalintNumber of matching, permission-visible items. This count is computed after permission filtering — it reflects what you can actually see, not raw index hits.
buckets.{type}.total_relationstringeq when total is exact within the searched window, or gte when it is a lower bound (more visible matches may exist beyond the searched window).
buckets.{type}.has_morebooltrue when more results exist past the current page.
buckets.{type}.statusstringok for a healthy bucket, or degraded when the backend behind that bucket was temporarily unavailable (the bucket returns an empty items array but is still present so you can tell a backend hiccup from a bucket that does not apply).

Bucket Item Shapes

Every item carries a relevance_score (higher is more relevant) and an updated timestamp. Beyond that, fields are type-specific:

Permission Model

Every bucket is permission-filtered against the same rules as that type's dedicated endpoint. Results are produced by reading each match from the source of truth and re-checking the caller's permission before any content or count is returned, so search can never reveal an item, a snippet, or even a count for something the caller is not allowed to see. In particular:

Error Responses

Error CodeHTTP StatusDescription
1605 (Invalid Input)400search missing/blank/too long, or a bucket request beyond the supported result window
1609 (Not Found)404Search not available for workspace-backed shares (shared folders); share only
1680 (Access Denied)403No search permission

Notes:

QuickShare (Workspace Only)

Deprecated — use File Share. The POST (create / extend-expiry) path now returns 10756 (Quickshare Deprecated) (403) with a directed message pointing to POST /current/workspace/{workspace_id}/create/fileshare/. The durable File Share replaces it. GET (details), DELETE (revoke), and the public read endpoints below remain live during the drain so existing links keep serving and can be torn down.

POST /current/workspace/{workspace_id}/storage/{node_id}/quickshare/
GET /current/workspace/{workspace_id}/storage/{node_id}/quickshare/
DELETE /current/workspace/{workspace_id}/storage/{node_id}/quickshare/

Retrieve or delete an existing temporary public link for a single file. Creation is deprecated (POST returns 403).

Auth required. Permission: Member.

Path Parameters

ParameterTypeRequiredDescription
{workspace_id}stringYes19-digit workspace profile ID
{node_id}stringYesFile OpaqueId

POST — Create or Update QuickShare (deprecated → 403)

This path is deprecated and returns 10756 (Quickshare Deprecated) (403) for both create and extend-expiry. Use POST /current/workspace/{workspace_id}/create/fileshare/ (see File Share). The legacy expires / expires_at request parameters no longer apply because creation is closed.

GET — Get QuickShare Details

Returns the same format as POST. Returns 1609 (Not Found) (404) if no quickshare exists.

DELETE — Delete QuickShare

Returns {"result": true}. 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

GET /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/1234567890123456789/storage/quickshares/list/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "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 UTC",
      "created": "2025-01-15 10:30:00 UTC"
    }
  ]
}

File Share Read Endpoints (Public)

A File Share is the durable successor to QuickShare — a long-lived, link-shareable view of one workspace file. These public read endpoints serve the link viewer. They are anonymous-allowed where the access tier (anyone_with_link) permits; for any_registered / named_people the caller must present a bearer token and have sufficient access. A link password, if set, is presented via the x-ve-password request header (never in the URL). The bound file is read from the File Share record, so a caller can never substitute a different node id. Bandwidth is metered to the owning organization (no per-link transfer cap). Management endpoints (create / list / update / delete / grants) are in the Workspaces reference.

GET /current/fileshare/{fileshare_id}/details/                                       -- viewer metadata + bound file info
GET /current/fileshare/{fileshare_id}/storage/metadata/details/                      -- the bound file's template + custom metadata fields (view capability)
GET /current/fileshare/{fileshare_id}/storage/read/                                  -- download the bound file (download capability)
GET /current/fileshare/{fileshare_id}/storage/preview/{preview_type}/read/           -- preview (view capability)
GET /current/fileshare/{fileshare_id}/storage/preview/{preview_type}/read/{download_token}/file/{filename}  -- preview sub-file
GET /current/fileshare/{fileshare_id}/storage/versions/                              -- list the bound file's version history (view capability)
GET /current/fileshare/{fileshare_id}/storage/versions/{version_id}/read/            -- download a specific version (download capability)

Details Response

{
  "result": true,
  "fileshare": {
    "fileshare": "1234567890123456789",
    "title": "Quarterly Presentation",
    "access_option": "anyone_with_link",
    "has_password": false,
    "effective_capability": "download",
    "file": {
      "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
      "type": "file",
      "name": "presentation.pdf",
      "parent": "a1b2c-3d4e5-6f7g8-9h0i1-2j3k4-lmno",
      "version": "3u6cr-vxmyl-4y2pr-5jboz-afoke-k4s5",
      "created": "2026-01-28 10:00:00 UTC",
      "modified": "2026-01-28 12:30:00 UTC",
      "size": 5242880,
      "hash": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "hash_algo": "sha256",
      "mimetype": "application/pdf",
      "mimecategory": "document",
      "previews": { "pdf": { "state": "ready" }, "thumbnail": { "state": "ready" } },
      "virus": { "status": "scanned", "infected": false },
      "summary": { "title": "Quarterly Presentation", "short": "Q4 results deck", "long": "..." },
      "metadata": { "title": null, "short": null }
    }
  }
}

The bound file object carries the same fields as a workspace file-node detail (see Node Details in this reference): a viewer of the share sees that one file's full info — versioning, previews, summary, metadata, and provenance — exactly as it appears in the workspace. The share-level effective_capability (view / download / edit) reflects the highest capability the access tier / grant / password admit for the calling viewer.

Metadata Response

GET /current/fileshare/{fileshare_id}/storage/metadata/details/ returns the bound file's template + custom metadata key-value fields — the same template_metadata / custom_metadata shape as the workspace metadata endpoint, so a viewer sees that one file's metadata exactly as it appears in the workspace info panel. View capability; the node is the bound file (never a caller-supplied node id).

{
  "result": true,
  "object_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
  "template_id": "tmpl4-q2w3e-r5t6y-u7i8o-p9a0s-dfgh",
  "node_id": { "id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf", "type": "file", "name": "presentation.pdf", "parent": "a1b2c-3d4e5-6f7g8-9h0i1-2j3k4-lmno", "mimetype": "application/pdf" },
  "template_metadata": [
    { "key": "department", "description": "Owning department", "type": "string", "value": "Finance", "is_auto": false, "updated": "2026-01-28 12:30:00 UTC" }
  ],
  "custom_metadata": [
    { "key": "reviewed_by", "description": null, "type": "string", "value": "jdoe", "is_auto": false, "updated": "2026-01-28 12:31:00 UTC" }
  ],
  "autoextractable": true
}

When the bound file carries no metadata (or the owning organization has no metadata feature), template_metadata and custom_metadata are empty arrays and template_id is null.

Preview types match the storage preview surface (thumbnail, image, pdf, mp4, hls_stream, etc.); multi-file previews (e.g. HLS) return a 307 Temporary Redirect to a sub-file endpoint. Version listing is read-only — there is no restore/promote on the public surface; each entry carries the same per-version fields as a workspace version listing.

Error Responses

HTTP StatusCodeCause
4001605 (Invalid Input)Invalid File Share id
4011650 (Authentication Invalid)A link password is required and was missing or wrong (present it via the x-ve-password header)
4031700 (Forbidden)The access tier or named grant does not permit the caller
4041609 (Not Found)No such File Share, or the bound file content is no longer available

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

POST /current/workspace/{workspace_id}/storage/{node_id}/lock/
POST /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)

ParameterTypeRequiredConstraintsDescription
durationintegerNo60-3600 secondsLock duration (default varies)
client_infostringNoJSON objectClient metadata: device_name (max 255), client_version (max 50)

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/lock/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'duration=300' \
  -d 'client_info={"device_name":"My Laptop","client_version":"2.1.0"}'

Response

{
  "result": true,
  "lock_token": "unique_lock_token_string",
  "locked_at": "2025-01-28 10:00:00 UTC",
  "expires_at": "2025-01-28 10:05:00 UTC",
  "node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf"
}

Response Fields

FieldTypeDescription
lock_tokenstringToken required for heartbeat and release operations
locked_atstringLock acquisition time (YYYY-MM-DD HH:MM:SS UTC)
expires_atstringLock expiration time (YYYY-MM-DD HH:MM:SS UTC)
node_idstringOpaqueId of the locked node

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found or outside your workspace/share scope
1609 (Not Found)404Cannot lock a deleted node (workspace only)
1660 (Conflict)409Node already locked by another user
1680 (Access Denied)401Insufficient permission to acquire a lock on this node (share only)
1693 (Temporarily Unavailable)503Lock service momentarily unavailable; retry after a brief delay

Heartbeat (Extend Lock)

POST /current/workspace/{workspace_id}/storage/{node_id}/lock/heartbeat/
POST /current/share/{share_id}/storage/{node_id}/lock/heartbeat/

Extend the lock duration.

Auth required. Permission: Guest (workspace), file modification permission (share).

Request Body (form-encoded)

ParameterTypeRequiredDescription
lock_tokenstringYesToken from acquire response

curl Example

curl -X POST "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/lock/heartbeat/" \
  -H "Authorization: Bearer {jwt_token}" \
  -d 'lock_token=unique_lock_token_string'

Response

{
  "result": true,
  "expires_at": "2025-01-28 10:10:00 UTC",
  "time_remaining": 300
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404No lock exists on this node
1609 (Not Found)404Lock has expired
1609 (Not Found)404Node not found or outside your workspace/share scope
1660 (Conflict)409Recreation race: another caller acquired the lock first; re-acquire and retry
1680 (Access Denied)401Lock token does not match
1680 (Access Denied)401Insufficient permission to heartbeat this lock (share only)
1693 (Temporarily Unavailable)503Lock service momentarily unavailable; retry after a brief delay

Notes:

Release Lock

DELETE /current/workspace/{workspace_id}/storage/{node_id}/lock/
DELETE /current/share/{share_id}/storage/{node_id}/lock/

Release a lock on a file.

Auth required. Permission: Guest (workspace), file modification permission (share).

Request Body/Query

ParameterTypeRequiredDescription
lock_tokenstringYesToken from acquire response

Response

{
  "result": true,
  "released": true
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404No lock exists on this node
1609 (Not Found)404Node not found or outside your workspace/share scope
1680 (Access Denied)401Lock token does not match
1680 (Access Denied)401Insufficient permission to release this lock (share only)
1693 (Temporarily Unavailable)503Lock service momentarily unavailable; retry after a brief delay

Lock Status

GET /current/workspace/{workspace_id}/storage/{node_id}/lock/
GET /current/share/{share_id}/storage/{node_id}/lock/

Check the lock status of a node.

Auth required. Permission: Guest (workspace), file view permission (share).

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Node not found or outside your workspace/share scope
1680 (Access Denied)401Insufficient permission to view lock status (share only)

Response (locked)

{
  "result": true,
  "locked": true,
  "locked_at": "2025-01-28 10:00:00 UTC",
  "expires_at": "2025-01-28 10:05:00 UTC",
  "node_id": "f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf",
  "time_remaining": 245,
  "locker_uid": "1234567890123456789"
}

Response (unlocked)

{
  "result": true,
  "locked": false
}

Previews

File previews provide rendered views of documents, images, video, and other content without downloading the original file.

Preview Types

ValueDescription
thumbnailSmall thumbnail image
imageFull-size image preview
mp4MP4 video preview (transcoded)
hlsstreamHLS video/audio stream
audioAudio preview (transcoded)
pdfPDF document preview
spreadsheetSpreadsheet preview
binBinary preview (raw bytes for clients that render their own preview)

Preview States

Returned in node details responses under previews.{type}.state:

StateDescription
unknownPreview status not yet determined
not possibleFile type cannot be previewed
not generatedPreview not yet generated
errorPreview generation failed
in progressPreview is being generated
readyPreview is available

Preauthorize Preview

GET /current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/preauthorize/
GET /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

ParameterTypeRequiredDescription
{preview_type}stringYesOne of: thumbnail, image, mp4, hlsstream, audio, pdf, spreadsheet, bin

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/preview/thumbnail/preauthorize/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "downloadToken": "eyJhbGciOiJIUzI1NiJ9...",
  "path": "/current/workspace/.../preview/thumbnail/read/eyJhbGci.../file/preview.png",
  "primaryFilename": "preview.png"
}

Response Fields

FieldTypeDescription
downloadTokenstringJWT token for preview access
pathstringFull API path to read the preview file
primaryFilenamestringName of the primary preview file

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404File not found
1605 (Invalid Input)400Can only preview file or note
1609 (Not Found)404File is in trash
1652 (Resource Not Found)404File content is no longer available
1652 (Resource Not Found)404Preview not available

Read Preview

GET /current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/read/
GET /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

GET /current/workspace/{workspace_id}/storage/{node_id}/preview/{preview_type}/read/{token}/file/{filename}
GET /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

ParameterTypeRequiredDescription
{token}stringYesDownload token from preauthorize
{filename}stringYesPreview filename

Transforms

Image transforms allow on-the-fly resizing, cropping, rotating, and format conversion.

Get Transform Status

GET /current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/
GET /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/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/transform/image/" \
  -H "Authorization: Bearer {jwt_token}"

Response

{
  "result": true,
  "state": "rendered"
}

Transformation States

StateDescription
renderedTransform is ready
renderingTransform in progress
unrenderedTransform not yet requested
unable to renderTransform failed or unsupported

Request Transform

POST /current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/request/
POST /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": true,
  "state": "rendered"
}

Error Responses

Error CodeHTTP StatusDescription
1609 (Not Found)404Unknown transformation name
1609 (Not Found)404Unable to transform (failed or unsupported)

Read Transformed File

GET /current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/read/
GET /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:

ParameterTypeValues
output-formatstringpng, jpg
widthintTarget width in pixels
heightintTarget height in pixels
cropwidthintCrop region width
cropheightintCrop region height
cropxintCrop region X offset
cropyintCrop region Y offset
rotateint0, 90, 180, 270
sizestringPredefined: IconSmall, IconMedium, Preview

curl Example

curl -X GET "https://api.fast.io/current/workspace/1234567890123456789/storage/f3jm5-zqzfx-pxdr2-dx8z5-bvnb3-rpjf/transform/image/read/?width=200&height=200&output-format=jpg" \
  -H "Authorization: Bearer {jwt_token}" \
  -o thumbnail.jpg

Request Transform Download Token

GET /current/workspace/{workspace_id}/storage/{node_id}/transform/{transform_name}/requestread/
GET /current/share/{share_id}/storage/{node_id}/transform/{transform_name}/requestread/

Get a temporary download token for the transformed file. Available on both workspaces and shares.

Auth required.

Response

{
  "result": true,
  "token": "eyJhbGciOiJIUzI1NiJ9..."
}

Download Tokens Pattern

The requestread endpoint generates temporary, auth-free download tokens for files, previews, and transforms.

Flow:

  1. GET .../storage/{node_id}/requestread/ — returns {"token": "..."}
  2. 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.

GET /current/workspace/{workspace_id}/metadata/view/?template_id={template_id}
POST /current/workspace/{workspace_id}/metadata/view/
DELETE /current/workspace/{workspace_id}/metadata/view/?template_id={template_id}
GET /current/workspace/{workspace_id}/metadata/views/
POST /current/workspace/{workspace_id}/metadata/view/{template_id}/export/

Retrieve, upsert, delete, list, or export 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

ParameterTypeRequiredDescription
template_idstringYesTemplate 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

ParameterTypeRequiredDescription
Content-TypeheaderYesMust be application/x-www-form-urlencoded. application/json bodies are rejected.
template_idstringYesTemplate OpaqueId the view belongs to. Accepted in the body or the query string.
configstring (URL-encoded JSON)YesForm-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.
namestringNoOptional human label for the view (max 30 chars). Preserved on upsert if omitted. The view id is server-owned and never client-supplied.

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

ParameterTypeRequiredDescription
template_idstringYesTemplate 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 }
  ]
}
FieldTypeRequiredDescription
versionintYesSchema version. Must equal 1.
columnsarrayYesOrdered list of column entries. The array order IS the display order.
columns[].fieldstringYesTemplate field name. Non-empty.
columns[].visibleboolNoWhether the column is visible.
columns[].widthintNoColumn width in pixels. Positive.
sortobjectYesDefault sort for this view.
sort.fieldstringYesTemplate field to sort by.
sort.dirstringYesasc or desc.
filtersarrayYesOrdered list of filter entries (may be empty). AND-chained; order preserved round-trip. See Filters below.
filters[].fieldstringYesTemplate field name. Non-empty. Matches the naming used by columns[].field and sort.field.
filters[].operatorstringYesOne of =, !=, <, <=, >, >=.
filters[].value_typestringYesOne of string, int, float, bool. json is rejected.
filters[].valuemixedYesNon-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.

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": true,
  "id": "{view_id}",
  "user_id": "{user_id}",
  "template_id": "{template_id}",
  "name": "{name}",
  "config": { "version": 1, "columns": [], "sort": { "field": "updated", "dir": "desc" }, "filters": [] },
  "created": "2026-04-23 11:02:00 UTC",
  "updated": "2026-04-23 11:02:00 UTC"
}

The list endpoint wraps rows under response.items[] and adds a response.count field.

POST Export — Export Saved View

Exports the caller's saved view for the given template to a TSV file written into workspace storage. The template_id is a path segment (before /export/), not a body or query field. The export runs asynchronously: the call queues a job and returns immediately — poll the destination folder for the finished file.

Precondition. A saved view must already exist for that template, otherwise the call responds 1609 (Not Found) (404).

Path Parameters

ParameterTypeRequiredDescription
template_idstringYesTemplate OpaqueId whose saved view is exported. Supplied as a URI path segment before /export/.

Request Body

Send as application/x-www-form-urlencoded. Both fields are optional.

ParameterTypeRequiredDescription
parent_node_idstringNoDestination folder OpaqueId (max 64 chars). Omit to write to the workspace root.
filenamestringNoOutput filename (max 200 chars).

Deduplication. If an export for the same (workspace, user, template, parent_node_id) is already in flight, the call returns {"status": "duplicate", "filename": "...", "message": "..."} with no job_id instead of queuing a second job.

Queued Response

{
  "result": true,
  "job_id": "{job_id}",
  "status": "queued",
  "filename": "{filename}",
  "message": "Export queued"
}

Exports are capped at roughly 50,000 rows.

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.

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 CodeHTTP StatusDescription
1605 (Invalid Input)406Missing or malformed parameters, or config failed validation (unknown keys, bad types, bad operator / value_type, or more than 5 filters). Also returned when the body is sent as application/json instead of form-encoded.
1609 (Not Found)404Caller has no saved view for this template (GET / DELETE / export)
1664 (Datastore Error)500Failed to read or persist the view
1680 (Access Denied)403Not a workspace member or metadata feature not enabled

Workspace-Only Features

These endpoints are only available on workspaces, not shares:

Share-Specific Notes

↑ Back to top