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.