Upload an experience image
/api/experiences/{id}/imageStores the file at {companyId}/{experienceId} in the experience-images bucket and writes a cache-busted public URL onto the experience row. Not gated on manage_services: a photo is part of editing the experience, same posture as the equivalent product-image route. Ownership is checked BEFORE the upload, so a request naming an experience the caller does not own is a 404 with nothing written.
Authorization
sessionCookie The dashboard's Supabase Auth session cookie, set at sign-in. Large sessions are split across
numbered chunks (…auth-token.0, .1), so treat this as a cookie family rather than one name.
Every request re-validates it against the Auth server (getUser()), never by decoding the cookie
locally: a JWT nothing has checked is not a credential. Tenancy is then read from the verified
app_metadata.company_id claim and enforced by row-level security; it is never read from request
input, on any route, ever.
role (admin / staff) is deliberately not in RLS. It gates specific actions in route code,
the operations marked admin below, so hiding a button in the UI is cosmetic only, and a route's
own check is the enforcement.
In: cookie
Path Parameters
Experience id.
Request Body
multipart/form-data
The experience image. Sent as multipart/form-data under the field name file.
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/api/experiences/string/image" \ -F file="string"{ "ok": true, "imageUrl": "http://example.com"}Update an experience, or archive/restore it PATCH
Two shapes in one route, same posture PATCH /api/products/{id} takes. A body of exactly `{"archived": true|false}` sets/clears `archived_at` and returns immediately, no other field required. Any other body is a full replacement of the catalog fields (parseExperienceInput's contract). RLS scopes the update, so an id from another org matches zero rows and returns 404.
Remove an experience image DELETE
Deletes the stored object and nulls `image_url`. Storage removal is best-effort and its failure does not fail the request.