Edit a catalog form, or toggle active (admin)
/api/forms/{id}Same shape PATCH /api/packages/{id} takes: a body of exactly { active: boolean } toggles that flag alone; anything else is validated and written as a full replace via parseFormInput. No DELETE: book_form_sends references form_id with ON DELETE SET NULL rather than CASCADE, so a form already sent keeps its own snapshot regardless, but deactivating rather than deleting keeps the catalog list honest about what is still offered.
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
book_forms.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
application/json
curl -X PATCH "https://example.com/api/forms/string" \ -H "Content-Type: application/json" \ -d '{ "active": true }'{ "ok": true}Add a form to the catalog (admin) POST
Same permission bar as PUT /api/intake and the same `intake_forms` PlanFeature: this is the other half of the one capability a business buys, not a new pricing decision. Not service-role: book_forms takes a plain `tenant_all` RLS policy, same posture book_intake_forms/book_products/book_packages all take for a catalog item a business edits directly. Shared by both engines: a form is not tied to book_services at all, unlike Packages, so there is no vertical split.
Send a catalog form to a customer POST
Snapshots title/description/fields off the CURRENT book_forms row onto a new book_form_sends row, mints its token, and best-effort emails the customer a link (notifyFormSent): the row exists and the link is returned either way, so a failed send never loses the credential. Service-role, not the RLS-scoped client: book_form_sends has no INSERT grant for `authenticated` at all. Same permission bar as the catalog routes above (admin, `intake_forms`): sending an existing form is still deciding what a business asks a customer.