Create an invited account (public)
/api/invites/registerThe "create account and join" half of the /invite/{token} form, no session required, because the visitor is about to establish their first one. Creates the account with its email already confirmed, then the browser signs in with the same password and goes on to POST /api/invites/accept.
Skipping the confirmation email is the reason this route exists rather than a shortcut taken around one: holding the token means having opened the message sent to the invite's address, so it is already proven, on the same bearer-capability reasoning that lets /invite/{token} be viewed with no auth and lets resend be honoured on a token alone. A second confirmation round-trip proves nothing the token did not.
The address is read off the invite row and never off the body; a token is authority over exactly one address, and honouring a caller-supplied one would turn a leaked invite into a create-an-account-anywhere primitive.
Replaced a browser-side supabase.auth.signUp(), which could not work here: with confirmations enabled GoTrue answers an already-registered address with a session-less 200 and no error at all (enumeration protection) and sends no mail, so the form's only remaining branch was to strand a returning invitee on "check your email" for a message that was never coming. The admin API does not obfuscate, which is what makes the 409 below possible.
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
curl -X POST "https://example.com/api/invites/register" \ -H "Content-Type: application/json" \ -d '{ "token": "string", "password": "string" }'{ "ok": true}Request a new invite link for the signed-in account (session-gated) POST
The onboarding page's "you already have an invitation" dialog (PendingInviteDialog), for someone who signed up fresh instead of using their invite email. Session-gated rather than token-keyed, unlike POST /api/invites/resend: this caller never held a token to begin with (migration 0142 stopped book_invites from storing one anyone but the emailed recipient could ever reconstruct), only a live session proving their own email address. Looks up the pending invite by the CALLER'S OWN verified email, then does exactly what the token-keyed resend does: mint a fresh token, rotate it in, email it. Not gated by requireMember(): the caller has no `company_id` claim yet, which is exactly what accepting the invite is about to create.
Request beta access (public) POST
The public entry point for the beta-access gate (book_beta_access_requests/book_beta_codes, migration 0188): `POST /api/onboarding/complete` refuses to create a brand new tenant without a valid, unredeemed code, and this is how a prospect asks for one. Rate-limited (`beta-request:` bucket, 5/min-window/IP via the same `consumeBucket()` the guest booking surface uses) and bot-checked (`guestBotCheck()`, registered in `bot-paths.ts`). Always answers `{ok:true}` regardless of outcome, whether fresh, a resubmission of an already-pending request (23505 on the partial-unique index, swallowed), or auto-approved, so the response can never be used to enumerate which emails have already asked or which auto-approve rules exist.