Technical docs
Invite Acceptance Flow
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.