This API was rebuilt on 2026-06-10 with breaking changes: the multipart
upload and the binary
/content download were replaced by the signed-URL
workflow below, and every response shape changed. See the
changelog for the full diff.Key Concepts
- Owner — every file and folder belongs to an owner: a configured Field Model V1 entity (
entityType+entityId, e.g. anExposure) or the whole company (entityType: "company", noentityId). - File — a display identity (
displayName, renameable) over an immutable uploaded version (originalfileName,contentType,byteSize). A file’sstatusispendingfrom upload intent until finalize, thenready— unless malware scanning holds itquarantined(scan verdict outstanding) or marks itinfected(downloads permanently refused). - Placement — one appearance of a file under an owner. A file can be placed on several entities at once: sharing adds a placement, never a copy — every placement sees the same current version and history. Folder location and
categoryare per placement;displayNameis per file. Removing a placement detaches the file from that owner; the file itself is deleted only when its last placement is removed. - Folder — a named node in the owner’s folder tree (
parentFolderIdnull = top level). A folder only ever holds files and subfolders of its own owner. - Signed URLs — uploads
PUTto a signeduploadUrlpinned to the declared content type and byte size; downloadsGETa signedurlminted per request. Both expire after 15 minutes — request fresh ones, never cache them. - Soft delete — file and folder deletes are soft; folder deletes cascade recursively through the subtree and its files.
API Endpoints
Files
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/files | Create an upload intent (returns the signed uploadUrl) |
| POST | /v1/files/{fileId}/finalize | Finalize an upload (makes the file ready) |
| GET | /v1/files | List an owner’s files (paginated, optional folder filter) |
| GET | /v1/files/{fileId} | Get file metadata |
| GET | /v1/files/{fileId}/download-url | Mint a signed download URL |
| PATCH | /v1/files/{fileId} | Rename and/or move a file |
| DELETE | /v1/files/{fileId} | Soft-delete a file (every placement) |
| GET | /v1/files/{fileId}/placements | List everywhere a file appears |
| POST | /v1/files/{fileId}/placements | Share a file to another entity |
| PATCH | /v1/files/{fileId}/placements/{placementId} | Move/categorize a file under one entity |
| DELETE | /v1/files/{fileId}/placements/{placementId} | Remove a file from one entity |
Folders
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/folders | Create a folder |
| GET | /v1/folders | Get an owner’s folder tree (flat adjacency list) |
| GET | /v1/folders/{folderId} | List folder contents (subfolders + files) |
| PATCH | /v1/folders/{folderId} | Rename and/or move a folder |
| DELETE | /v1/folders/{folderId} | Soft-delete a folder subtree (recursive) |
Permissions
| Operation | Permission |
|---|---|
| List files, Get file, List placements, Get folder tree, List folder contents | company.file:read |
| Create upload intent, Finalize upload, Create folder, Add placement | company.file:create |
| Rename/move file, Update placement, Rename/move folder | company.file:update |
| Delete file, Remove placement, Delete folder | company.file:delete |
| Get download URL | company.file:download |
Upload Workflow
Uploading is a three-step handshake; only steps 1 and 3 touch this API. 1. Declare the upload (intent)Content-Type you declared,
and x-goog-content-length-range: <byteSize>,<byteSize>:
pending to ready):
Download Workflow
Mint a signed URL, then fetch the bytes from storage:Organizing with Folders
Sharing a File Across Entities
The same document often belongs on more than one entity — a loss run on both an Event and its Submission. Add a placement instead of uploading twice; the bytes are stored once and every placement stays in sync:409 Conflict. Removing the file’s last
placement deletes the file itself and reclaims its stored content
("fileDeleted": true).
Folder location and category are per placement, so once a file is
shared the owner-less PATCH /v1/files/{fileId} can no longer address them —
it returns 409 Conflict pointing at the per-placement update; displayName
stays on the file (one name everywhere) and remains renameable there.