List a project's photos
List this project's photos — both diary photos and standalone uploads — newest first. An API key is **Admin-equivalent and tenant-wide**, so this returns every photo on the project. The narrower per-role visibility introduced by SUP-449 — a Site Manager sees the sites they manage, Site Crew only the photos they uploaded — applies to signed-in users of the web app; there is no crew-scoped API credential. Each item carries its `storagePath`.
List this project's photos — both diary photos and standalone uploads —
newest first. An API key is Admin-equivalent and tenant-wide, so this
returns every photo on the project. The narrower per-role visibility
introduced by SUP-449 — a Site Manager sees the sites they manage, Site
Crew only the photos they uploaded — applies to signed-in users of the
web app; there is no crew-scoped API credential. Each item carries its
storagePath.
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
Resource id.
Query Parameters
Filter to these uploader (crew) user ids. Repeatable.
Restrict to diary photos or standalone uploads.
Inclusive upload-day start (YYYY-MM-DD).
Inclusive upload-day end (YYYY-MM-DD).
Page size, 1–200. Defaults to 50.
Opaque cursor from a previous page's nextCursor. Omit for the first page.
Response Body
application/json
application/json
curl -X GET "https://example.com/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08/photos"{ "data": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "storagePath": "string", "originalFilename": "string", "projectId": "5a8591dd-4039-49df-9202-96385ba3eff8", "projectName": "string", "projectArchived": true, "entryId": "09a8b554-45ca-4bab-a638-265db4b3e828", "uploadedById": "e4e90afa-33ee-4f3f-ba8f-3fb33b13c87d", "uploaderName": "string", "uploadedAt": "2019-08-24T14:15:22Z", "source": "diary" } ], "nextCursor": "string"}{ "error": { "code": "unauthorized", "message": "Missing or invalid API key." }}Attach standalone photos to a project POST
Register one or more **standalone** photos against the project (no diary entry). The `storagePaths` come from `.../photos/upload-url` after PUTting the bytes out of band. Any crew member assigned to the active project may upload (an Admin/API key passes implicitly). Blocked on read-only/archived projects.
Request a presigned standalone-photo upload URL POST
**Step 1 of the presigned standalone-photo upload** — the no-diary-entry sibling of `.../diary-photos/upload-url`. Validates the declared image (JPG/PNG only) 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` — out of band — then passes the returned `storagePath` in the `storagePaths` array of `POST /projects/{id}/photos`. No diary entry is involved. Any crew member assigned to the project may upload photos; 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.