Files
igny8/docs/plans/implemented/FREE_ACCOUNT_OPTIONS_ANALYSIS.md
IGNY8 VPS (Salman) cf755b23dc content calendar fixed
2026-01-16 21:34:20 +00:00

24 KiB

Free Account Options - Architecture Analysis

Date: January 14, 2026
Status: Planning Phase
Purpose: Compare two approaches for free user onboarding with limited AI operations


Current System Architecture

1. Account & Plan System

Account Model:
- plan (FK to Plan)
- credits (Integer, current balance)
- status (trial, active, suspended, pending_payment, cancelled)
- payment_method
- usage_ahrefs_queries (monthly counter)
- usage_period_start/end

Plan Model:
- name, slug, price, billing_cycle
- is_internal (hide from public listings)
- max_sites, max_keywords, max_users, max_author_profiles
- included_credits (monthly allocation)
- extra_credit_price
- allow_credit_topup
- max_ahrefs_queries (monthly limit)

2. AI Configuration System

AIModelConfig (Global - Single Source of Truth):
- model_name (e.g., 'gpt-4o-mini', 'hidream-full')
- model_type (text/image)
- provider (openai, runware, etc.)
- is_default (one default per type)
- is_active
- cost_per_1k_input/output (text models)
- credits_per_image (image models)
- tokens_per_credit (text models)

AISettings (Per-Account Overrides):
- account (FK)
- integration_type (openai, runware)
- config (API keys, settings)
- model_preferences (per operation type)
- cost_limits (budgets)

3. Credit Tracking System

CreditTransaction:
- transaction_type (purchase, subscription, deduction, adjustment)
- amount (positive/negative)
- balance_after
- description, metadata

CreditUsageLog (Per AI Operation):
- operation_type (clustering, idea_generation, content_generation, image_generation)
- credits_used
- cost_usd
- model_used
- tokens_input/output
- site (FK for filtering)
- related_object_type/id

4. Current Registration Flow

  1. User registers → RegisterSerializer.create()
  2. If plan_slug not provided or = 'free':
    • Assigns Plan.slug='free' (must exist)
    • Account.status = 'trial'
    • Account.credits = plan.included_credits
    • Creates CreditTransaction (initial allocation)
  3. User can perform AI operations until credits exhausted

📊 Option 1: Individual Free Accounts (Isolated)

Concept

Each user gets their own free account with:

  • Fixed cheaper AI models (GPT-4o mini, Hidream-full)
  • Low credit allocation (50-100 operations)
  • Own isolated data/workspace
  • Ability to upgrade to paid plan

Implementation Plan

Step 1: Create Free Plan

-- Admin action via Django Admin
INSERT INTO igny8_plans (
    name, slug, price, billing_cycle, 
    is_featured, is_internal, is_active,
    max_sites, max_users, max_keywords,
    included_credits, allow_credit_topup,
    max_ahrefs_queries
) VALUES (
    'Free Starter', 'free', 0.00, 'monthly',
    false, true, true,
    1,  -- max_sites: 1 site only
    1,  -- max_users: owner only
    100,  -- max_keywords: 100
    100,  -- included_credits: 100 credits (~50 operations)
    false,  -- No credit topup for free
    0  -- No Ahrefs access
);

Step 2: Create AI Model Configs (If Not Exist)

-- GPT-4o Mini (cheaper text model)
INSERT INTO igny8_ai_model_config (
    model_name, model_type, provider, display_name,
    is_default, is_active,
    cost_per_1k_input, cost_per_1k_output,
    tokens_per_credit, max_tokens, context_window
) VALUES (
    'gpt-4o-mini', 'text', 'openai', 'GPT-4o Mini (Fast & Efficient)',
    false, true,
    0.00015, 0.0006,  -- Cheaper than GPT-4
    1000, 16384, 128000
);

-- Hidream Full (cheaper image model)
INSERT INTO igny8_ai_model_config (
    model_name, model_type, provider, display_name,
    is_default, is_active,
    credits_per_image, quality_tier,
    square_size, landscape_size
) VALUES (
    'hidream-full', 'image', 'runware', 'Hidream Full (Standard Quality)',
    false, true,
    1,  -- 1 credit per image (cheapest)
    'basic',
    '1024x1024', '1280x768'
);

Step 3: Update Registration Logic

# In auth/serializers.py RegisterSerializer.create()

# No changes needed! Current logic already handles this:
if not plan_slug or plan_slug == 'free':
    plan = Plan.objects.get(slug='free', is_active=True)
    account_status = 'trial'
    initial_credits = plan.included_credits  # 100 credits

