Documents

Create a document folder

Creates a folder in one of a project's document libraries. Names are unique per project and library, **case-insensitively** — "Drawings" and "drawings" are the same folder, and a collision returns `409`. Admin or the project's Site Manager; blocked on read-only tenants and archived projects, like every other write on these libraries.

POST
/projects/{id}/folders

Creates a folder in one of a project's document libraries. Names are unique per project and library, case-insensitively — "Drawings" and "drawings" are the same folder, and a collision returns 409.

Admin or the project's Site Manager; blocked on read-only tenants and archived projects, like every other write on these libraries.

Authorization

x-api-key<token>

The per-tenant API key, copied from Settings → API & integrations. Sent as the x-api-key request header. The key is tenant-scoped and acts with Admin-equivalent, tenant-wide access.

In: header

Path Parameters

id*string

Resource id.

Query Parameters

kind*string

Which library the folder belongs to.

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08/folders?kind=ohs" \  -H "Content-Type: application/json" \  -d '{    "name": "string"  }'
{  "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",  "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",  "kind": "ohs",  "name": "string",  "documentCount": 0}
{  "error": {    "code": "unauthorized",    "message": "Missing or invalid API key."  }}
{  "error": {    "code": "read_only",    "message": "Your subscription is inactive. This action is read-only."  }}
{  "error": {    "code": "not_found",    "message": "Not found."  }}
{  "error": {    "code": "conflict",    "message": "This template has submissions and can't be deleted. Archive it instead."  }}
{  "error": {    "code": "validation",    "message": "One or more inputs are invalid.",    "fields": {      "fieldName": "A message explaining what's wrong with this field."    }  }}

Request a presigned diary-photo upload URL POST

**Step 1 of the presigned diary-photo upload** — the image-only sibling of the document upload. Validates the declared image (JPG/PNG only — no PDF) and returns a short-lived, single-use **signed PUT URL** (`uploadUrl`) plus the computed `storagePath`. **The image bytes never travel through this API call.** After this returns, the caller PUTs the raw image bytes directly to `uploadUrl` (a plain HTTP `PUT` with the file as the request body and the matching `Content-Type`) — out of band — then passes the returned `storagePath` in the `photoPaths` array of `POST /diary-entries`. **There is no register step**: creating the diary entry writes the `diary_photo` row(s) (ADR 0001 §5). Any crew member assigned to the project may log diary photos (not manager-only); an Admin/API key is implicitly on every project. Blocked on read-only/archived projects.

List a document library's folders GET

Lists the folders of **one** of a project's two document libraries, A→Z. A folder belongs to a project *and* a library, so `kind` is required — there is no folder that spans both. `documentCount` reflects only the documents the caller may see: the Project-Information audience rule is applied, and folders that are empty *for the caller* are omitted for anyone who cannot manage the project, so a reader never opens a folder to find nothing. Folders are one level deep and **organisational only** — a folder never affects who can see a document. Use a returned `id` as `folderId` when listing documents or registering a new one.