Download diary entries as a PDF
The diary entries matching the **same filters as `GET /diary-entries`**, as one PDF: for sharing a day's site record (defects, rectification work) with people who aren't on FOREMAN. "What you see is what you download" — the web app's **Export → Download PDF** menu item sends the browse's current filters to the same report. Contents: the business name/logo, the project (or "N projects"), the date span, a summary (entries · hours · people · photos), then entries grouped **project → day**, earliest first — each with the crew member, the shift's times in the site's timezone (or "Entered by hand"), hours, "Logged by … for …" where it applies, the full description, and its photos **two per row**. A range that reaches today is marked as possibly incomplete. API downloads are stamped "Generated via the FOREMAN API". Limits keep the file small enough to email: at most **300 entries** and **120 photos** (photos are embedded resized, ~800px). Over either, or with no matching entries, the call answers `422`; the message names the limit and the count (e.g. "Too many photos for one PDF: this view has 164, the limit is 120. Add filters …"). The key is Admin-equivalent, so every project is downloadable (signed-in web users need to manage every project in the view — an Admin, or that site's Site Manager). Like `GET /diary-entries`, this read closes expired shifts first. The response is **streamed** (`Content-Disposition: attachment`). Not on MCP by design: `list_diary_entries` gives an assistant the same data; the PDF is only a presentation of it.
The diary entries matching the same filters as GET /diary-entries,
as one PDF: for sharing a day's site record (defects, rectification work)
with people who aren't on FOREMAN. "What you see is what you download" —
the web app's Export → Download PDF menu item sends the browse's current filters
to the same report.
Contents: the business name/logo, the project (or "N projects"), the date span, a summary (entries · hours · people · photos), then entries grouped project → day, earliest first — each with the crew member, the shift's times in the site's timezone (or "Entered by hand"), hours, "Logged by … for …" where it applies, the full description, and its photos two per row. A range that reaches today is marked as possibly incomplete. API downloads are stamped "Generated via the FOREMAN API".
Limits keep the file small enough to email: at most 300 entries and
120 photos (photos are embedded resized, ~800px). Over either, or with
no matching entries, the call answers 422; the message names the limit
and the count (e.g. "Too many photos for one PDF: this view has 164, the
limit is 120. Add filters …"). The key is Admin-equivalent, so every project is
downloadable (signed-in web users need to manage every project in the
view — an Admin, or that site's Site Manager).
Like GET /diary-entries, this read closes expired shifts first. The
response is streamed (Content-Disposition: attachment).
Not on MCP by design: list_diary_entries gives an assistant the same
data; the PDF is only a presentation of it.
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
Filter by project id (repeatable).
Filter by crew-member user id (repeatable).
Inclusive start of the entry-date range (YYYY-MM-DD).
Inclusive end of the entry-date range (YYYY-MM-DD).
Free-text match on description and crew name.
Response Body
application/pdf
application/json
application/json
curl -X GET "https://example.com/diary-entries/pdf""string"{ "error": { "code": "unauthorized", "message": "Missing or invalid API key." }}{ "error": { "code": "validation", "message": "One or more inputs are invalid.", "fields": { "fieldName": "A message explaining what's wrong with this field." } }}Delete is not available via the API DELETE
Deleting a diary entry stays in the app (Admin only, on the entry's detail page); this verb is answered with a `405` pointing there.
List diary entries GET
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. **This read closes expired shifts before it answers** (ADR 0012, amended): open shifts past 12 hours inside the read's scope are auto-closed first, so the list can contain entries created by this very call — `origin` `auto_closed`, `createdBy` null, `durationMinutes`/`hours` **null** (hours *missing*, never 0, zero in totals), `closedAutomaticallyReason` `12h`. An hourly database job runs the same sweep whether or not anyone reads, so the entries exist before they are asked for. Writes never sweep.