Step 4: Force Free Plan AI Models

Option A: Global Default (Simplest)

-- Set GPT-4o Mini and Hidream as defaults
UPDATE igny8_ai_model_config 
SET is_default = false 
WHERE model_type = 'text';

UPDATE igny8_ai_model_config 
SET is_default = true 
WHERE model_name = 'gpt-4o-mini';

UPDATE igny8_ai_model_config 
SET is_default = false 
WHERE model_type = 'image';

UPDATE igny8_ai_model_config 
SET is_default = true 
WHERE model_name = 'hidream-full';

Pros: Zero code changes, all free accounts inherit defaults
Cons: Affects ALL accounts (paid users too)

Option B: Per-Account AI Settings (Recommended)

# In auth/serializers.py RegisterSerializer.create()
# After account creation:

if account_status == 'trial':  # Free accounts only
    from igny8_core.modules.system.settings_models import AISettings
    
    # Create AI settings for OpenAI (text)
    AISettings.objects.create(
        account=account,
        integration_type='openai',
        model_preferences={
            'clustering': 'gpt-4o-mini',
            'idea_generation': 'gpt-4o-mini',
            'content_generation': 'gpt-4o-mini',
            'optimization': 'gpt-4o-mini',
        },
        is_active=True
    )
    
    # Create AI settings for Runware (images)
    AISettings.objects.create(
        account=account,
        integration_type='runware',
        model_preferences={
            'image_generation': 'hidream-full',
        },
        is_active=True
    )

Pros: Free accounts locked to cheap models, paid accounts unaffected
Cons: Requires code change in registration flow

Option C: Plan-Level AI Model Configuration

# Add new field to Plan model (migration required)
class Plan(models.Model):
    # ... existing fields ...
    allowed_text_models = models.JSONField(
        default=list,
        help_text="Allowed text AI models (empty = all)"
    )
    allowed_image_models = models.JSONField(
        default=list,
        help_text="Allowed image AI models (empty = all)"
    )
    force_default_models = models.BooleanField(
        default=False,
        help_text="Force plan defaults, ignore user overrides"
    )

# Update Free plan:
plan = Plan.objects.get(slug='free')
plan.allowed_text_models = ['gpt-4o-mini']
plan.allowed_image_models = ['hidream-full']
plan.force_default_models = True
plan.save()

# In AI operation logic (ai/services.py or similar):
def get_ai_model_for_account(account, operation_type):
    plan = account.plan
    if plan.force_default_models:
        if operation_type in ['clustering', 'idea_generation', 'content_generation']:
            return 'gpt-4o-mini'
        elif operation_type == 'image_generation':
            return 'hidream-full'
    # ... existing logic for paid accounts

Pros: Centralized plan-based control, scalable
Cons: Requires migration + AI operation logic changes

Step 5: Frontend Restrictions

// In frontend, check plan limits
if (user.account.plan.slug === 'free') {
  // Hide model selector (force defaults)
  // Show "Upgrade for more models" message
  // Disable credit topup
  // Disable Ahrefs research
}

Pros: Individual Free Accounts

  1. Full User Experience - Users get their own workspace, test all features
  2. Data Isolation - Private data, no cross-contamination
  3. Smooth Upgrade Path - Existing account → upgrade plan → keep data
  4. Proper Multi-Tenancy - Each account is isolated, secure
  5. Credit Tracking - Accurate per-user usage analytics
  6. Marketing Value - "100 Free Credits" sounds generous

Cons: Individual Free Accounts

  1. Database Growth - Each user = new Account + User + potential Sites/Keywords
  2. Abuse Potential - Users can create multiple emails for free credits
  3. Complex Enforcement - Need to enforce model restrictions per account
  4. Storage Costs - Each account stores independent data
  5. Migration Complexity - If user upgrades, need to handle plan transition

Effort Estimate: Individual Free Accounts

  • Minimal Approach (Option A): 1 hour

    • Create free plan via admin
    • Set default models globally
    • Update frontend to hide topup for free users
  • Recommended Approach (Option B): 4-6 hours

    • Create free plan via admin
    • Update registration to create AISettings per free account
    • Update AI operation logic to read account-specific models
    • Frontend: Hide model selector for free users
    • Testing across all AI operations
  • Enterprise Approach (Option C): 1-2 days

    • Migration: Add allowed_models fields to Plan
    • Update registration flow
    • Refactor AI operation logic (all modules)
    • Admin UI for plan model management
    • Comprehensive testing

🎭 Option 2: Shared Demo Account (Multi-User)

Concept

