Create Onboarding Organization
Initialize a new org on the server: one transaction, one definitive answer.
Onboarding used to write orgs from the browser under RLS, where orgs_crud_user (requesting_org_id() = org_id) only passes once Clerk has minted a JWT carrying the brand-new org, and then made two more client calls to enqueue the signup job chain and the structural defaults. A signup could lose the RLS race and discard the user's business description and links, or win it and still land a populated row with no Business DNA.
So this endpoint owns all of it:
- onboarding_initialize_org writes the orgs row AND a durable job-handoff intent in ONE Postgres transaction. Either both land or neither does — there is no "row exists but nothing will build it" state.
- The signup job chain is enqueued FROM that committed intent, and the structural defaults follow after the response. Anything left unfinished is completed by drain_pending_initializations, so an org row always converges to a fully initialized workspace.
Two deliberate departures from the rest of this file:
- It does NOT raise 401 when the JWT carries no org_id claim — that claim missing is precisely this endpoint's case. get_current_user_and_org already returns (user_id, None) without raising in that situation.
- The org comes from the BODY. That is an explicit exception to internal-docs/fastapi/org-context-contract.md ("org comes from the JWT, never a header"), and it is safe only because the body value is AUTHORIZED, never trusted: Clerk must confirm the caller holds an admin role in that org before anything is written, an answer Clerk cannot enumerate fails closed with 503 rather than falling through to a write, and an existing row is neither proof of completion nor permission to overwrite. No org header is read; the header guard is untouched.
Responses: 200 { org, jobs_enqueued, initialization } — initialized, replayed identically, or re-initialized by the same admin after an attempt that never completed. A genuine resume is never a 409: dead-ending recovery is the failure mode this endpoint exists to remove. 409 the same key with a DIFFERENT payload, or an initialization that belongs to another actor / an org that already completed one 403 authenticated, but not permitted to initialize this org 503 authorization undeterminable — fail closed, never a write 422 a malformed body — including a signup_platform that is not a recognised client surface, which is refused rather than coerced 401 no user in the token
Web and mobile both use it. Everything a mobile signup needs beyond the web payload — its platform stamp, its theme colour, its inline survey answers — is optional and absent from a web request, so the web client's request is unchanged down to the payload fingerprint.
Request body
Response
Successful Response
Changes
Changed in 2 of the 41 revisions of this API.2
- ○
added the new optional request property
new-optional-request-property
- ○
- ○
endpoint added
endpoint-added
- ○