Upload your own profile photo
/api/profile/avatarPersonal account data, not tenant data; a plain signed-in check, no organization scoping, no role. The storage policy pins writes to {userId}/avatar, which is what actually enforces "only your own folder". Stored on the auth user's metadata, not on any booking table.
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
Request Body
multipart/form-data
The profile photo. 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
curl -X POST "https://example.com/api/profile/avatar" \ -F file="string"{ "ok": true, "avatarUrl": "http://example.com"}Remove the website's own hero photo (admin, or manage_org_settings) DELETE
Deletes the object and nulls `site_background_image_url`. The website falls back to the widget's own hero photo, live, not to no photo at all. Takes no body.
Remove your own profile photo DELETE
Deletes the object and nulls the metadata field.