Units RPC
Units are sub-groups within an organization. A user must be an organization member before they can be added to a unit.
All functions execute as the authenticated user with server-side role enforcement.
Reading Units
Section titled “Reading Units”list_my_units()
Section titled “list_my_units()”Returns all units the current user belongs to across all organizations.
Signature
public.list_my_units()RETURNS TABLE ( id UUID, organization_id UUID, name TEXT, role TEXT, role_label TEXT)role is the stable identifier to key logic on; role_label is the role’s display name (core.roles.description) to render. Neither is derivable from the other — super_admin’s label is Owner, so de-slugifying yields “Super Admin”, a role that does not exist. role_label is nullable.
Usage
const { data, error } = await supabase.rpc('list_my_units');list_units(p_org_id)
Section titled “list_units(p_org_id)”Returns all units in a given organization (not just the units the current user belongs to). Useful for admin views.
Signature
public.list_units(p_org_id UUID)RETURNS TABLE ( id UUID, organization_id UUID, name TEXT, description TEXT, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ)Usage
const { data, error } = await supabase.rpc('list_units', { p_org_id: 'org-uuid'});get_unit(p_id)
Section titled “get_unit(p_id)”Returns details for a single unit.
Signature
public.get_unit(p_id UUID)RETURNS TABLE ( id UUID, organization_id UUID, name TEXT, description TEXT, created_at TIMESTAMPTZ, updated_at TIMESTAMPTZ)Usage
const { data, error } = await supabase.rpc('get_unit', { p_id: 'unit-uuid'});list_unit_members(p_unit_id)
Section titled “list_unit_members(p_unit_id)”Returns all members of a unit with their roles.
Signature
public.list_unit_members(p_unit_id UUID)RETURNS TABLE ( user_id UUID, email TEXT, first_name TEXT, last_name TEXT, role TEXT, role_label TEXT)role is the stable identifier to key logic on; role_label is the role’s display name (core.roles.description) to render. Neither is derivable from the other — super_admin’s label is Owner, so de-slugifying yields “Super Admin”, a role that does not exist. role_label is nullable.
Usage
const { data, error } = await supabase.rpc('list_unit_members', { p_unit_id: 'unit-uuid'});get_user_unit_permissions(p_unit_id)
Section titled “get_user_unit_permissions(p_unit_id)”Returns the current user’s role name and CASL rules for the specified unit. Use this to build a unit-scoped CASL Ability when a user may hold different roles at different units. See CASL Integration for a full example.
Signature
public.get_user_unit_permissions(p_unit_id UUID)RETURNS TABLE ( role_name TEXT, casl_rules JSONB)Usage
const { data, error } = await supabase.rpc('get_user_unit_permissions', { p_unit_id: 'unit-uuid'});// data[0].role_name === 'team'// data[0].casl_rules === [{ action: 'read', subject: 'Schedule' }, ...]Creating Units
Section titled “Creating Units”create_unit(p_org_id, p_name, p_description?)
Section titled “create_unit(p_org_id, p_name, p_description?)”Creates a new unit within an organization.
Signature
public.create_unit( p_org_id UUID, p_name TEXT, p_description TEXT DEFAULT NULL)RETURNS TABLE ( id UUID, organization_id UUID, name TEXT, description TEXT, created_by UUID, updated_by UUID)Usage
const { data, error } = await supabase.rpc('create_unit', { p_org_id: 'org-uuid', p_name: 'Engineering', p_description: 'Engineering team'});Member Management
Section titled “Member Management”SMTA provides two functions for assigning users to units, with different parameter orders and authorization requirements. There are also two corresponding removal functions.
assign_user_to_unit(p_user_id, p_unit_id, p_role_id)
Section titled “assign_user_to_unit(p_user_id, p_unit_id, p_role_id)”Assigns an org member to a unit. No super_admin required — available to any member of the organization that owns the unit.
The target user must already be a member of that organization. Re-adding someone who was previously removed from the unit is supported: the existing membership row is reactivated rather than duplicated.
Signature
public.assign_user_to_unit( p_user_id UUID, p_unit_id UUID, p_role_id UUID)RETURNS VOIDRaises
| Condition | Message |
|---|---|
| Unit does not exist, or caller is not a member of the owning org | Unit not found |
| Target user is not a member of the owning org | User is not a member of the organization |
Usage
await supabase.rpc('assign_user_to_unit', { p_user_id: 'user-uuid', p_unit_id: 'unit-uuid', p_role_id: 'role-uuid'});remove_user_from_unit(p_user_id, p_unit_id)
Section titled “remove_user_from_unit(p_user_id, p_unit_id)”Soft-deletes a user’s membership in a unit. Sets is_deleted = true on the membership record. No super_admin required — available to any member of the organization that owns the unit.
Signature
public.remove_user_from_unit( p_user_id UUID, p_unit_id UUID)RETURNS VOIDRaises
| Condition | Message |
|---|---|
| Unit does not exist, or caller is not a member of the owning org | Unit not found |
| The user has no live membership in that unit | Member not found |
Updating Units
Section titled “Updating Units”update_unit(p_id, p_name, p_description?)
Section titled “update_unit(p_id, p_name, p_description?)”Updates the unit’s name and description.
Signature
public.update_unit( p_id UUID, p_name TEXT, p_description TEXT DEFAULT NULL)RETURNS TABLE ( id UUID, name TEXT, description TEXT, updated_at TIMESTAMPTZ)Usage
select * from public.update_unit('unit-uuid', 'Engineering', 'Backend team');delete_unit(p_id)
Section titled “delete_unit(p_id)”Soft-deletes the unit and all of its memberships. Sets is_deleted = true on the unit and cascades to membership records. Super admin only.
Signature
public.delete_unit(p_id UUID)RETURNS VOIDRaises
| Condition | Message |
|---|---|
| Unit does not exist, or caller is not a member of the owning org | Unit not found |
| Caller is an org member but not a super_admin | Only a super_admin can delete a unit |
Usage
select public.delete_unit('unit-uuid');-- Soft-deletes the unit, its unit_meta record, and all unit membershipsSuper Admin Operations
Section titled “Super Admin Operations”These functions require the caller to be the organization’s super admin. They also require the target user to already be an organization member before being added to a unit.
add_member_to_unit(p_unit_id, p_user_id, p_role_id)
Section titled “add_member_to_unit(p_unit_id, p_user_id, p_role_id)”Adds an existing org member to a unit. Super admin only. Note the parameter order: p_unit_id comes first, unlike assign_user_to_unit.
Signature
public.add_member_to_unit( p_unit_id UUID, p_user_id UUID, p_role_id UUID)RETURNS VOIDNote: The user must already be a member of the parent organization. Use
add_member_to_organizationfirst if needed.
Usage
await supabase.rpc('add_member_to_unit', { p_unit_id: 'unit-uuid', p_user_id: 'user-uuid', p_role_id: 'role-uuid'});update_unit_member_role(p_unit_id, p_user_id, p_role_id)
Section titled “update_unit_member_role(p_unit_id, p_user_id, p_role_id)”Changes the role of an existing unit member. Super admin only.
Signature
public.update_unit_member_role( p_unit_id UUID, p_user_id UUID, p_role_id UUID)RETURNS VOIDremove_member_from_unit(p_unit_id, p_user_id)
Section titled “remove_member_from_unit(p_unit_id, p_user_id)”Removes a member from a unit. Super admin only.
Signature
public.remove_member_from_unit( p_unit_id UUID, p_user_id UUID)RETURNS VOIDParameter Order Summary
Section titled “Parameter Order Summary”| Function | First param | Second param | Third param | Super admin? |
|---|---|---|---|---|
assign_user_to_unit | p_user_id | p_unit_id | p_role_id | No |
add_member_to_unit | p_unit_id | p_user_id | p_role_id | Yes |
remove_user_from_unit | p_user_id | p_unit_id | — | No |
remove_member_from_unit | p_unit_id | p_user_id | — | Yes |
Always double-check the parameter order when calling these functions, as the p_user_id and p_unit_id positions are swapped between each pair. Both pairs take the same arguments and both accept two UUIDs, so a transposed call will not fail on type — it will act on the wrong records or raise Unit not found.