Admins

Partner organizations

Invite partner orgs and onboard owners.

Invite partner organizations at /admin/experience/partners or with the SQL cookbook below.

Use the admin Partners page when your deployment has SUPABASE_SERVICE_ROLE_KEY set in server env (see Getting started).

  1. Open Partners in the admin nav.
  2. Enter name, optional website, and owner email or owner user UUID.
  3. Submit — if the owner already has a BoardingPass account, they receive partner role access.

The app uses the service-role client to look up users (with paginated email search) and assign roles without manual trigger workarounds.

Partners then use the partner portal and Partner help to create challenges.


SQL cookbook (Supabase SQL Editor)

Use these blocks in the Supabase SQL Editor when bootstrapping the first admin, recovering access, or working without the service-role key.

Migration 20260612120000_profiles_role_protection.sql installs trigger profiles_prevent_role_self_update, which blocks profiles.role changes unless the JWT role is service_role. The SQL Editor does not run as service role, so you must disable the trigger, run the update, then re-enable it.

1. Find a user by email

Replace the email before running:

select
  u.id as user_id,
  u.email,
  p.role as profile_role,
  p.full_name
from auth.users u
left join public.profiles p on p.id = u.id
where lower(u.email) = lower('owner@example.com');

Copy the user_id UUID for the steps below.

2. Bootstrap the first admin

Replace YOUR-USER-UUID with the id from step 1:

alter table public.profiles disable trigger profiles_prevent_role_self_update;

update public.profiles
set role = 'admin',
    updated_at = now()
where id = 'YOUR-USER-UUID';

alter table public.profiles enable trigger profiles_prevent_role_self_update;

Sign out and back in, then open /admin/experience. You should see the Admin link in the main navbar.

3. Create a partner organization

insert into public.partner_orgs (name, website, slug, description)
values (
  'Acme Corp',
  'https://acme.example',
  'acme-corp',
  'Optional public about text for /experience/partners/acme-corp'
)
returning id, name, slug;

Copy the returned id as ORG-UUID. Slug must be unique (lowercase letters, numbers, hyphens).

4. Add an owner and grant partner role

Replace ORG-UUID and USER-UUID:

insert into public.partner_members (org_id, user_id, role)
values ('ORG-UUID', 'USER-UUID', 'owner')
on conflict (org_id, user_id) do update
  set role = excluded.role;

alter table public.profiles disable trigger profiles_prevent_role_self_update;

update public.profiles
set role = 'partner',
    updated_at = now()
where id = 'USER-UUID';

alter table public.profiles enable trigger profiles_prevent_role_self_update;

5. Add another org member

Additional teammates need a partner_members row. Give them partner profile role if they should use the partner portal:

insert into public.partner_members (org_id, user_id, role)
values ('ORG-UUID', 'USER-UUID', 'member')
on conflict (org_id, user_id) do nothing;

alter table public.profiles disable trigger profiles_prevent_role_self_update;

update public.profiles
set role = 'partner',
    updated_at = now()
where id = 'USER-UUID';

alter table public.profiles enable trigger profiles_prevent_role_self_update;

6. Verify org membership

select
  o.id as org_id,
  o.name as org_name,
  o.slug,
  u.id as user_id,
  u.email,
  pm.role as member_role,
  p.role as profile_role
from public.partner_orgs o
join public.partner_members pm on pm.org_id = o.id
join auth.users u on u.id = pm.user_id
left join public.profiles p on p.id = u.id
order by o.name, pm.role, u.email;

When to use app UI vs SQL

SituationUse
Production partner onboarding, service key configuredAdmin Partners UI
First admin before any admin existsSQL cookbook step 2
Local dev without SUPABASE_SERVICE_ROLE_KEYSQL cookbook
Bulk fixes, recovery, or scriptingSQL cookbook
Assign role with API/service role onlyadmin_set_user_role() via service-role client (not SQL Editor)

Operational notes

  • Keep the partner roster aligned with real collaborating organizations.
  • Rejected gallery submissions should include clear notes so students can improve and resubmit.
  • Coordinate with partners before approving challenges that go live to all students.

See Partner help → Getting started for the partner-side workflow.