Skip to content

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. Run python -m aeo_bunny.scripts.seed_test_data to 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

  1. 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 on location_id may require handling dependent records (e.g. locations, projects) first.

  1. Delete from Supabase Auth via dashboard or API.

Notes

  • The User model also has ghl_contact_id, product_tier, and avatar_key columns (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_id from the GHL purchase webhook, product_tier from the purchase event, avatar_key from profile photo upload).
  • Admin users have location_id = NULL — they're not tied to any project
  • Customer users have location_id set — one project per customer
  • The super_admin gate on the settings page checks role = 'super_admin' exactly
  • The Supabase Python SDK now supports create_async_client() for async usage. The seed script and admin tooling use sync create_client() which is fine for standalone scripts. Note: All current production code also uses the sync create_client() inside async route handlers — the async client guidance here is for future use, as the codebase does not currently use create_async_client. For production code that eventually needs async Supabase operations (e.g. calling client.auth.admin.create_user(...) from within an async FastAPI route), use await create_async_client(url, key) instead. The admin API pattern (client.auth.admin.*) is the same in both sync and async clients.