Invitations RPC
The invitation flow allows organization admins to invite users by email. Invited users receive a token-based link to accept their invitation.
Invitation Flow
Section titled “Invitation Flow”create_invitation() → generates token, sends email ↓get_invitation_details() → landing page reads invite info (no auth required) ↓accept_invitation() → authenticated user accepts, joins org/unitcreate_invitation(p_email, p_organization_id, p_role_id, p_unit_id?, p_metadata?)
Section titled “create_invitation(p_email, p_organization_id, p_role_id, p_unit_id?, p_metadata?)”Creates a new invitation for a user to join an organization (and optionally a unit). Returns the invitation token used to construct the acceptance link.
Signature
public.create_invitation( p_email TEXT, p_organization_id UUID, p_role_id UUID, p_unit_id UUID DEFAULT NULL, p_metadata JSONB DEFAULT '{}')RETURNS TABLE ( id UUID, token TEXT, email TEXT, expires_at TIMESTAMPTZ)Usage
const { data, error } = await supabase.rpc('create_invitation', { p_email: 'newuser@example.com', p_organization_id: 'org-uuid', p_role_id: 'role-uuid', p_unit_id: 'unit-uuid' // optional});
// data.token is used to build the acceptance URLconst acceptUrl = `https://yourapp.com/accept-invite?token=${data[0].token}`;get_invitation_details(p_token)
Section titled “get_invitation_details(p_token)”Returns human-readable details about an invitation from its token. No authentication is required — this function is designed for public landing pages shown to invitees before they log in.
Signature
public.get_invitation_details(p_token TEXT)RETURNS TABLE ( id UUID, email TEXT, organization_name TEXT, unit_name TEXT, role_name TEXT, role_label TEXT, invited_by_name TEXT, expires_at TIMESTAMPTZ, status TEXT)Usage
// No auth needed — safe to call on a public landing pageconst { data, error } = await supabase.rpc('get_invitation_details', { p_token: tokenFromUrl});
// Check status before showing accept UIif (data[0].status !== 'pending') { // show expired/cancelled message}accept_invitation(p_token)
Section titled “accept_invitation(p_token)”Accepts an invitation. The caller must be authenticated. The user is added to the organization (and unit, if specified in the invitation). Returns context about what was joined.
Signature
public.accept_invitation(p_token TEXT)RETURNS TABLE ( organization_id UUID, unit_id UUID, role_id UUID, organization_name TEXT)Usage
const { data, error } = await supabase.rpc('accept_invitation', { p_token: tokenFromUrl});list_invitations(p_organization_id, p_status?)
Section titled “list_invitations(p_organization_id, p_status?)”Returns invitations for an organization. Optionally filter by status.
Signature
public.list_invitations( p_organization_id UUID, p_status TEXT DEFAULT NULL)RETURNS TABLE ( id UUID, email TEXT, organization_id UUID, unit_id UUID, role_name TEXT, role_label TEXT, invited_by_email TEXT, status TEXT, expires_at TIMESTAMPTZ, created_at TIMESTAMPTZ)role_name vs role_label: role_name is the stable identifier (core.roles.name) — key your logic on it. role_label is the human-readable display name (core.roles.description) — render that. Do not derive one from the other: super_admin’s label is Owner, so de-slugifying the identifier yields “Super Admin”, a role that does not exist. role_label is nullable, because a deployment may seed a role without a description.
p_status values: 'pending', 'accepted', 'expired', 'cancelled'
Omit p_status (or pass NULL) to get every status. Any other value raises Invalid status. Must be "pending", "accepted", "expired", or "cancelled". — the filter is an exact match, so an unvalidated typo would otherwise return an empty set that looks like “no invitations”.
Usage
// All invitationsconst { data } = await supabase.rpc('list_invitations', { p_organization_id: 'org-uuid'});
// Only pendingconst { data } = await supabase.rpc('list_invitations', { p_organization_id: 'org-uuid', p_status: 'pending'});cancel_invitation(p_invitation_id)
Section titled “cancel_invitation(p_invitation_id)”Cancels a pending invitation. The invitation status is set to 'cancelled' and can no longer be accepted.
Signature
public.cancel_invitation(p_invitation_id UUID)RETURNS VOIDUsage
await supabase.rpc('cancel_invitation', { p_invitation_id: 'invite-uuid'});resend_invitation(p_invitation_id)
Section titled “resend_invitation(p_invitation_id)”Re-sends an invitation email and returns a refreshed token with a new expiry.
Signature
public.resend_invitation(p_invitation_id UUID)RETURNS TABLE ( id UUID, token TEXT, email TEXT, expires_at TIMESTAMPTZ)Usage
const { data, error } = await supabase.rpc('resend_invitation', { p_invitation_id: 'invite-uuid'});