One demo account shared by multiple users:

  • Users provide email → get "demo access" token
  • Limited operations pool (50-100 per user, tracked separately)
  • Shared data (users see what others created)
  • Pre-configured cheaper AI models
  • No upgrade path (must create new account)

Implementation Plan

Step 1: Create Demo Account

-- Create demo plan (internal)
INSERT INTO igny8_plans (
    name, slug, price, billing_cycle,
    is_internal, is_active,
    max_sites, max_users, max_keywords,
    included_credits, allow_credit_topup
) VALUES (
    'Demo Access', 'demo', 0.00, 'monthly',
    true, true,
    1,  -- 1 demo site
    999,  -- Unlimited demo users
    50,  -- Limited keywords
    10000,  -- Large shared pool
    false
);

-- Create demo account
INSERT INTO igny8_tenants (
    name, slug, owner_id, plan_id, 
    credits, status
) VALUES (
    'IGNY8 Demo Workspace', 'igny8-demo', 1,  -- owner = admin
    (SELECT id FROM igny8_plans WHERE slug='demo'),
    10000, 'active'
);

-- Create demo site
INSERT INTO igny8_sites (
    name, url, account_id, is_active
) VALUES (
    'Demo Content Site', 'https://demo.example.com',
    (SELECT id FROM igny8_tenants WHERE slug='igny8-demo'),
    true
);

Step 2: Create DemoUserAccess Model

# In auth/models.py
class DemoUserAccess(models.Model):
    """Track individual demo user access and limits"""
    email = models.EmailField(unique=True, db_index=True)
    demo_account = models.ForeignKey(
        'Account',
        on_delete=models.CASCADE,
        related_name='demo_users'
    )
    access_token = models.CharField(max_length=255, unique=True)
    operations_used = models.IntegerField(default=0)
    operations_limit = models.IntegerField(default=50)
    created_at = models.DateTimeField(auto_now_add=True)
    last_accessed = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True)
    
    class Meta:
        db_table = 'igny8_demo_user_access'
        indexes = [
            models.Index(fields=['email', 'is_active']),
            models.Index(fields=['access_token']),
        ]
    
    def __str__(self):
        return f"Demo: {self.email} ({self.operations_used}/{self.operations_limit})"
    
    def has_operations_remaining(self):
        return self.operations_used < self.operations_limit

Step 3: Migration

# migrations/0014_demo_user_access.py
from django.db import migrations, models

class Migration(migrations.Migration):
    dependencies = [
        ('igny8_core_auth', '0013_add_plan_is_internal'),
    ]
    
    operations = [
        migrations.CreateModel(
            name='DemoUserAccess',
            fields=[
                ('id', models.AutoField(primary_key=True)),
                ('email', models.EmailField(unique=True, db_index=True)),
                ('demo_account', models.ForeignKey(
                    on_delete=models.CASCADE,
                    to='igny8_core_auth.Account',
                    related_name='demo_users'
                )),
                ('access_token', models.CharField(max_length=255, unique=True)),
                ('operations_used', models.IntegerField(default=0)),
                ('operations_limit', models.IntegerField(default=50)),
                ('created_at', models.DateTimeField(auto_now_add=True)),
                ('last_accessed', models.DateTimeField(auto_now=True)),
                ('is_active', models.BooleanField(default=True)),
            ],
            options={'db_table': 'igny8_demo_user_access'},
        ),
    ]

Step 4: Demo Access Endpoint

# In auth/views.py AuthViewSet

@action(detail=False, methods=['post'], permission_classes=[])
def request_demo_access(self, request):
    """Request demo account access with email only"""
    email = request.data.get('email')
    if not email:
        return error_response(
            error='Email is required',
            status_code=status.HTTP_400_BAD_REQUEST,
            request=request
        )
    
    # Validate email format
    from django.core.validators import validate_email
    from django.core.exceptions import ValidationError
    try:
        validate_email(email)
    except ValidationError:
        return error_response(
            error='Invalid email format',
            status_code=status.HTTP_400_BAD_REQUEST,
            request=request
        )
    
    # Get demo account
    try:
        demo_account = Account.objects.get(slug='igny8-demo', status='active')
    except Account.DoesNotExist:
        return error_response(
            error='Demo account not configured',
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            request=request
        )
    
    # Get or create demo user access
    from .models import DemoUserAccess
    import secrets
    
    demo_user, created = DemoUserAccess.objects.get_or_create(
        email=email,
        demo_account=demo_account,
        defaults={
            'access_token': secrets.token_urlsafe(32),
            'operations_limit': 50,
        }
    )
    
    if not demo_user.is_active:
        return error_response(
            error='Demo access suspended. Please contact support.',
            status_code=status.HTTP_403_FORBIDDEN,
            request=request
        )
    
    if not demo_user.has_operations_remaining():
        return error_response(
            error='Demo operation limit reached. Please sign up for a full account.',
            status_code=status.HTTP_429_TOO_MANY_REQUESTS,
            request=request
        )
    
    # Generate temporary JWT for demo account
    # (Custom token that includes demo_user_id)
    access_token = generate_demo_access_token(demo_account, demo_user)
    
    return success_response(
        data={
            'access_token': access_token,
            'demo_user': {
                'email': demo_user.email,
                'operations_remaining': demo_user.operations_limit - demo_user.operations_used,
                'operations_limit': demo_user.operations_limit,
            },
            'account': {
                'id': demo_account.id,
                'name': demo_account.name,
                'is_demo': True,
            }
        },
        message='Demo access granted' if created else 'Welcome back to demo',
        request=request
    )

