Skip to content

Organizing files: spaces, folders, trash ​

Every file in FluidCloud lives inside a space, and optionally inside a folder within that space. This guide shows how to structure your storage with the partner API: creating and listing spaces, building and reshaping the folder tree, browsing per space, and using the universal Trash to soft-delete and restore both files and folders.

All calls go to the production base URL https://api-cloud.fluidvip.com/api/v1 and authenticate with your API key (see authentication.md). The endpoints below require the spaces:*, folders:*, and files:* scopes that come with every partner key.

For the data model behind spaces, folders, and files, see concepts.md. For the full request/response shapes, see reference/spaces.md, reference/folders.md, and reference/files.md.


The shape of your storage ​

tenant (your account)
└── space            "My files" (default) + any spaces you create
    └── folder        nestable tree (parent_id chains)
        └── folder
            └── file  belongs to a space, optionally to a folder
  • A space is a top-level bucket. Your account always has at least one — the default "My files" space, provisioned automatically the first time you list spaces.
  • A folder is a node in a tree inside one space. A folder can have a parent folder (parent_id); a folder with no parent sits at the space root.
  • A file belongs to exactly one space and either to one folder or to the space root (folder_id is null).
  • Folders and files cannot cross spaces. A folder's parent and a file's target folder must be in the same space.

IDs are UUID strings; timestamps are ISO-8601 UTC. Anything addressed by an ID that does not belong to your account returns 404 (never 403), so cross-account IDs are indistinguishable from non-existent ones.


Spaces ​

List spaces ​

Listing spaces also provisions your default space on a brand-new account, so the response is never empty for an authenticated key. Spaces come back oldest-first, so "My files" leads.

bash
curl https://api-cloud.fluidvip.com/api/v1/spaces \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"
python
from fluidcloud import FluidCloud

fc = FluidCloud(api_key="fck_live_...")

for space in fc.spaces.list():
    print(space.id, space.name)
typescript
import { FluidCloud } from "fluidcloud";

const fc = new FluidCloud({ apiKey: "fck_live_..." });

const spaces = await fc.spaces.list();
for (const space of spaces) {
  console.log(space.id, space.name);
}

Each space looks like:

json
{
  "id": "0b1c…",
  "tenant_id": "9f2a…",
  "name": "My files",
  "app": "fluidcloud",
  "created_at": "2026-06-23T10:00:00Z",
  "updated_at": "2026-06-23T10:00:00Z"
}

Requires the spaces:read scope.

Create a space ​

Use a separate space to keep a workload's files apart from everything else — for example one space per environment, customer, or job batch. The name is a free-text label (1–200 characters); it is not a path component, so spaces and punctuation are fine.

bash
curl -X POST https://api-cloud.fluidvip.com/api/v1/spaces \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Render outputs"}'
python
space = fc.spaces.create(name="Render outputs")
print(space.id)
typescript
const space = await fc.spaces.create({ name: "Render outputs" });
console.log(space.id);

Returns 201 with the new space. Requires the spaces:write scope. An empty or over-200-character name returns 400.

The partner API can create and list spaces, but it does not rename or delete them. Manage those from the dashboard at cloud.fluidvip.com. To clear out a space's contents, trash its folders and files (below).


Folders ​

Create a folder ​

A folder belongs to a space_id. Pass parent_id to nest it; omit parent_id to put it at the space root. Folder names are path components — they are validated and may not contain a path separator or traversal sequence (an invalid name returns 422).

bash
curl -X POST https://api-cloud.fluidvip.com/api/v1/folders \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "2026", "space_id": "0b1c…"}'
python
year = fc.folders.create(name="2026", space_id=space.id)
june = fc.folders.create(name="june", space_id=space.id, parent_id=year.id)
typescript
const year = await fc.folders.create({ name: "2026", spaceId: space.id });
const june = await fc.folders.create({
  name: "june",
  spaceId: space.id,
  parentId: year.id,
});

Returns 201. Requires folders:write. If parent_id points at a folder in a different space, you get 400.

List folders in a space ​

Browsing is per space. Pass space_id; pass parent_id to list the children of one folder, or omit it to list the space root. Results are sorted naturally (so 1, 2, …, 10, not 1, 10, 2).

bash
# Root of the space
curl -G https://api-cloud.fluidvip.com/api/v1/folders \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  --data-urlencode "space_id=0b1c…"

# Children of a folder
curl -G https://api-cloud.fluidvip.com/api/v1/folders \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  --data-urlencode "space_id=0b1c…" \
  --data-urlencode "parent_id=$YEAR_ID"
python
roots = fc.folders.list(space_id=space.id)            # space root
children = fc.folders.list(space_id=space.id, parent_id=year.id)
typescript
const roots = await fc.folders.list({ spaceId: space.id });
const children = await fc.folders.list({ spaceId: space.id, parentId: year.id });

Requires folders:read. When browsing (not Trash), space_id is required — omitting it returns 422.

Listing files within a folder uses the files endpoint with the same space_id / folder_id scoping. See Uploading and reference/files.md.

Rename and move folders ​

PATCH /folders/{folder_id} both renames and moves. Send name to rename (re-validated as a path component), and/or send parent_id to move:

  • parent_id set to another folder → re-parent under it (must be in the same space, or 400).
  • parent_id set to null → move to the space root.
bash
# Rename
curl -X PATCH https://api-cloud.fluidvip.com/api/v1/folders/$JUNE_ID \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "2026-06"}'

