Request a presigned standalone-photo upload URL
**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.
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.
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.
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
curl -X POST "https://example.com/projects/497f6eca-6276-4993-bfeb-53cbbbba6f08/photos/upload-url" \ -H "Content-Type: application/json" \ -d '{ "filename": "string", "contentType": "string" }'{ "storagePath": "string", "uploadUrl": "string", "bucket": "string"}{ "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": "validation", "message": "One or more inputs are invalid.", "fields": { "fieldName": "A message explaining what's wrong with this field." } }}List a project's photos GET
List this project's photos — both diary photos and standalone uploads — newest first. Visibility is RLS-enforced (an Admin/API key sees the tenant; a Site Manager their projects). Each item carries its `storagePath`.
Assign a crew member to a project POST
Adds a Site Crew member to the project's roster (Admin or the project's Site Manager). Idempotent — re-adding is a no-op. Admins are on every project already and can't be rostered.