Create a diary entry
Logs a daily site-diary entry. The key acts as a tenant Admin: with `userId` the entry is logged FOR that crew member (issue 6) — they must be an active tenant member assigned to the project, the entry's origin is `logged_for`, `createdBy` names the acting admin, and every surface shows "Logged by [admin] for [crew member]"; without it the key logs its own owner's day (origin `manual`). `entryDate` may not be in the future, and hours ENTERED BY HAND move in 15-minute steps with a 12-hour maximum (ADR 0012, amended): pass `durationMinutes` as a multiple of 15 (0–720, e.g. `465` = 7h 45m), or the legacy `hours` (0–12, rounded to the nearest minute). A value that is not a multiple of 15 is refused the way one over 720 is; entries created by a check-out can hold any whole minute (e.g. 518 = 8h 38m), but those hours are derived and never entered here. 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 (the edit verbs `405`, pointing there — "Edit it in the FOREMAN app."; creating is a real API call, so the message does not say "create"). While the subject is checked in, logging by hand would double-count the shift its check-out is about to write, so the create `409`s with "Check out first to log this shift." (brief B2 — one rule across the web form, this REST route and MCP `create_diary_entry`).
Logs a daily site-diary entry. The key acts as a tenant Admin: with
userId the entry is logged FOR that crew member (issue 6) — they
must be an active tenant member assigned to the project, the entry's
origin is logged_for, createdBy names the acting admin, and every
surface shows "Logged by [admin] for [crew member]"; without it the
key logs its own owner's day (origin manual). entryDate may not
be in the future, and
hours ENTERED BY HAND move in 15-minute steps with a 12-hour maximum
(ADR 0012, amended): pass durationMinutes as a multiple of 15
(0–720, e.g. 465 = 7h 45m), or the legacy hours (0–12, rounded to
the nearest minute). A value that is not a multiple of 15 is refused
the way one over 720 is; entries created by a check-out can hold any
whole minute (e.g. 518 = 8h 38m), but those hours are derived and
never entered here. 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
(the edit verbs 405, pointing there — "Edit it in the FOREMAN app.";
creating is a real API call, so the message does not say "create"). While
the subject is checked in, logging by hand would double-count the shift
its check-out is about to write, so the create 409s with
"Check out first to log this shift." (brief B2 — one rule across the web
form, this REST route and MCP create_diary_entry).
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
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/diary-entries" \ -H "Content-Type: application/json" \ -d '{ "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "entryDate": "2019-08-24" }'{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "photoCount": 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": "Check out first to log this shift." }}{ "error": { "code": "validation", "message": "One or more inputs are invalid.", "fields": { "fieldName": "A message explaining what's wrong with this field." } }}Resolve the authenticated tenant GET
Echoes the tenant and role the presented API key resolves to. Useful as an "is my key valid?" check. Returns `401` if the key is missing/invalid.
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.