Admin & Super Admin Management¶
How to create, promote, and remove operator accounts.
Creating a New Admin¶
Step 1: Create Supabase Auth User¶
Go to Supabase Dashboard > Authentication > Users > Add User.
Set email and password. Check "Auto Confirm" (or manually mark the email as confirmed after creation). Without a confirmed email, the admin cannot log in — Supabase requires confirmed emails for JWT issuance. Note the UUID shown after creation.
Step 2: Insert DB Record¶
INSERT INTO users (id, supabase_id, email, role, is_active, first_name, last_name)
VALUES (
gen_random_uuid(),
'<uuid-from-step-1>',
'name@yourcompany.com',
'admin',
true,
'First',
'Last'
);
Dev/QA shortcut: For development and QA environments, the seed script (
scripts/seed_test_data.py) is the recommended approach. It creates 3 admin users automatically — 1 super_admin (admin@test.aeobunny.com) and 2 admins (ops1@test.aeobunny.com,ops2@test.aeobunny.com) — with both Supabase auth records (including"email_confirm": True) and DB records in a single command. Runpython -m aeo_bunny.scripts.seed_test_datato use it.
Roles¶
| Role | Access |
|---|---|
super_admin |
Everything — settings, system config, all projects |
admin |
Approvals, project management, stats — no system settings |
customer |
Own project only — portal, review, photos, visibility |
Promoting an Admin to Super Admin¶
UPDATE users SET role = 'super_admin' WHERE email = 'name@yourcompany.com';
Deactivating an Admin¶
UPDATE users SET is_active = false WHERE email = 'name@yourcompany.com';
This blocks JWT validation at the middleware layer. The Supabase auth user still exists but can't access the app.
Removing an Admin Completely¶
- Delete the DB record:
DELETE FROM users WHERE email = 'name@yourcompany.com';
Note: For admin users (who have
location_id = NULL), this DELETE works cleanly. For customer users, foreign key constraints onlocation_idmay require handling dependent records (e.g. locations, projects) first.
- Delete from Supabase Auth via dashboard or API.
Notes¶
- The User model also has
ghl_contact_id,product_tier, andavatar_keycolumns (all nullable). These are NULL for admin users and can safely be omitted from manual INSERT statements. They are populated for customer users during onboarding (ghl_contact_idfrom the GHL purchase webhook,product_tierfrom the purchase event,avatar_keyfrom profile photo upload). - Admin users have
location_id = NULL— they're not tied to any project - Customer users have
location_idset — one project per customer - The
super_admingate on the settings page checksrole = 'super_admin'exactly - The Supabase Python SDK now supports
create_async_client()for async usage. The seed script and admin tooling use synccreate_client()which is fine for standalone scripts. Note: All current production code also uses the synccreate_client()inside async route handlers — the async client guidance here is for future use, as the codebase does not currently usecreate_async_client. For production code that eventually needs async Supabase operations (e.g. callingclient.auth.admin.create_user(...)from within an async FastAPI route), useawait create_async_client(url, key)instead. The admin API pattern (client.auth.admin.*) is the same in both sync and async clients.