Skip to content

Deleting data

FluidCloud gives a partner API key two distinct ways to delete a file, and they are not the same thing:

  1. Soft delete (trash + restore) — reversible. The file moves to Trash, but its bytes stay in storage and keep counting against your storage quota until you purge them. You can restore it.
  2. Permanent delete (purge)irreversible. The bytes are removed from object storage, the database row is deleted, share links are revoked, and the freed bytes are returned to your quota. There is no undelete.

Both flows are available to a partner key with the files:write scope. Listing/reading the trash needs files:read.

Irreversibility. Permanent deletes (/gdpr/files/{id} and /gdpr/trash) destroy data with no recovery path at the application layer. There is no "undelete" endpoint. Treat these calls as final. If you might need the file again, use soft delete instead.

See the deletion endpoint reference for the exact request/response shapes.


Choosing a level

You want to…UseReversible?Frees quota?
Remove a file from view but keep it recoverableDELETE /files/{id} (soft delete)Yes — POST /files/{id}/restoreNo (bytes still counted)
Get back a soft-deleted filePOST /files/{id}/restore
Permanently destroy one file and reclaim its bytesDELETE /gdpr/files/{id}NoYes
Permanently empty the whole Trash (or one space's)DELETE /gdpr/trashNoYes

A common, safe pattern is soft delete → verify → purge later: soft-delete in your hot path, then run a background sweep that calls DELETE /gdpr/trash to reclaim storage on a schedule.


1. Soft delete (trash)

DELETE /files/{id} marks the file as deleted and moves it to Trash. The underlying stored object is not removed, so the file is fully restorable and its bytes still count toward your storage quota. The call returns the updated file record (note deleted_at is now set).

Required scope: files:write.

curl

bash
curl -X DELETE \
  https://api-cloud.fluidvip.com/api/v1/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301 \
  -H "X-API-Key: fck_live_..."

Python (fluidcloud)

python
from fluidcloud import FluidCloud

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

file = fc.files.delete("3f2504e0-4f89-41d3-9a0c-0305e82c3301")
print(file.deleted_at)  # now set -> the file is in Trash

TypeScript (fluidcloud)

ts
import { FluidCloud } from "fluidcloud";

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

const file = await fc.files.delete("3f2504e0-4f89-41d3-9a0c-0305e82c3301");
console.log(file.deletedAt); // now set -> the file is in Trash

Restore from trash

POST /files/{id}/restore clears deleted_at and returns the file to its folder.

bash
curl -X POST \
  https://api-cloud.fluidvip.com/api/v1/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301/restore \
  -H "X-API-Key: fck_live_..."
python
file = fc.files.restore("3f2504e0-4f89-41d3-9a0c-0305e82c3301")
print(file.deleted_at)  # None -> restored
ts
const file = await fc.files.restore("3f2504e0-4f89-41d3-9a0c-0305e82c3301");
console.log(file.deletedAt); // null -> restored

Listing the trash

To find soft-deleted files before purging, list with trash=true. The Trash spans every space in your tenant, so you can omit space_id:

bash
curl "https://api-cloud.fluidvip.com/api/v1/files?trash=true" \
  -H "X-API-Key: fck_live_..."
python
trashed = fc.files.list(trash=True)
for f in trashed:
    print(f.id, f.original_name, f.size)
ts
const trashed = await fc.files.list({ trash: true });
trashed.forEach((f) => console.log(f.id, f.originalName, f.size));

Listing requires files:read. See Organizing files for the full listing parameters.


2. Permanent delete (purge)

Permanent deletes live under the /gdpr prefix. They remove the stored object and the database row, revoke and delete every share link for the file, and return the freed bytes to your storage quota. There is no recovery.

Both purge operations require the files:write scope.

This cannot be undone. Once a purge call returns success, the bytes are gone from storage and the row is gone from the database. There is no undelete endpoint and no support recovery path. Operational backups age out under a short retention policy, so no copy survives long-term either. Confirm you have the right id before calling.

Purge one file — DELETE /gdpr/files/{id}

Permanently destroys a single file you own and frees its bytes. This works whether or not the file is currently in Trash — you do not have to soft-delete first. It returns 204 No Content on success.

The SDKs do not ship a dedicated helper for this destructive endpoint, so call it via the SDK's low-level request method (which still applies your API key, base URL, and typed error mapping).

curl

bash
curl -X DELETE \
  https://api-cloud.fluidvip.com/api/v1/gdpr/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301 \
  -H "X-API-Key: fck_live_..."
# -> HTTP/1.1 204 No Content

Python (fluidcloud, low-level request)

python
from fluidcloud import FluidCloud

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

# No high-level helper for a permanent purge — call the raw endpoint.
fc.request("DELETE", "/gdpr/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301")
# 204 No Content -> the file's bytes are permanently gone and quota is freed.

TypeScript (fluidcloud, low-level request)

ts
import { FluidCloud } from "fluidcloud";

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

// No high-level helper for a permanent purge — call the raw endpoint.
await fc.request("DELETE", "/gdpr/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301");
// 204 No Content -> the file's bytes are permanently gone and quota is freed.

A purge of a file id that is not yours (or does not exist) returns 404 — cross-tenant ids never leak existence. See Errors.

Empty the trash — DELETE /gdpr/trash

Permanently purges everything currently in Trash: every soft-deleted file (objects + rows, with their share links revoked) and the now-empty trashed folders. This is how you reclaim the storage that soft-deleted files were still occupying.

  • Omit space_id to empty the universal Trash — every space in your tenant.
  • Pass space_id to empty just that one space's Trash.

The work runs in the background: the call returns immediately with the count and byte total being purged, and reports {"status": "emptying", ...}. Re-requesting the same scope while a sweep is already running is a no-op. Because the sweep is asynchronous, a successful response means the purge was accepted, not that every byte is already gone — poll the trash listing if you need to confirm completion.

curl

bash
# Empty the universal Trash (every space)
curl -X DELETE \
  https://api-cloud.fluidvip.com/api/v1/gdpr/trash \
  -H "X-API-Key: fck_live_..."

# Empty just one space's Trash
curl -X DELETE \
  "https://api-cloud.fluidvip.com/api/v1/gdpr/trash?space_id=8c1f0b2a-6d4e-4a11-9b3c-0a1b2c3d4e5f" \
  -H "X-API-Key: fck_live_..."
# -> {"status":"emptying","files":42,"bytes":10485760}

Python (fluidcloud, low-level request)

python
from fluidcloud import FluidCloud

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

# Whole-tenant Trash
result = fc.request("DELETE", "/gdpr/trash")
print(result["files"], result["bytes"])  # being purged

# One space's Trash
fc.request(
    "DELETE",
    "/gdpr/trash",
    params={"space_id": "8c1f0b2a-6d4e-4a11-9b3c-0a1b2c3d4e5f"},
)

TypeScript (fluidcloud, low-level request)

ts
import { FluidCloud } from "fluidcloud";

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

// Whole-tenant Trash
const result = await fc.request("DELETE", "/gdpr/trash");
console.log(result.files, result.bytes); // being purged

// One space's Trash
await fc.request("DELETE", "/gdpr/trash", {
  params: { space_id: "8c1f0b2a-6d4e-4a11-9b3c-0a1b2c3d4e5f" },
});

What gets cleaned up on a purge

When you permanently purge a file (either endpoint), FluidCloud, in one atomic operation:

  1. Revokes and deletes every share link for the file. Each link is added to the edge denylist before its row is removed.
  2. Deletes the stored object, removing the actual bytes.
  3. Returns the freed bytes to your storage quota — your usage drops by the file's size (read it via the quota endpoint; see Quota and usage).
  4. Deletes the file record. It will no longer appear in any listing or resolve by client_key.

Edge-cache note on revoked links. A purge revokes the file's share links, but a previously issued permanent raw link can still serve from edge cache until its max-age of 3600 seconds (one hour) lapses. If you need a link to stop serving immediately, do not rely on the cache window — and never treat a purge as an instant content takedown. See Raw links for link behavior.


Errors you may see

All errors are an HTTP status with a JSON body keyed on detail. The most relevant cases for deletion:

StatusMeaning
401Missing, invalid, or revoked API key.
403insufficient_scope — your key lacks files:write (or files:read for the trash listing).
404File not found or not yours. Cross-tenant ids always return 404, never 403, so existence never leaks.
429Rate limited. Honor the Retry-After header.

The SDKs map these to typed errors — AuthError (401), PermissionError (403), NotFoundError (404), and ApiError for the rest. Full table in Errors and limits in Rate limits.

python
from fluidcloud import FluidCloud, NotFoundError, PermissionError

fc = FluidCloud(api_key="fck_live_...")
try:
    fc.request("DELETE", "/gdpr/files/3f2504e0-4f89-41d3-9a0c-0305e82c3301")
except NotFoundError:
    print("Already gone, or not in your tenant.")
except PermissionError:
    print("This key can't purge — it lacks files:write.")

Scope and what's not available

A partner API key can soft-delete, restore, and permanently purge its own tenant's files and trash, as documented above. Tenant-wide account erasure and per-application erasure are not available to API keys — those are operator-only surfaces and are out of scope for partner integrations.


FluidCloud API — part of the Fluidvip ecosystem.