Your team grows. You add people to your Velocity X account. How do they get in? Option one: send them a signup link and manually assign their role after they join (broken, manual, error-prone). Option two: your admin invites them by email, Supabase Auth sends the invite with a magic link, they land on signup-with-password, and their role auto-assigns from invite metadata. No manual steps, no cleanup, no "I sent the signup link 3 days ago, where are they?" Velocity X does this out of the box.
Why Invite Flow Beats Open Signup
Open signup means anyone with an email can join your workspace. Invite-only means admins control who gets in and what role they get. Supabase Auth's admin.inviteUserByEmail() generates a one-time invite email. That email contains a magic link that expires in 24 hours (configurable). User clicks it, lands on a signup page pre-filled with their email, and you read the invite metadata to auto-set their role. Zero manual role assignment. Zero "did they sign up as a member or admin?" questions. One invite, one click, they're in.
The Architecture
Admin clicks "Add Team Member", enters an email and role (Editor, Viewer, Admin). Your app calls admin.inviteUserByEmail(email, { data: { role: 'editor' } }). Supabase mails them an invite link. User clicks it in their inbox, gets redirect-verified by Supabase, and lands at your signup callback (/invite). At that moment, their user record exists in Supabase Auth but isn't confirmed yet. Your callback reads session.user.user_metadata (which contains the role from the invite), inserts them into your team_members table with that role, and confirms their email.
Then: they set a password on your signup form. Done. They're a confirmed team member with the role the admin assigned. No extra database queries, no "match invite email to user ID" joins. Supabase metadata carries the role forward through the entire flow.
Code Shape
Admin invites: POST /api/invite calls supabaseAdmin.auth.admin.inviteUserByEmail(email, { data: { role } }). Callback is a protected edge function that reads the confirmation token and handles role insertion. Client-side: new user submits their password at /invite?token=xyz, your function verifies the token, sets the password, and assigns the role. JWT claims pick up the role on next auth check. Done. Total code: ~150 lines split across API, callback edge function, and a single form component.
Multi-Org Scaling
Velocity X supports multiple teams per admin. Invite metadata stores both role and organization_id. Callback inserts into team_members(user_id, organization_id, role). Same invite flow, zero changes. RLS policies lock down queries by both org_id and user_id. Invite 100 people to 5 different orgs; each sees only their team members. Authentication is global (one Supabase user); authorization is per-org (RLS + team_members table).
Frequently Asked Questions
What if an invite expires before they click it?
Supabase invalidates expired tokens. Admin re-sends the invite (same email, fresh token). User receives a new link. No stale invites left floating. Your app can show a "resend invite" button that calls the API again.
Can an admin change someone's role after they've joined?
Yes. Update the team_members table directly (no Supabase Auth call needed). Their JWT role claim updates on next login. Or refresh the session in-app if you're reading role from the team_members table instead of the JWT.
What if I need to revoke access immediately?
Set is_active = false in team_members or delete the row. RLS policy blocks queries if is_active is false. No JWT revocation needed; the database-level check happens on every API call.
Can invitees see the invite email before they click it?
No. Supabase sends it directly to their inbox. You don't store the invite text in your database. If you need templating (custom subject, sender name), use Supabase Auth email templates in the dashboard.
Does this work with SSO (Google, GitHub login)?
Yes. Invite still works — Supabase marks the user as invited, they click the link, and if they sign in with Google instead of password, Supabase links the OAuth account to the invited user record. Role carries forward. Same metadata flow.
How do I audit who invited whom and when?
Log invites in your own table (invites(id, admin_id, email, role, created_at, status)). When the user confirms, set status = 'accepted'. Audit trail is yours, not Supabase's. Track it alongside role changes for full history.
The Bottom Line
Invite flows turn onboarding from a 3-step manual process into a 1-click experience. Supabase Auth handles the hard parts (token generation, expiry, email delivery). Your job is reading the metadata and inserting into your team table. One edge function, one callback route, zero SaaS seat charges. Velocity X ships with it baked in.