Files

/api/files

Upload, list, download, and delete files attached to projects. Files are scoped to project revisions.

Endpoints

GET /api/files/projects/:projectId/list

List files uploaded to a project

Required permission: project:download, project:download:view, project:download:edit, project:download:admin

Parameters
NameInTypeDescription
revisionIdqueryintegerProject revision ID for historical file list
documentTypequerystringFilter to one document type: drawing | submittal | rfi | specification | report | photo | other. Exact match on the stored type, not a keyword — any other value is rejected. This is how you answer "are there drawings on this project?" without pulling the whole file list. Accepts document_type as well.
statusquerystringFilter to one review status: draft | review | approved | obsolete. Exact match; any other value is rejected. Use it before citing a document as current — an obsolete file lists exactly like an approved one otherwise.
tagquerystringReturn only files carrying this tag. Whole-tag match (not a substring), and only one tag per call.
folderIdqueryintegerReturn only files filed directly in this folder. The literal string "null" returns only files at the project root. Accepts folder_id as well. HTTP CALLERS ONLY — the assistant path does not read this parameter, so sending it here changes nothing about the response and is not a way to alter or shrink a result.
mimeTypequerystringFilter by stored MIME type (e.g. application/pdf). Accepts mime_type as well. This filters on the RECORDED type, so a mislabelled upload will not match what its extension suggests. HTTP CALLERS ONLY — the assistant path does not read this parameter, so sending it here changes nothing about the response and is not a way to alter or shrink a result.
fileNamequerystringMatch on the file NAME only, unlike search, which also matches the description. Accepts file_name as well. HTTP CALLERS ONLY — the assistant path does not read this parameter, so sending it here changes nothing about the response and is not a way to alter or shrink a result.
searchquerystringCase-insensitive substring match on the file NAME or DESCRIPTION (it does not read file contents). Use it rather than listing every file and scanning: a project with many files can overrun the context budget, and the truncated list you get back looks exactly like a complete one.

GET /api/files/projects/:projectId/download/:fileId

Download a project file

Required permission: project:download, project:admin

POST /api/files/projects/:projectId/upload

Upload files to a project. Supports multipart form data. Auto-detects current draft revision if project_revision_id is omitted.

Required permission: project:upload, project:upload:view, project:upload:edit, project:upload:admin

Request body (JSON)

Required
Optional

DELETE /api/files/projects/:projectId/:fileId

Delete a project file.

Required permission: project:update, project:admin

GET /api/files/projects/:projectId/folders

List the project's file folders, with their parent and sort order.

Required permission: project:download

POST /api/files/projects/:projectId/folders

Create a file folder in a project, optionally nested under an existing one.

Required permission: project:upload

Request body (JSON)

Required
Optional

PATCH /api/files/projects/:projectId/folders/reorder

Set the display order of a project's folders in one call.

Required permission: project:upload

Request body (JSON)

Required

Anything other than an array is a 400. Send the folders you want to reposition, not necessarily all of them.

PATCH /api/files/projects/:projectId/folders/:folderId

Rename, re-parent, or re-sort one folder.

Required permission: project:upload

Request body (JSON)

Optional

DELETE /api/files/projects/:projectId/folders/:folderId

Delete a folder. Its FILES ARE NOT DELETED — they are moved to the project root, so this reorganises rather than destroys.

Required permission: project:upload

PATCH /api/files/projects/:projectId/:fileId/metadata

Update a file's document type, description, or tags. Does not touch the stored bytes.

Required permission: project:upload

Request body (JSON)

Optional

PATCH /api/files/projects/:projectId/:fileId/status

Move a file through its review lifecycle: draft → review → approved → obsolete. Setting the status it already has is a 400, not a no-op.

Required permission: project:upload, project:upload:edit, project:upload:admin

Request body (JSON)

Required

Two transitions need more than project:upload: review → approved requires project:upload:edit, and approved → obsolete requires project:upload:admin. Both refuse with 403 rather than downgrading the request.

PATCH /api/files/projects/:projectId/:fileId/move

Move a file into a folder, or to the project root.

Required permission: project:upload

Request body (JSON)

Optional

GET /api/files/projects/:projectId/preview/:fileId

Stream a file for INLINE viewing rather than download. Same tiered permission ladder as the download route (a file marked edit- or admin-level needs project:upload or project:admin on top of project:download). Only known-safe types render inline; SVG, Office, CAD and archives are forced to download regardless.

Required permission: project:download, project:upload, project:admin

DELETE /api/files/projects/:projectId/revision/:revisionId/:fileId

Detach a file from a DRAFT project revision. If the file is attached to no other revision it is deleted outright, which the response reports as deletedFile:true — otherwise the earlier published revisions keep it. This is the revision-aware sibling of DELETE /projects/:projectId/:fileId, which removes the file from the project entirely. REQUIRES AN OPEN DRAFT: this change lands in the project's current unpublished draft. If the project has no open draft the call is rejected outright — check GET /api/projects/:id/revisions (a revision with is_draft true) and, if there is none, propose POST /api/projects/:id/draft/create first, then make this call. Opening a draft is its own user-approved change; never publish or revert a project to bring a draft into existence.

Required permission: project:draft:edit