Shared

Update organization settings (admin, or manage_org_settings, or deposit_staff_editable)

patch/api/companies

A true partial update: a key that is not in the body is not in the UPDATE.

This paragraph used to say the opposite, and the opposite used to be true; every field was derived with a fallback, so omitting tagline nulled it, omitting notifyEmailEnabled forced it true, and omitting bookingTheme reset it to light. That was fixed in the route (a has() gate, the same discipline the reservations, appointments and clients PATCH routes use) and the description was not moved with it. scripts/check-hardening.ts asserts the current behaviour column by column.

Three ways in (migration 0082), checked in order of breadth. An admin may write anything below. A staff login granted manage_org_settings may write anything EXCEPT five admin-only keys that either change the org's public identity (slug, status) or grant a permission (depositStaffEditable, unlinkedStaffFullAccess, staffClientVisibility); a permission that can grant permissions is not delegable, it is the thing doing the delegating. Absent that, a staff login may still write the six deposit-policy fields alone, but only once this org's admin has turned on depositStaffEditable (migration 0075).

slug, status and the three admin-only permission flags are written with service-role, because migration 0022 (and 0075 for the deposit flag) leaves those columns out of the tenant column grant; a staff JWT provably could flip them through raw PostgREST before that. Everything else goes through the RLS-scoped client.

business_type is NOT settable. Flipping a live org's vertical strands whatever it already has; a conversion is a data migration, not a settings toggle.

Authorization

sessionCookie
sb-qxrvgfkjyvbngipqvslu-auth-token<token>

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

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

application/json

curl -X PATCH "https://example.com/api/companies" \  -H "Content-Type: application/json" \  -d '{    "name": "string",    "slug": "string",    "status": "active"  }'
{  "ok": true}