Technical docs

Invite Acceptance Flow

Public reference generated from tech docs/invite-flow.md.

Overview

Support reference for the public invite page: `/invite/[code]`.

For shared login/session bootstrap behavior outside the invite-specific UX, see auth-and-session.md.

Verified routes

- `GET /api/invites/{code}` - `GET /api/invites/{code}/accept` - `GET /api/auth/google` - `GET /api/auth/google/callback`

Page states

- The page waits for auth bootstrap, then loads `GET /api/invites/{code}`. - The invite code comes from the route param first and falls back to `?code=...`. - Backend returns one of: - `ready_to_accept` - `already_in_organization` - `not_found` - The response includes organization `id`, `name`, and `description` when the invite exists. - If the route has no code, the frontend shows `Invalid invite code` and redirects to `/login`.

Guest flow

- A guest with a valid pending invite sees the organization details and `Sign in with Google`. - The page sends Google OAuth state as `{"inviteCode":"<code>"}`. - The guest view warns that signing in will automatically accept the invite. - In the Google callback, if `inviteCode` is present, the backend immediately tries to accept the invite before returning a redirect. - Successful invite-based sign-in redirects to `/`. - If callback-time acceptance fails, the callback redirects back to `/invite/{code}`. - New invited users skip the non-invite onboarding redirect and still land on `/` after acceptance.

Authenticated flow

- An authenticated user with `ready_to_accept` sees the signed-in account plus `Accept Invite`. - `Accept Invite` calls `GET /api/invites/{code}/accept`. - On success, the frontend refetches the user and follows the backend `redirectUrl`. - The current accept processor returns `/` with message `Invite accepted successfully`. - On accept failure, the page shows the backend/frontend error toast and reloads invite state. - Accepting an invite can restore a previously deleted membership and switches the user's `current_organization_id` to the invited organization.

Already in organization

- If the signed-in user already has membership in the invited organization, `GET /api/invites/{code}` returns `already_in_organization`. - The page shows `Already a member`, hides the accept button, and offers `Go to Dashboard` and `Log out`. - `Go to Dashboard` routes to `/`.

Not found and error behavior

- The page renders the not-found view when invite status is `not_found`. - In current backend logic, `status: not_found` covers invites that exist but are expired, cancelled, or already accepted. - A completely unknown invite code returns HTTP 404 from `GET /api/invites/{code}`. - The frontend currently handles both `status: not_found` and failed invite lookups by rendering `InviteNotFound`. - When the lookup fails at HTTP/request level, the frontend also shows an error toast and schedules a redirect to `/login` after 3 seconds. - The accept processor can return these verified errors: - `Invite not found` - `Invite is not pending` - `Invite has expired` - `User is already in this organization`

Support notes

- Invite acceptance currently does not enforce that the signed-in Google email matches the invite email. - Invite links can be created either with an email address or as shareable links with `email: null`; both land on the same public `/invite/[code]` page. - Invite-based sign-up does not run the normal non-invite onboarding redirect. New users coming through an invite are redirected to `/` after acceptance. - For invite creation, resend, cancel, and Staff-area context, see staff.md.

Start building your AI team