Files API

building

Deletes are reversible for 30 days — how to see what is recoverable and bring it back.

Restoring a deleted file

Deleting stops a file serving immediately. It does not destroy it.

For 30 days the stored copy is kept and the record survives alongside it, so the file can be brought back exactly as it was — same id, same URL, same entity attachment. After that the copy expires and the record is removed.

This is not a feature you should need. It exists because the alternative was a system where one mistyped call was permanent, and DELETE /v1/files?role=gallery is a legal request that removes every gallery image in a project.

GET/v1/files/deleted

What is recoverable, most recently deleted first.

Response
1{2  "files": [3    {4      "id": "E7iVnWA1LiEpu3u8dRTVK",5      "originalFilename": "eso1242a (2).tif",6      "contentType": "image/tiff",7      "size": 35233254,8      "deletedAt": "2026-08-17T09:12:44.001Z",9      "restorableUntil": "2026-09-16T09:12:44.001Z"10    }11  ]12}

Deliberately not part of GET /v1/files, and there is no flag to include them. A deleted file is not a file: it does not serve, it has no working URL, and a caller who forgot a filter should never find one in a gallery.

For the same reason no url or variants come back here. The object is behind a delete marker and any link would 404 — offering one would be offering something that cannot work.

POST/v1/files/{id}/restore
Request
1curl -X POST "$BASE/v1/files/E7iVnWA1LiEpu3u8dRTVK/restore" \2  -H "authorization: Bearer $KEY"
Response
1{ "file": { "…": "the file, readable again, with fresh URLs" } }

Needs files:write — it changes what the project serves.

Everything comes back with it: the id, the filename, the entity it was attached to, who uploaded it. Smaller versions do not, and do not need to: they are derived, and generation is on demand, so each rebuilds the first time it is requested. Storing them through a deletion would be paying to keep what a request can recreate.

Errors

400 — it was not deleted
1{ "error": "That file was not deleted" }
410 — the window closed
1{ "error": "The stored copy is gone. Deleted files are recoverable for 30 days." }

A 410 is final. The record may still be listed for a moment before the daily sweep removes it, but the bytes are gone and nothing will bring them back.

What deleting actually does

Worth knowing, because it explains the edges:

  1. The stored object is deleted, which is what stops it serving.
  2. Storage keeps the previous copy for 30 days rather than destroying it.
  3. The record stays, marked deleted — because the bytes alone are not enough. What a file was attached to, and who uploaded it, exist only in the record.
  4. Smaller versions are removed outright and rebuilt on demand if it comes back.

Already-cached copies can still be served briefly. A public file's URL is permanent and cached at the edge, so deleting it stops it being distributed rather than making it instantly unreachable everywhere. If something must be unreachable now, that is a different problem from deletion.