Step 5: Custom JWT with Demo Context

# In auth/utils.py

def generate_demo_access_token(account, demo_user):
    """Generate JWT for demo access with demo_user context"""
    import jwt
    from datetime import datetime, timedelta
    from django.conf import settings
    
    expiry = datetime.utcnow() + timedelta(hours=24)  # 24-hour demo session
    
    payload = {
        'account_id': account.id,
        'account_slug': account.slug,
        'demo_user_id': demo_user.id,
        'demo_user_email': demo_user.email,
        'is_demo': True,
        'exp': expiry,
        'iat': datetime.utcnow(),
    }
    
    return jwt.encode(payload, settings.SECRET_KEY, algorithm='HS256')

Step 6: Demo Operation Tracking Middleware

# In middleware/demo_tracking.py

class DemoOperationTrackingMiddleware:
    """Track demo user operations and enforce limits"""
    
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # Check if demo user
        if hasattr(request, 'demo_user_id'):
            from igny8_core.auth.models import DemoUserAccess
            
            demo_user = DemoUserAccess.objects.select_for_update().get(
                id=request.demo_user_id
            )
            
            # Check limit before processing
            if not demo_user.has_operations_remaining():
                return JsonResponse({
                    'success': False,
                    'error': 'Demo operation limit reached. Please sign up for a full account.',
                    'upgrade_url': '/pricing'
                }, status=429)
            
            # Store demo_user in request for operation tracking
            request.demo_user = demo_user
        
        response = self.get_response(request)
        return response

# Add to settings.py MIDDLEWARE

Step 7: Update AI Operation Logic

# In ai/services.py or wherever AI operations are tracked

def log_ai_operation(account, operation_type, credits_used, **kwargs):
    """Log AI operation and increment demo counter if demo user"""
    from igny8_core.business.billing.models import CreditUsageLog
    from django.db import transaction
    
    with transaction.atomic():
        # Create credit usage log
        CreditUsageLog.objects.create(
            account=account,
            operation_type=operation_type,
            credits_used=credits_used,
            **kwargs
        )
        
        # Deduct credits from account
        account.credits -= credits_used
        account.save()
        
        # If demo user, increment their personal counter
        from threading import local
        _request = getattr(local(), 'request', None)
        if _request and hasattr(_request, 'demo_user'):
            demo_user = _request.demo_user
            demo_user.operations_used += 1
            demo_user.save()

Step 8: Frontend Demo Flow

// New demo signup flow
async function requestDemoAccess(email: string) {
  const response = await api.post('/v1/auth/request-demo-access/', { email });
  
  if (response.success) {
    // Store demo token
    localStorage.setItem('demo_token', response.data.access_token);
    localStorage.setItem('is_demo', 'true');
    
    // Show demo banner
    showDemoBanner({
      operationsRemaining: response.data.demo_user.operations_remaining,
      operationsLimit: response.data.demo_user.operations_limit,
    });
    
    // Redirect to demo workspace
    router.push('/dashboard');
  }
}

// Demo banner component
<DemoBanner>
  <p>🎭 You're in Demo Mode - {operationsRemaining} operations remaining</p>
  <Button onClick={() => router.push('/pricing')}>
    Upgrade for Full Access
  </Button>
</DemoBanner>

// Disable certain features in demo mode
if (isDemo) {
  disableFeatures(['integrations', 'automation', 'wordpress_sync']);
  showSharedDataWarning();
}