# Move to the space root
curl -X PATCH https://api-cloud.fluidvip.com/api/v1/folders/$JUNE_ID \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"parent_id": null}'
python
fc.folders.update(june.id, name="2026-06")            # rename
fc.folders.update(june.id, parent_id=None)            # to space root
fc.folders.update(june.id, parent_id=other.id)        # re-parent
typescript
await fc.folders.update(june.id, { name: "2026-06" });    // rename
await fc.folders.update(june.id, { parentId: null });     // to space root
await fc.folders.update(june.id, { parentId: other.id }); // re-parent

Requires folders:write. A move that would put a folder inside its own descendant (or make it its own parent) returns 400.

Moving files between folders is a file operation, not a folder one: PATCH /files/{file_id} with a new folder_id (or null for the space root). The target folder must be in the same space as the file. See reference/files.md.

Folder stats ​

Get the recursive size and file count of a folder's whole subtree (descending into subfolders), excluding anything in Trash:

bash
curl https://api-cloud.fluidvip.com/api/v1/folders/$YEAR_ID/stats \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"
python
stats = fc.folders.stats(year.id)
print(stats.file_count, stats.total_bytes)
typescript
const stats = await fc.folders.stats(year.id);
console.log(stats.fileCount, stats.totalBytes);
json
{ "folder_id": "…", "file_count": 128, "total_bytes": 5242880 }

Requires folders:read. These counts reflect only live content — trashed subfolders and trashed files are not included.


Trash: soft-delete and restore ​

Deleting a file or folder via the API is a soft delete — it moves the item to Trash, where it stays fully restorable. The underlying bytes are not removed by a soft delete. Browsing (the listing calls above) hides trashed items; a separate Trash view lists them.

Soft-deleted bytes still count against your storage quota until they are permanently removed. To reclaim quota, empty the Trash — covered in Deleting data.

The universal Trash ​

Normal browsing is per space, but Trash is one tenant-wide view spanning every space. To list it, pass trash=true and omit space_id:

bash
# Trashed folders (across all spaces)
curl -G https://api-cloud.fluidvip.com/api/v1/folders \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  --data-urlencode "trash=true"

# Trashed files (across all spaces)
curl -G https://api-cloud.fluidvip.com/api/v1/files \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY" \
  --data-urlencode "trash=true"
python
trashed_folders = fc.folders.list(trash=True)
trashed_files = fc.files.list(trash=True)
typescript
const trashedFolders = await fc.folders.list({ trash: true });
const trashedFiles = await fc.files.list({ trash: true });

The Trash view lists only items you trashed directly. When you trash a folder, everything inside it is swept along but does not appear as separate Trash entries — those nested items restore (or purge) together with the folder they came in with. The parent_id / folder_id scope does not apply in Trash mode.

Trash a file, restore a file ​

bash
# Trash
curl -X DELETE https://api-cloud.fluidvip.com/api/v1/files/$FILE_ID \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"

# Restore
curl -X POST https://api-cloud.fluidvip.com/api/v1/files/$FILE_ID/restore \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"
python
fc.files.delete(file_id)      # → Trash
fc.files.restore(file_id)     # back from Trash
typescript
await fc.files.delete(fileId);    // → Trash
await fc.files.restore(fileId);   // back from Trash

Both return the file row (with deleted_at set on delete, cleared on restore). Requires files:write.

Trash a folder, restore a folder (recursive) ​

Trashing a folder is recursive: the folder, all of its subfolders, and every file inside it move to Trash together in one batch, so nothing is left hidden-but-stored.

bash
# Trash the whole subtree
curl -X DELETE https://api-cloud.fluidvip.com/api/v1/folders/$FOLDER_ID \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"

# Restore it
curl -X POST https://api-cloud.fluidvip.com/api/v1/folders/$FOLDER_ID/restore \
  -H "X-API-Key: $FLUIDCLOUD_API_KEY"
python
fc.folders.delete(folder_id)      # recursive → Trash
fc.folders.restore(folder_id)     # recursive ← Trash
typescript
await fc.folders.delete(folderId);    // recursive → Trash
await fc.folders.restore(folderId);   // recursive ← Trash

Requires folders:write. Restore behaves as the exact inverse of the trash that swept the subtree in:

  • It brings back only the items that were trashed in the same batch (matched by the shared delete timestamp). Anything you had separately trashed inside that folder earlier stays in Trash.
  • If the folder's original parent is gone or still trashed, the restored folder is re-homed to the space root rather than left live-but-unreachable. Check the returned parent_id to see where it landed.

Quota is held until permanent deletion ​

Soft-deleting (a file or a recursive folder trash) does not release storage quota — the bytes stay counted while the items sit in Trash, so a trash/restore round-trip never drifts your usage. Quota is reclaimed only when items are permanently deleted from Trash. See Quota and usage and Deleting data.


Errors you may hit here ​

StatusMeaning in this context
400Bad input — e.g. empty space name, or a parent_id / folder_id in a different space, or a move into a folder's own descendant.
401Missing, invalid, or revoked API key.
403insufficient_scope — your key lacks the scope the endpoint needs.
404Space, folder, or file not found — including any ID that isn't yours (cross-account IDs return 404, never 403).
422Validation — e.g. browsing folders/files without space_id, or a folder name containing a path separator.
429Rate limited; honor the Retry-After header.

See errors.md for the full error model and rate-limits.md for the per-key limits. The SDKs raise typed errors (PermissionError for 403, NotFoundError for 404, and so on).


FluidCloud API — part of the Fluidvip ecosystem.