# Tenant Invitation & Custom Onboarding Flow ## Overview Replace the current "Create Tenant" modal with an invitation-based flow: 1. Platform admin sends invitation with custom permissions/limits 2. Invited owner receives email with onboarding link 3. Owner completes multi-step onboarding wizard 4. Tenant is fully provisioned with custom settings ## Part 1: Backend - TenantInvitation Model ### New Model: `TenantInvitation` (in `platform_admin/models.py`) ```python class TenantInvitation(models.Model): """ Invitation for new business owners to create their tenant. Allows platform admins to pre-configure custom limits and permissions. """ class Status(models.TextChoices): PENDING = 'PENDING', 'Pending' ACCEPTED = 'ACCEPTED', 'Accepted' EXPIRED = 'EXPIRED', 'Expired' CANCELLED = 'CANCELLED', 'Cancelled' # Invitation target email = models.EmailField() token = models.CharField(max_length=64, unique=True) status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING) # Pre-configured business name (optional, owner can change during onboarding) suggested_business_name = models.CharField(max_length=100, blank=True) # Custom subscription settings (overrides tier defaults) subscription_tier = models.CharField(max_length=50, default='PROFESSIONAL') custom_max_users = models.IntegerField(null=True, blank=True) # null = use tier default custom_max_resources = models.IntegerField(null=True, blank=True) # Platform permissions (what features this tenant can access) permissions = models.JSONField(default=dict, blank=True) # Example permissions: # { # "can_manage_oauth_credentials": true, # "can_accept_payments": true, # "can_use_custom_domain": true, # "can_white_label": false, # "can_api_access": true, # } # Metadata invited_by = models.ForeignKey('users.User', on_delete=models.SET_NULL, null=True) created_at = models.DateTimeField(auto_now_add=True) expires_at = models.DateTimeField() # After acceptance accepted_at = models.DateTimeField(null=True, blank=True) created_tenant = models.ForeignKey('core.Tenant', null=True, blank=True, on_delete=models.SET_NULL) created_user = models.ForeignKey('users.User', null=True, blank=True, on_delete=models.SET_NULL, related_name='tenant_invitation_accepted') ``` ### API Endpoints 1. **Create Invitation** (Platform Admin) - `POST /api/platform/tenant-invitations/` - Body: `{ email, suggested_business_name?, subscription_tier, custom_max_users?, custom_max_resources?, permissions }` - Sends invitation email 2. **List Invitations** (Platform Admin) - `GET /api/platform/tenant-invitations/` - Returns all invitations with status 3. **Get Invitation Details** (Public - by token) - `GET /api/platform/tenant-invitations/token/{token}/` - Returns invitation details for onboarding page 4. **Accept Invitation** (Public - by token) - `POST /api/platform/tenant-invitations/token/{token}/accept/` - Body: `{ password, first_name, last_name, business_name, subdomain, contact_email?, phone? }` - Creates tenant, domain, and owner user - Returns auth tokens for immediate login 5. **Resend/Cancel** (Platform Admin) - `POST /api/platform/tenant-invitations/{id}/resend/` - `DELETE /api/platform/tenant-invitations/{id}/` ## Part 2: Update Tenant Model Add new permission fields to `core/models.py` Tenant: ```python # Platform-controlled permissions (copied from invitation) can_accept_payments = models.BooleanField(default=False) can_use_custom_domain = models.BooleanField(default=False) can_white_label = models.BooleanField(default=False) can_api_access = models.BooleanField(default=False) # ... existing can_manage_oauth_credentials ``` ## Part 3: Frontend - Simplified Create Modal Replace current create modal with invitation form: ### PlatformBusinesses.tsx - New "Invite Tenant" Modal **Fields:** - Email address (required) - Suggested business name (optional) - Subscription tier dropdown - Custom limits section (toggle to override): - Max users (number input) - Max resources (number input) - Platform permissions (toggles): - Can manage OAuth credentials - Can accept payments - Can use custom domain - Can white-label - Can use API **Actions:** - Send Invitation button - Shows success message with "Invitation sent to {email}" ## Part 4: Frontend - Onboarding Wizard ### New Page: `/tenant-onboard` (TenantOnboardPage.tsx) Multi-step wizard triggered when owner clicks email link: **Step 1: Welcome & Account Setup** - Display: "You've been invited to create a business on SmoothSchedule" - Show: Invited email, tier, what permissions they'll have - Form: Password, Confirm Password - First Name, Last Name **Step 2: Business Details** - Business Name (pre-filled from invitation if provided) - Subdomain (auto-generated from name, editable) - Contact Email (pre-filled from invitation email) - Phone (optional) **Step 3: Payment Setup (conditional)** - Only shown if `can_accept_payments` permission is true - Stripe Connect embedded onboarding - Skip option available **Step 4: Complete** - Summary of what was created - "Go to Dashboard" button ### Route Setup ```tsx // App.tsx } /> ``` ## Part 5: Email Template ``` Subject: You're invited to create your business on SmoothSchedule Hi, {inviter_name} from SmoothSchedule has invited you to create your own business account. Your plan: {tier} Features included: - Up to {max_users} team members - Up to {max_resources} resources {if can_accept_payments}- Accept online payments{/if} {if can_use_custom_domain}- Custom domain support{/if} Click the link below to get started: {onboarding_url} This invitation expires in 7 days. Thanks, The SmoothSchedule Team ``` ## Implementation Order 1. **Backend Model & Migration** - Create TenantInvitation model - Add new permission fields to Tenant model - Run migrations 2. **Backend API Endpoints** - Create/list/cancel invitation endpoints - Public token lookup and accept endpoints - Email sending 3. **Frontend - Update Create Modal** - Replace current form with invitation form - Add invitation list/status to businesses view 4. **Frontend - Onboarding Wizard** - Create TenantOnboardPage component - Multi-step form with validation - Conditional Stripe Connect step - Route configuration 5. **Testing** - E2E test for full flow - Unit tests for API endpoints ## Files to Create/Modify ### Backend - `smoothschedule/platform_admin/models.py` (new - TenantInvitation) - `smoothschedule/core/models.py` (modify - add permissions) - `smoothschedule/core/migrations/0007_*.py` (new - permissions) - `smoothschedule/platform_admin/migrations/0001_*.py` (new - TenantInvitation) - `smoothschedule/platform_admin/serializers.py` (modify - add invitation serializers) - `smoothschedule/platform_admin/views.py` (modify - add invitation viewset) - `smoothschedule/platform_admin/urls.py` (modify - add invitation routes) ### Frontend - `src/api/platform.ts` (modify - add invitation API) - `src/hooks/usePlatform.ts` (modify - add invitation hooks) - `src/pages/platform/PlatformBusinesses.tsx` (modify - replace create modal) - `src/pages/TenantOnboardPage.tsx` (new - onboarding wizard) - `src/App.tsx` (modify - add route) - `src/i18n/locales/en.json` (modify - add translations)