Diary entries

List diary entries

Lists diary entries, most-recent-first, paginated. Filters map onto the domain browse: `projectId` and `userId` are repeatable. An API key is **Admin-equivalent and tenant-wide**, so this returns every entry in the tenant. The narrower per-role visibility introduced by SUP-449 — a Site Manager sees their sites plus their own, Site Crew only their own — applies to signed-in users of the web app; there is no crew-scoped API credential.

GET
/diary-entries

Lists diary entries, most-recent-first, paginated. Filters map onto the domain browse: projectId and userId are repeatable.

An API key is Admin-equivalent and tenant-wide, so this returns every entry in the tenant. The narrower per-role visibility introduced by SUP-449 — a Site Manager sees their sites plus their own, Site Crew only their own — applies to signed-in users of the web app; there is no crew-scoped API credential.

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

Query Parameters

limit?integer

Page size, 1–200. Defaults to 50.

cursor?string

Opaque cursor from a previous page's nextCursor. Omit for the first page.

projectId?array<>

Filter by project id (repeatable).

userId?array<>

Filter by crew-member user id (repeatable).

from?string

Inclusive start of the entry-date range (YYYY-MM-DD).

to?string

Inclusive end of the entry-date range (YYYY-MM-DD).

search?string

Free-text match on description and crew name.

Response Body

application/json

application/json

curl -X GET "https://example.com/diary-entries"
{  "data": [    {      "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",      "entryDate": "2019-08-24",      "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8",      "projectName": "string",      "projectArchived": true,      "userId": "2c4a230c-5085-4924-a3e1-25fb4fc5965b",      "crewMemberName": "string",      "hours": 0,      "description": "string",      "photoCount": 0,      "submittedAt": "2019-08-24T14:15:22Z",      "latitude": 0,      "longitude": 0,      "locationAccuracyM": 0,      "siteLocation": {        "verdict": "on_site",        "distanceM": 0,        "accuracyM": 0,        "missingReason": "no_site"      }    }  ],  "nextCursor": "string"}
{  "error": {    "code": "unauthorized",    "message": "Missing or invalid API key."  }}

Create a diary entry POST

Logs a daily site-diary entry on behalf of a crew member. The key acts as a tenant Admin, so the body names the crew member (`userId`) the entry is for; the entry is attributed to them and they must be an active tenant member assigned to the project. `entryDate` may not be in the future, and `hours` are 0–14 in 0.25h steps. To attach photos, first upload each via `POST /projects/{id}/diary-photos/upload-url` and pass the returned `storagePath`(s) in `photoPaths`. Editing/deleting entries stays in the app.

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. **Photos must be 2 MB or smaller.** The limit is enforced by Storage on the PUT itself, so an oversized upload fails at that step rather than here — this call only sees the *declared* file. Resize before uploading: 1600px on the long edge is plenty, and is what the FOREMAN app sends.