organization

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:

  1. 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.
  2. 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:

  1. 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.
  2. 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.

post/api/organization/onboarding/create

Request body

org_idstring required

Clerk organization id the caller just created

guided_onboarding_version1 nullable
namestring nullable
addressstring nullable
currencystring nullable
sales_tax_defaultstring nullable
org_promptstring nullable
emailstring nullable
phonestring nullable
verticalstring nullable
signup_sourcestring nullable
org_linksobject nullable

Same shape OrgContext.saveOrgLinks writes: {files, links, driveFiles}

timezonestring nullable

IANA timezone from the browser, for the Daily Brief schedule

daily_briefobject nullable

Daily Brief defaults to apply during the structural bootstrap

signup_platformstring nullable

Client surface that created this org: 'web' or 'mobile'. Absent means 'web'. Any other value is refused, never coerced — this column keys the lazy free-trial grant.

brand_colorstring nullable

Client theme colour as hex (#RGB/#RRGGBB/#RRGGBBAA). Fill-if-null.

onboarding_profileobject nullable

Mobile's inline onboarding-survey answers, in the same JSON shape business_dna_service reads. One of the three sources the Business DNA gate accepts.

Response

Successful Response

{"stackTrail":"paths:/api/organization/onboarding/create:post:responses:200:content:application/json:schema","oasType":"schema","type":"unknown"}

Changes

Changed in 2 of the 41 revisions of this API.2