Core concepts & resources
This page is your mental model for the FluidCloud Partner API. It defines every resource your API key touches — Tenant, Space, Folder, File, Share, and Quota — and the fields you get back on each one. It also walks the File lifecycle, the rule that matters most for storage backends (quarantine-until-clean), and an optional convenience for addressing files by your own key (client_key).
Each resource has a full endpoint reference: see Spaces, Folders, Files, Uploads, Shares, and Quota. New here? Start with the Quickstart.
How the resources nest
FluidCloud is hierarchical. Everything you create lives inside your tenant, and the containment chain is fixed:
Tenant (your workspace — implicit, comes from your API key)
└── Space (a top-level bucket you create)
└── Folder (optional tree inside a Space; can nest)
└── File (a stored object: the bytes + metadata)
└── Share (a raw link that serves one File's bytes)- A Space belongs to your tenant.
- A Folder belongs to one Space (and may have a parent Folder).
- A File belongs to one Space, and optionally one Folder.
- A Share points at exactly one File.
- Quota is a single per-tenant view of your usage and limits.
All ids are UUID strings. All timestamps are ISO-8601 UTC (e.g. 2026-06-23T14:05:00Z).
Tenant
Your workspace. A tenant is the unit of isolation and billing — every Space, Folder, File, and Share you create lives inside it, and your subscription (and its storage / share-link limits) attaches here.
You do not pass a tenant id on requests and there is no "tenants" endpoint. Your API key is your tenant: every call is automatically scoped to the tenant that owns the key, and you can only ever see your own data. This is why cross-tenant ids return 404 Not Found rather than 403 — an id outside your tenant simply does not exist as far as your key is concerned (see Errors).
The closest thing to a "read my tenant" call is the Quota endpoint, which reports your tenant's usage and limits.
Team membership, seats, and roles are an account-level concern managed in the dashboard and are not exposed to API keys.
Space
A top-level bucket within your tenant. A Space is the natural place to separate one logical area of your integration from another — for example one Space per customer, per project, or per environment. Folders and Files always live inside a Space.
Requires the spaces:read / spaces:write scopes. Full reference: Spaces.
Fields you see:
| Field | Type | Notes |
|---|---|---|
id | UUID string | The Space id; use it as space_id when creating folders and files. |
name | string | Display name you chose. |
created_at | ISO-8601 UTC | When the Space was created. |
updated_at | ISO-8601 UTC | Last modification time. |
{
"id": "5b1f2c0a-9d4e-4a77-8c21-0f3a6b8e1d22",
"name": "Customer ACME — production",
"created_at": "2026-06-20T09:12:44Z",
"updated_at": "2026-06-20T09:12:44Z"
}Folder
An optional tree inside a Space. Folders organize Files. They are self-referential — a Folder can have a parent Folder, so you can build an arbitrarily deep tree. Files may sit directly in a Space (no folder) or inside a Folder.
Folders are soft-deletable: deleting a Folder marks it deleted (it stops appearing in listings) without immediately destroying its contents. See Organizing files and Deleting data.
Requires the folders:read / folders:write scopes. Full reference: Folders.
Fields you see:
| Field | Type | Notes |
|---|---|---|
id | UUID string | The Folder id. |
space_id | UUID string | The Space this folder belongs to. |
parent_id | UUID string or null | Parent folder, or null if it sits at the Space root. |
name | string | Display name. |
created_at | ISO-8601 UTC | When the folder was created. |
updated_at | ISO-8601 UTC | Last modification time. |
{
"id": "a77c9e10-2b44-4d8f-bb0e-7c5a1d9e3f04",
"space_id": "5b1f2c0a-9d4e-4a77-8c21-0f3a6b8e1d22",
"parent_id": null,
"name": "renders",
"created_at": "2026-06-21T11:30:02Z",
"updated_at": "2026-06-21T11:30:02Z"
}File
A stored object — the bytes plus their metadata. This is the core resource of the API. A File lives in a Space (and optionally a Folder), carries identifying metadata (name, MIME type, size, content hash), and tracks two pieces of state that gate what you can do with it: its scan status and its lifecycle state.
The API never proxies your bytes. You upload directly from your client to storage via a presigned URL, and you serve bytes to the public over a raw Share link — see Uploading and Raw links. The File row is the metadata record of that object.
Requires the files:read / files:write scopes. Full reference: Files.
Fields you see:
| Field | Type | Notes |
|---|---|---|
id | UUID string | The File id (FluidCloud's canonical handle). |
space_id | UUID string | Owning Space. |
folder_id | UUID string or null | Owning Folder, or null if at the Space root. |
client_key | string or null | Your own logical key, if you supplied one on upload. See client_key. |
original_name | string | The filename you uploaded. |
mime | string or null | Content type, e.g. image/png. |
size | integer or null | Size in bytes. |
sha256 | string or null | SHA-256 of the content, once known. |
scan_status | string | pending, clean, or infected — gates sharing/serving (see below). |
created_at | ISO-8601 UTC | When the File row was created. |
updated_at | ISO-8601 UTC | Last modification time. |
{
"id": "c3e9a1b6-7f20-4f15-9a4d-2e6b8c0f1a33",
"space_id": "5b1f2c0a-9d4e-4a77-8c21-0f3a6b8e1d22",
"folder_id": "a77c9e10-2b44-4d8f-bb0e-7c5a1d9e3f04",
"client_key": "results/job-7/out.png",
"original_name": "out.png",
"mime": "image/png",
"size": 482113,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"scan_status": "clean",
"created_at": "2026-06-22T08:01:19Z",
"updated_at": "2026-06-22T08:01:27Z"
}The File lifecycle
A File moves through scanning, then optionally soft-delete and permanent purge.
upload (presign → PUT → complete)
│
▼
scan_status = pending ← virus scan runs automatically
│
┌────┴────────────┐
▼ ▼
clean infected
(shareable) (blocked — cannot be shared or served)
│
▼
soft-delete ──restore──► (back to live)
│
▼
permanent purge (bytes + row destroyed; not recoverable)- Upload. You start an upload,
PUTthe bytes straight to storage via the presigned URL, then complete the upload. The File row exists withscan_status: pending. See Uploading. - Scan. Every uploaded file is automatically scanned for malware. While scanning,
scan_statusstayspending. - Resolve. When scanning finishes the file becomes
clean(safe to share and serve) orinfected(permanently blocked from sharing and serving). - Soft-delete / restore. Deleting a File marks it deleted — it leaves your listings but its bytes are retained for a grace window, so a delete is reversible by restoring it.
- Permanent purge. A purge destroys the bytes and the row. This is not recoverable. See Deleting data and Deletion.
Quarantine-until-clean
A File can only be shared or served once it has been scanned clean. This is the single most important rule for using FluidCloud as a file-link backend.
- A freshly uploaded file is
scan_status: pending. - If you try to create a share for it — or serve it — before scanning finishes (or if it came back
infected), the API responds 409 Conflict (the file is not ready). - Once
scan_statusbecomesclean, sharing and serving succeed.
Build your integration to wait for clean (poll the File, or retry the share on a 409 with backoff) before you publish a link. See the Uploading and Raw links guides for the polling pattern, and Errors for the 409 shape.
Share
A raw link that serves one File's bytes. A Share turns a clean File into a public URL that anyone with the link can open — served from the cookieless files domain, https://files.fluidadmin.com. A raw link looks like:
https://files.fluidadmin.com/s/<token>The full token is returned once, at creation time — store it then, because it cannot be retrieved again. You can list and revoke shares afterward, but the link string itself is shown only on the create response. Creating a share counts against your tenant's share-link limit (see Quota).
A Share can only be created for a File whose scan_status is clean (otherwise 409, per quarantine-until-clean above). Requires the shares:read / shares:write scopes. Full reference: Shares; concepts and edge cases in Raw links.
Fields you see:
| Field | Type | Notes |
|---|---|---|
id | UUID string | The Share id. |
file_id | UUID string | The File this link serves. |
url | string | The raw link https://files.fluidadmin.com/s/<token>. Returned only on the create response. |
permission | string | view or download — how the bytes are served. |
expires_at | ISO-8601 UTC or null | When the link stops working, or null for no expiry. |
max_downloads | integer or null | Optional cap on total downloads, or null for no cap. |
download_count | integer | How many times the link has been served. |
revoked | boolean | true once you revoke the link. |
created_at | ISO-8601 UTC | When the link was created. |
updated_at | ISO-8601 UTC | Last modification time. |
{
"id": "f0a4d2c8-3e51-4b9a-8d77-1c6e5b2f9a40",
"file_id": "c3e9a1b6-7f20-4f15-9a4d-2e6b8c0f1a33",
"url": "https://files.fluidadmin.com/s/Hk9_2pQ7zR4vXm1Lb0sN",
"permission": "view",
"expires_at": "2026-07-22T08:05:00Z",
"max_downloads": null,
"download_count": 0,
"revoked": false,
"created_at": "2026-06-22T08:05:00Z",
"updated_at": "2026-06-22T08:05:00Z"
}Known limitations to design around. A share-link password is accepted and stored but is not enforced at serve time — do not rely on it to gate access; use
expires_atandmax_downloadsinstead. And revoking a permanent (non-expiring) link does not take effect instantly: an already-cached copy can still serve from the edge for up to 3600 seconds (its max-age) after you revoke it. Both are covered in Security and Raw links.
Quota
Your tenant's usage and limits, in one read-only view. Before a big upload or a batch of shares, check Quota to know where you stand. Requires the quota:read scope. Full reference: Quota; guide: Quota and usage.
Fields you see:
| Field | Type | Notes |
|---|---|---|
bytes_used | integer | Storage you are currently using, in bytes. |
bytes_limit | integer | Storage allowance, in bytes. A generous pool comes with your plan. |
links_used | integer | Active share links you have created. |
links_limit | integer | Maximum share links your plan allows. |
A limit value of -1 means unlimited.
{
"bytes_used": 18253611008,
"bytes_limit": 1099511627776,
"links_used": 412,
"links_limit": 5000
}These counters are FluidCloud's authoritative numbers and are checked synchronously on every write. Exceeding them returns 402 with detail equal to quota_exceeded (see Errors). Both an over-storage upload and an over-cap share return 402.
client_key — address a file by your own key
client_key is an optional convenience that lets you reference a File by your logical key instead of FluidCloud's file_id. This is ideal for a stateless backend: you don't have to persist FluidCloud's id alongside every object you store.
On upload, pass your own key (any string that makes sense to you):
client_key = "results/job-7/out.png"Later, fetch that same File back by the key — without ever having stored its file_id — via files.resolve (GET /files/resolve). The resolver matches on your tenant and the key, and returns the most recent live match, so re-uploading under the same client_key supersedes the older object.
A few properties to keep in mind:
- It is logical per tenant, not globally unique — two different tenants can use the same
client_keywith no collision. - A
client_keyis optional. Files you upload without one are addressed only byfile_id(andclient_keyisnullon the row). - Resolve returns the same File object shape shown above, so you can chain straight into sharing or deleting it.
See Files → resolve for the endpoint and the Uploading guide for setting it during upload.
Where to go next
- Make your first call: Quickstart
- How auth works: Authentication
- Upload bytes: Uploading · Uploads reference
- Publish a link: Raw links · Shares reference
- Stay within plan: Quota and usage
- Full endpoint catalog: API reference
- When things go wrong: Errors · Rate limits