API Reference — Shares
A share turns a scanned-clean file into a public raw link served from the cookieless files domain (https://files.fluidadmin.com/s/<token>). Minting a share returns a one-time URL that embeds a signed token; FluidCloud stores only a hash of that token, so the plaintext link is shown once and never again.
All endpoints are under the API base https://api-cloud.fluidvip.com/api/v1 and the /shares prefix. Authenticate with your API key in the X-API-Key header (see authentication.md).
For the end-to-end "how do I publish a file and hand out a link" walkthrough, see guides/raw-links.md.
| Method | Path | Scope | Purpose |
|---|---|---|---|
POST | /shares | shares:write | Mint a share link for a clean file |
GET | /shares | shares:read | List the tenant's share links |
DELETE | /shares/{id} | shares:write | Revoke a share link |
The share token model
- A file must be scanned clean (
scan_status == "clean") before it can be shared. Every upload is automatically virus-scanned; sharing a not-yet-clean file returns409. See "quarantine-until-clean" in concepts.md. - The minted link looks like
https://files.fluidadmin.com/s/<token>. The token is HMAC-signed and verified at the edge — FluidCloud never proxies your file bytes. - The plaintext token appears only in the
urlfield of the create response. It is stored hashed at rest, so a listed share (GET /shares) does not include the URL. If you lose the URL, revoke the share and mint a new one. - Links can be expiring (1–365 days; counts against your share-link quota) or permanent (
expires_in_days: null; never expires, not counted against the share-link quota — metered as storage instead). The TypeScript/Python SDKs'files.upload({ public: true })shortcut mints a permanent link and surfaces it aspublic_url.
Mint a share link
POST /shares — scope shares:write
Creates a share link for a clean file your tenant owns and returns the one-time URL.
Request body — ShareCreate
| Field | Type | Default | Notes |
|---|---|---|---|
file_id | UUID string | (required) | The file to share. Must belong to your tenant and be scanned clean. |
permission | string | "view" | "view" (served inline in-browser) or "download" (served as an attachment). Any other value → 422. |
expires_in_days | integer or null | 7 | 1–365 for an expiring link, or null for a permanent public link that never expires. Out-of-range → 422. |
password | string or null | null | Accepted and stored hashed. See known gaps — not enforced at serve time. |
max_downloads | integer or null | null | Optional download cap; minimum 1. |
Response — 201 ShareCreated
| Field | Type | Notes |
|---|---|---|
url | string | The full raw link, e.g. https://files.fluidadmin.com/s/<token>. Shown only here — capture it now. |
id | UUID string | The share's id (use it to list/revoke). |
expires_at | ISO-8601 UTC string or null | null for a permanent link. |
permission | string | "view" or "download". |
Status codes
| Status | When |
|---|---|
201 | Share created. |
402 | detail is quota_exceeded — you are at your share-link cap (expiring links only; permanent links skip this gate). |
404 | File not found — unknown id, deleted, or it belongs to another tenant (cross-tenant ids return 404, never 403). |
409 | File is not ready — scan_status is not yet clean (quarantine-until-clean). Retry once the scan completes. |
422 | Validation — bad permission, or expires_in_days/max_downloads out of range. |
429 | Rate limited — share-link creation is capped at 120/min per key (plus the global 600/min). Honor the Retry-After header. |
Examples
curl
curl -X POST https://api-cloud.fluidvip.com/api/v1/shares \
-H "X-API-Key: fck_live_..." \
-H "Content-Type: application/json" \
-d '{
"file_id": "5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
"permission": "view",
"expires_in_days": 7
}'{
"url": "https://files.fluidadmin.com/s/eyJ0eXAiOiJzaGFyZSJ9.aXNzdWVkLW9uY2U",
"id": "9b8c7d6e-5f4a-4b3c-2d1e-0f9a8b7c6d5e",
"expires_at": "2026-06-30T12:00:00Z",
"permission": "view"
}Python (pip install fluidcloud)
from fluidcloud import FluidCloud
fc = FluidCloud(api_key="fck_live_...")
share = fc.shares.create(
file_id="5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
permission="view",
expires_in_days=7,
)
print(share.url) # capture this now — shown only once
# A permanent public link (never expires, not counted against the share-link cap):
public = fc.shares.create(file_id=share.id and "5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
expires_in_days=None)TypeScript (npm install fluidcloud)
import { FluidCloud } from "fluidcloud";
const fc = new FluidCloud({ apiKey: "fck_live_..." });
const share = await fc.shares.create({
fileId: "5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
permission: "view",
expiresInDays: 7,
});
console.log(share.url); // capture this now — shown only once
// Permanent public link:
const publicShare = await fc.shares.create({
fileId: "5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
expiresInDays: null,
});Tip: if all you want is a permanent public hotlink for a file you're uploading,
files.upload({ public: true })mints one for you and returns it aspublic_url— see guides/uploading.md and reference/files.md.
List share links
GET /shares — scope shares:read
Returns your tenant's share links, newest first. By default only active links are returned (not revoked and not past expiry; a null expiry never expires).
Query parameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
file_id | UUID string | (none) | Filter to a single file's links. |
include_inactive | boolean | false | When true, also return revoked and expired links. |
Response — 200 array of ShareOut
| Field | Type | Notes |
|---|---|---|
id | UUID string | The share id. |
file_id | UUID string | The shared file. |
permission | string | "view" or "download". |
expires_at | ISO-8601 UTC string or null | null for permanent links. |
max_downloads | integer or null | The download cap, if set. |
download_count | integer | Downloads recorded so far. |
revoked | boolean | Whether the link has been revoked. |
has_password | boolean | Whether a password was set (the password itself is never returned). |
created_at | ISO-8601 UTC string | When the link was minted. |
updated_at | ISO-8601 UTC string | Last modification. |
The listed shape never includes the URL or token — those exist only in the one-time create response.
Status codes
| Status | When |
|---|---|
200 | List returned (may be empty). |
429 | Rate limited — global 600/min per key. Honor Retry-After. |
Examples
curl
curl https://api-cloud.fluidvip.com/api/v1/shares \
-H "X-API-Key: fck_live_..."
# Filter to one file, including revoked/expired links:
curl "https://api-cloud.fluidvip.com/api/v1/shares?file_id=5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f&include_inactive=true" \
-H "X-API-Key: fck_live_..."Python
shares = fc.shares.list()
for s in shares:
print(s.id, s.file_id, s.expires_at, s.revoked)
# Filter to one file, including inactive:
one_file = fc.shares.list(
file_id="5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
include_inactive=True,
)TypeScript
const shares = await fc.shares.list();
for (const s of shares) {
console.log(s.id, s.fileId, s.expiresAt, s.revoked);
}
// Filter to one file, including inactive:
const oneFile = await fc.shares.list({
fileId: "5f1c2d3e-9a4b-4c6d-8e0f-1a2b3c4d5e6f",
includeInactive: true,
});Revoke a share link
DELETE /shares/{id} — scope shares:write
Revokes a share link. This flips the link to revoked, frees the share-link quota slot (expiring links only — permanent links never consumed a slot), and pushes the link onto the edge denylist so it stops serving. The operation is idempotent: revoking an already-revoked link still returns 204 and never double-decrements your quota.
Path parameter
| Parameter | Type | Notes |
|---|---|---|
id | UUID string | The share's id (from create or list). |
Response
204 No Content — no body.
Status codes
| Status | When |
|---|---|
204 | Revoked (or already revoked — idempotent). |
404 | Share link not found — unknown id or it belongs to another tenant (cross-tenant ids return 404, never 403). |
429 | Rate limited — global 600/min per key. Honor Retry-After. |
Examples
curl
curl -X DELETE https://api-cloud.fluidvip.com/api/v1/shares/9b8c7d6e-5f4a-4b3c-2d1e-0f9a8b7c6d5e \
-H "X-API-Key: fck_live_..."Python
fc.shares.revoke("9b8c7d6e-5f4a-4b3c-2d1e-0f9a8b7c6d5e")TypeScript
await fc.shares.revoke("9b8c7d6e-5f4a-4b3c-2d1e-0f9a8b7c6d5e");Known gaps
Two behaviors do not work the way you might expect. Design around them:
- Passwords are not enforced at serve time. You may pass
passwordon create and it is stored hashed (andhas_passwordreflects it), but the edge serves any valid token without prompting for a password. Do not rely on a share password as an access control. Use short expiries,max_downloads, and revocation instead. - Revoking a permanent link is not instant at the edge. A revoked permanent link can still serve from edge cache until its cache max-age of 3600 seconds (1 hour) lapses. Plan for up to an hour of residual availability after revoking a permanent link. (Expiring links carry the same edge-denylist push and also stop being treated as active in the backend immediately.)
Related
- guides/raw-links.md — the end-to-end guide to publishing files and handing out raw links.
- reference/files.md — file objects,
scan_status, andfiles.upload({ public: true }). - reference/quota.md — read your share-link limit and current usage (a limit of
-1means unlimited). - errors.md — the full error model and SDK error types.
- rate-limits.md — per-key and per-action rate caps.
- reference/index.md — all endpoints at a glance.