Pros: Shared Demo Account

  1. Zero Database Growth - One account, minimal new records
  2. Instant Access - No account creation, just email → token
  3. Showcase Content - Users see real AI-generated examples from others
  4. Anti-Abuse - Email-based tracking, hard limits per email
  5. Conversion Pressure - "See others creating, sign up for your own workspace"
  6. Cost Efficient - Shared credit pool, bulk tracking

Cons: Shared Demo Account

  1. No Data Privacy - All users see shared workspace (could be feature or bug)
  2. Complex Access Control - Need custom JWT + middleware + tracking
  3. No Upgrade Path - Demo token ≠ real account, must register separately
  4. Shared Credit Pool - If pool exhausted, demo is down for everyone
  5. Feature Limitations - Can't show integrations, automation, publishing
  6. User Confusion - "Why do I see others' content?" + "Lost my demo data!"
  7. Backend Complexity - New model, middleware, JWT type, operation tracking

Effort Estimate: Shared Demo Account

Full Implementation: 2-3 days

  • Create demo plan + account + site (1 hour)
  • Create DemoUserAccess model + migration (2 hours)
  • Build request_demo_access endpoint (2 hours)
  • Custom JWT generation with demo context (2 hours)
  • Middleware for demo tracking + limits (3 hours)
  • Update AI operation logging (2 hours)
  • Frontend: Demo flow + banner + restrictions (4 hours)
  • Admin: Dashboard to manage demo users (2 hours)
  • Testing: Edge cases, limits, shared data (4 hours)

🎯 Recommendation

🏆 Winner: Option 1 - Individual Free Accounts (Option B)

Rationale:

  1. Simpler Architecture - Leverages existing multi-tenancy, no custom JWT/middleware
  2. Better UX - Private workspace, smooth upgrade path, feels like real product
  3. Faster Implementation - 4-6 hours vs 2-3 days
  4. Lower Risk - No shared data confusion, no new access control layer
  5. Marketing Win - "100 Free Credits" > "Demo Access with Shared Data"
  6. Scalable - If abuse becomes issue, add email verification or captcha

Implementation Checklist:

- [ ] Create 'free' plan via Django Admin
  - [ ] Set: included_credits=100, max_sites=1, max_keywords=100
  - [ ] Set: is_internal=true, allow_credit_topup=false
  
- [ ] Verify AI Model Configs exist
  - [ ] GPT-4o Mini (text, cheap)
  - [ ] Hidream Full (image, cheap)
  
- [ ] Update RegisterSerializer (auth/serializers.py)
  - [ ] After account creation for trial status:
  - [ ] Create AISettings for openai (text) → gpt-4o-mini
  - [ ] Create AISettings for runware (images) → hidream-full
  
- [ ] Update Frontend
  - [ ] Hide model selector for free plan
  - [ ] Disable credit topup for free plan
  - [ ] Show "Upgrade for more models" CTA
  
- [ ] Testing
  - [ ] Register new free account
  - [ ] Run text AI operation → verify gpt-4o-mini used
  - [ ] Run image AI operation → verify hidream-full used
  - [ ] Verify 100 credits allocated
  - [ ] Verify upgrade flow works

🔮 Future Enhancements (Optional)

For Option 1 (Individual Free Accounts):

  1. Email Verification - Require verified email to prevent abuse
  2. Captcha - Add reCAPTCHA on free signups
  3. Usage Analytics - Track free-to-paid conversion rates
  4. Referral Credits - Give 50 bonus credits for referrals
  5. Time-Limited Trial - 30-day access instead of credit-limited

For Option 2 (Shared Demo - If Pursued):

  1. Demo Content Curation - Pre-seed with high-quality examples
  2. Demo Reset - Daily reset to clean state
  3. Anonymous Mode - Show "User A, User B" instead of emails
  4. Live Activity Feed - "User just generated an article about X"
  5. Demo Leaderboard - Gamify the experience

📚 Reference Files

Models:

  • /backend/igny8_core/auth/models.py - Account, Plan, User
  • /backend/igny8_core/business/billing/models.py - AIModelConfig, CreditTransaction, CreditUsageLog
  • /backend/igny8_core/modules/system/settings_models.py - AISettings

Registration:

  • /backend/igny8_core/auth/serializers.py - RegisterSerializer
  • /backend/igny8_core/auth/views.py - AuthViewSet.register()

AI Operations:

  • Check modules: clustering, ideas, content, images for credit deduction logic

Decision

Recommended: Proceed with Option 1 - Individual Free Accounts (Option B)
Estimated Time: 4-6 hours
Risk Level: Low
User Experience: Excellent

Consider Option 2 only if:

  • Need to showcase "collaborative" aspect
  • Want zero database growth (high traffic expected)
  • Marketing wants "see what others create" feature