# Credits & Limits System - Implementation Plan **Date:** January 5, 2026 **Status:** 🚧 IMPLEMENTATION PLAN - Pending Execution **Prepared For:** IGNY8 Platform **Purpose:** Comprehensive plan to simplify, optimize, and properly enforce credits and limits system --- ## Executive Summary This implementation plan addresses the IGNY8 platform's credits and limits system based on complete codebase analysis. ### Core Philosophy: SIMPLICITY **Keep ONLY 4 Hard Limits:** 1. **Sites** - How many sites per account 2. **Team Users** - How many team members 3. **Keywords** - Total keywords in workspace 4. **Ahrefs Queries** - Monthly research queries (NEW) **Everything Else = Credits** - Let users consume credits however they want (content, images, ideas, etc.) ### What We're Doing ✅ **REMOVE** all unnecessary monthly limits (content_ideas, content_words, images_basic, images_premium, image_prompts) ✅ **SIMPLIFY** to credit-based system only ✅ **ENFORCE** the 4 hard limits properly ✅ **ELIMINATE** data duplication across pages ✅ **REDESIGN** Usage page with multi-dimensional insights ✅ **IMPLEMENT** Ahrefs keyword research structure ✅ **VALIDATE** all credit balance checks ✅ **USE** user-friendly terminology (no "API", "operations", etc.) --- ## 1. FINAL SIMPLIFIED MODEL ### 1.1 The Only Limits That Matter | Limit | Type | Description | Enforcement | |-------|------|-------------|-------------| | **Sites** | Hard | Max sites per account | ✅ Keep & Enforce | | **Team Users** | Hard | Max team members | ✅ Keep & Enforce | | **Keywords** | Hard | Total keywords in workspace | ✅ Keep & Enforce | | **Ahrefs Queries** | Monthly | Research queries per month | 🆕 Implement | ### 1.2 What Gets REMOVED **❌ REMOVE These Limits (Use Credits Instead):** - `max_content_ideas` - Let credits control this - `max_content_words` - Let credits control this - `max_images_basic` - Let credits control this - `max_images_premium` - Let credits control this - `max_image_prompts` - Let credits control this - `max_clusters` - Let credits control this (or combine with keywords) - `usage_content_ideas` - Not needed - `usage_content_words` - Not needed - `usage_images_basic` - Not needed - `usage_images_premium` - Not needed - `usage_image_prompts` - Not needed **Why Remove?** - Confusing for users ("I have credits but can't generate content?") - Double limiting (credits + monthly limits) - Maintenance overhead - Credit system already provides control ### 1.3 Keyword Research Structure (NEW) **Two Ways to Add Keywords:** ``` ┌─────────────────────────────────────────────────────────────────┐ │ KEYWORD RESEARCH OPTIONS │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ Option 1: IGNY8 Pre-Researched Keywords (FREE) │ │ ──────────────────────────────────────────── │ │ • Access global keyword database │ │ • Filter by industry, sector, country │ │ • Pre-analyzed for search volume, difficulty │ │ • Free to browse and add to workspace │ │ • Limited by: max_keywords (total workspace limit) │ │ │ │ Option 2: Ahrefs Live Research (LIMITED) │ │ ──────────────────────────────────────────── │ │ • Query Ahrefs API directly │ │ • Get fresh, custom keyword data │ │ • Monthly query limit (e.g., 50-500 depending on plan) │ │ • Limited by: max_ahrefs_queries (monthly limit) │ │ • Results can be added to workspace (counts toward max_keywords)│ │ │ └─────────────────────────────────────────────────────────────────┘ ``` **Implementation:** - Rename current "Add Keywords" to "Browse Pre-Researched Keywords" - Add new tab/section "Research with Ahrefs" (with monthly limit indicator) - Show remaining queries: "You have 42/50 Ahrefs queries remaining this month" --- ## 2. Current System Analysis (What We Found) ### 1.1 Frontend Pages Breakdown #### Plans & Billing Dropdown (`/account/plans`) | Page | Route | Primary Purpose | Current Data Displayed | |------|-------|----------------|----------------------| | **Current Plan** | `/account/plans` | View active subscription | Plan name, price, renewal date, included credits | | **Upgrade Plan** | `/account/plans/upgrade` | Compare and purchase plans | Pricing table with all plans, features, limits | | **History** | `/account/plans/history` | Invoices and payments | Invoice list, payment methods, transaction history | **File:** `frontend/src/pages/account/PlansAndBillingPage.tsx` (881 lines) **Tab Structure:** ```typescript type TabType = 'plan' | 'upgrade' | 'invoices'; ``` **Key Data Shown:** - Current plan details (name, price, billing cycle) - Upgrade options (pricing table with all plans) - Credit balance (via `getCreditBalance()`) - Billing history (invoices, payments) - Payment methods management #### Usage Dropdown (`/account/usage`) | Page | Route | Primary Purpose | Current Data Displayed | |------|-------|----------------|----------------------| | **Limits & Usage** | `/account/usage` | Track plan limits | Hard limits + monthly limits with progress bars | | **Credit History** | `/account/usage/credits` | View credit transactions | Transaction log (purchases, deductions, adjustments) | | **Activity** | `/account/usage/activity` | Monitor API operations | API call statistics by operation type | **File:** `frontend/src/pages/account/UsageAnalyticsPage.tsx` (266 lines) **Tab Structure:** ```typescript type TabType = 'limits' | 'activity' | 'api'; ``` **Key Data Shown:** - Usage summary (hard limits: sites, users, keywords, clusters) - Monthly limits (content ideas, words, images) - Credit balance and monthly usage - Transaction history - API activity by operation type --- ### 1.2 Backend Models #### Account Model (`backend/igny8_core/auth/models.py`) **Credits Field:** ```python credits = models.IntegerField(default=0, validators=[MinValueValidator(0)]) ``` **Monthly Usage Tracking Fields:** ```python usage_content_ideas = models.IntegerField(default=0) # Monthly limit tracking usage_content_words = models.IntegerField(default=0) # Monthly limit tracking usage_images_basic = models.IntegerField(default=0) # Monthly limit tracking usage_images_premium = models.IntegerField(default=0) # Monthly limit tracking usage_image_prompts = models.IntegerField(default=0) # Monthly limit tracking usage_period_start = models.DateTimeField(null=True) # Billing period tracking usage_period_end = models.DateTimeField(null=True) # Billing period tracking ``` #### Plan Model **Hard Limits (Never Reset):** ```python max_sites = models.IntegerField(default=1) # Sites allowed per account max_users = models.IntegerField(default=1) # Team members allowed max_keywords = models.IntegerField(default=1000) # Total keywords allowed max_clusters = models.IntegerField(default=100) # Total clusters allowed ``` **Monthly Limits (Reset on Billing Cycle):** ```python max_content_ideas = models.IntegerField(default=300) # Ideas per month max_content_words = models.IntegerField(default=100000) # Words per month max_images_basic = models.IntegerField(default=300) # Basic images per month max_images_premium = models.IntegerField(default=60) # Premium images per month max_image_prompts = models.IntegerField(default=300) # Image prompts per month ``` **Credits:** ```python included_credits = models.IntegerField(default=0) # Monthly credit allocation extra_credit_price = models.DecimalField(default=0.01) # Price per additional credit ``` --- ## 2. Data Flow Analysis ### 2.1 Credit Deduction Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ CREDIT DEDUCTION WORKFLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ 1. User triggers AI operation (e.g., generate content) │ │ ↓ │ │ 2. Backend service calls CreditService │ │ ↓ │ │ 3. Check balance: CreditService.check_credits(account, credits)│ │ ↓ │ │ 4. Execute AI operation (OpenAI/Runware/etc.) │ │ ↓ │ │ 5. AI returns tokens used (input + output) │ │ ↓ │ │ 6. Calculate credits: CreditService.calculate_credits_from_tokens()│ │ • Lookup AIModelConfig.tokens_per_credit for model │ │ • credits = ceil(total_tokens / tokens_per_credit) │ │ ↓ │ │ 7. Deduct credits: │ │ • Create CreditTransaction (amount=-credits) │ │ • Create CreditUsageLog (operation details) │ │ • Update Account.credits │ │ ↓ │ │ 8. Return result to user │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` **Key Services:** - `CreditService.check_credits()` - Pre-flight balance check - `CreditService.calculate_credits_from_tokens()` - Token-based calculation - `CreditService.deduct_credits_for_operation()` - Deduct and log ### 2.2 Limit Enforcement Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ LIMIT ENFORCEMENT WORKFLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ HARD LIMITS (Sites, Users, Keywords, Clusters) │ │ ─────────────────────────────────────────────── │ │ 1. User attempts to create new resource │ │ ↓ │ │ 2. LimitService.check_hard_limit(account, limit_type, count) │ │ • Query database for current count │ │ • Compare: current_count + new_count <= plan.max_XXX │ │ ↓ │ │ 3. If exceeded: Raise HardLimitExceededError │ │ If OK: Allow creation │ │ │ │ MONTHLY LIMITS (Ideas, Words, Images, Prompts) │ │ ───────────────────────────────────────────── │ │ 1. User attempts AI operation │ │ ↓ │ │ 2. LimitService.check_monthly_limit(account, limit_type, amount)│ │ • Read from Account.usage_XXX field │ │ • Compare: current_usage + amount <= plan.max_XXX │ │ ↓ │ │ 3. If exceeded: Raise MonthlyLimitExceededError │ │ If OK: Proceed with operation │ │ ↓ │ │ 4. After operation: LimitService.increment_usage() │ │ • Update Account.usage_XXX field │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` **Key Services:** - `LimitService.check_hard_limit()` - Check persistent limits - `LimitService.check_monthly_limit()` - Check monthly allowances - `LimitService.increment_usage()` - Update monthly usage counters - `LimitService.get_usage_summary()` - Get all limits and usage --- ## 3. Current Limits Configuration ### 3.1 Items That ARE Limit-Based (User-Requested) | Item | Type | Plan Field | Enforcement Location | Status | |------|------|-----------|---------------------|--------| | **Sites** | Hard Limit | `max_sites` | Account creation, Site model | ✅ Enforced | | **Team Users** | Hard Limit | `max_users` | User invite, SiteUserAccess model | ✅ Enforced | | **Keywords** | Hard Limit | `max_keywords` | Keyword import/creation | ⚠️ PARTIAL (see below) | ### 3.2 Items That Should Have Limits (Future) | Item | Type | Plan Field | Status | Notes | |------|------|-----------|--------|-------| | **Ahrefs Queries** | Monthly Limit | `max_ahrefs_queries` (new) | ❌ Not Implemented | Future feature for keyword research | ### 3.3 Items NOT Limit-Based (Credit-Based) | Item | Enforcement | Notes | |------|------------|-------| | **AI Content Generation** | Credits | Token-based calculation via AIModelConfig | | **AI Image Generation** | Credits | Fixed credits per image (1, 5, or 15) | | **AI Clustering** | Credits | Token-based calculation | | **AI Idea Generation** | Credits | Token-based calculation | | **Internal Linking** | Credits | 8 credits per content piece | | **SEO Optimization** | Credits | 1 credit per 200 words | **Important:** Monthly limits exist for these (max_content_ideas, max_content_words, max_images_basic, etc.) but they serve as **soft limits for cost control**, not hard enforcement. Users are primarily constrained by credits. --- ## 4. Data Duplication Issues ### 4.1 Problem: Overlapping Data Across Pages **Current Situation:** | Data Type | Plans & Billing | Usage Analytics | Duplication Level | |-----------|----------------|-----------------|-------------------| | **Credit Balance** | ✅ Shown in Current Plan tab | ✅ Shown in all tabs (top cards) | 🔴 HIGH | | **Monthly Usage** | ✅ Credits used this month | ✅ Credits used this month | 🔴 HIGH | | **Plan Limits** | ✅ Shown in plan details | ✅ Full limits panel with progress bars | 🟡 MEDIUM | | **Credit Transactions** | ✅ Billing History tab | ✅ Credit History tab | 🔴 HIGH | **Example of Duplication:** **Plans & Billing - Current Plan Tab:** ```tsx

Current Plan: {planName}

Credits: {creditBalance.credits}
Used This Month: {creditBalance.credits_used_this_month}
Monthly Allowance: {creditBalance.plan_credits_per_month}
``` **Usage Analytics - Limits Tab:** ```tsx
Credits Left: {creditBalance.credits}
Credits Used This Month: {creditBalance.credits_used_this_month}
Your Monthly Limit: {creditBalance.plan_credits_per_month}
``` ### 4.2 Problem: Confusing User Journey **Current User Experience Issues:** 1. **Credit Balance** appears in BOTH dropdowns - Plans & Billing → Current Plan → Shows credit balance - Usage → All tabs → Shows credit balance in top cards 2. **Transaction History** appears in BOTH dropdowns - Plans & Billing → History → Shows invoices and payments - Usage → Credit History → Shows credit transactions 3. **Plan Limits** appears in BOTH dropdowns - Plans & Billing → Current Plan → Shows plan features and limits - Usage → Limits & Usage → Shows detailed progress bars for all limits **User Confusion:** - "Where do I check my credits?" → Two places - "Where do I see my usage?" → Two places - "Where do I see my transaction history?" → Two places --- ## 5. Enforcement Analysis ### 5.1 Well-Enforced Limits ✅ #### Sites (max_sites) **Enforcement Location:** `backend/igny8_core/auth/models.py` (Site model) ```python # When creating a new site LimitService.check_hard_limit(account, 'sites', 1) ``` **Status:** ✅ **FULLY ENFORCED** **Works:** Users cannot create more sites than their plan allows #### Team Users (max_users) **Enforcement Location:** User invite flow ```python # When inviting a new user LimitService.check_hard_limit(account, 'users', 1) ``` **Status:** ✅ **FULLY ENFORCED** **Works:** Users cannot invite more team members than their plan allows --- ### 5.2 Partially Enforced Limits ⚠️ #### Keywords (max_keywords) **Current State:** - ✅ Plan field exists: `Plan.max_keywords` - ✅ LimitService has mapping: ```python 'keywords': { 'model': 'planner.Keywords', 'plan_field': 'max_keywords', 'display_name': 'Keywords', 'filter_field': 'account', } ``` - ⚠️ **ENFORCEMENT INCONSISTENT** **Evidence from Code Search:** Found in `backend/igny8_core/business/planning/models.py`: ```python from igny8_core.business.billing.services.limit_service import LimitService # Only enforced in SOME locations: LimitService.increment_usage( account=self.account, limit_type='keywords', amount=1 ) ``` **Problem:** - Keyword import from SeedKeywords may not check limit - Keyword creation via API may not check limit consistently - Manual keyword creation may bypass checks **Impact:** - Users may exceed keyword limits without errors - Limits shown in UI but not enforced at all entry points **Recommended Fix:** 1. Add pre-create check to all keyword creation flows: ```python # Before creating keyword LimitService.check_hard_limit(account, 'keywords', 1) ``` 2. Locations to add enforcement: - `addSeedKeywordsToWorkflow` API endpoint - Keyword bulk import - Manual keyword creation form - CSV import --- ### 5.3 Missing Future Limits ❌ #### Ahrefs Keyword Queries (Planned) **User Requirement:** > "in future soon we will have option for user to query and research keywords from ahrefs, which will also have monthly limit" **Current State:** - ❌ No plan field defined - ❌ No LimitService mapping - ❌ No enforcement **Current Keywords Situation:** - **Existing:** Pre-searched keywords from IGNY8's own database (SeedKeywords) - **Limited by:** `max_keywords` (how many they can add to their workspace) - **Future:** Ahrefs API queries (need new monthly limit) **Recommended Implementation:** 1. **Add Plan Field:** ```python # backend/igny8_core/auth/models.py - Plan model max_ahrefs_queries = models.IntegerField( default=50, validators=[MinValueValidator(0)], help_text="Maximum Ahrefs keyword queries per month" ) ``` 2. **Add Account Usage Field:** ```python # backend/igny8_core/auth/models.py - Account model usage_ahrefs_queries = models.IntegerField( default=0, validators=[MinValueValidator(0)], help_text="Ahrefs queries used this month" ) ``` 3. **Add LimitService Mapping:** ```python # backend/igny8_core/business/billing/services/limit_service.py MONTHLY_LIMIT_MAPPINGS = { # ... existing mappings ... 'ahrefs_queries': { 'plan_field': 'max_ahrefs_queries', 'usage_field': 'usage_ahrefs_queries', 'display_name': 'Ahrefs Keyword Queries', }, } ``` 4. **Enforce Before Query:** ```python # In Ahrefs query service def query_ahrefs_keywords(account, query): # Check limit LimitService.check_monthly_limit(account, 'ahrefs_queries', 1) # Execute query results = ahrefs_api.search_keywords(query) # Increment usage LimitService.increment_usage(account, 'ahrefs_queries', 1) return results ``` --- ## 6. IMPLEMENTATION PLAN ### 6.1 Phase 1: Backend Cleanup (Week 1) #### 1.1 Remove Unused Monthly Limits from Database **File:** `backend/igny8_core/auth/models.py` **Remove from Plan Model:** ```python # REMOVE THESE FIELDS: max_content_ideas = models.IntegerField(...) # ❌ DELETE max_content_words = models.IntegerField(...) # ❌ DELETE max_images_basic = models.IntegerField(...) # ❌ DELETE max_images_premium = models.IntegerField(...) # ❌ DELETE max_image_prompts = models.IntegerField(...) # ❌ DELETE max_clusters = models.IntegerField(...) # ❌ DELETE (or merge with keywords) ``` **Remove from Account Model:** ```python # REMOVE THESE FIELDS: usage_content_ideas = models.IntegerField(...) # ❌ DELETE usage_content_words = models.IntegerField(...) # ❌ DELETE usage_images_basic = models.IntegerField(...) # ❌ DELETE usage_images_premium = models.IntegerField(...) # ❌ DELETE usage_image_prompts = models.IntegerField(...) # ❌ DELETE ``` **KEEP ONLY:** ```python # Plan Model - KEEP THESE: max_sites = models.IntegerField(default=1) # ✅ KEEP max_users = models.IntegerField(default=1) # ✅ KEEP max_keywords = models.IntegerField(default=1000) # ✅ KEEP max_ahrefs_queries = models.IntegerField(default=50) # 🆕 ADD # Account Model - KEEP THESE: credits = models.IntegerField(default=0) # ✅ KEEP usage_ahrefs_queries = models.IntegerField(default=0) # 🆕 ADD usage_period_start = models.DateTimeField(...) # ✅ KEEP usage_period_end = models.DateTimeField(...) # ✅ KEEP ``` **Migration Script:** ```python # Create migration: 0XXX_remove_unused_limits.py operations = [ # Remove from Plan migrations.RemoveField(model_name='plan', name='max_content_ideas'), migrations.RemoveField(model_name='plan', name='max_content_words'), migrations.RemoveField(model_name='plan', name='max_images_basic'), migrations.RemoveField(model_name='plan', name='max_images_premium'), migrations.RemoveField(model_name='plan', name='max_image_prompts'), migrations.RemoveField(model_name='plan', name='max_clusters'), # Remove from Account migrations.RemoveField(model_name='account', name='usage_content_ideas'), migrations.RemoveField(model_name='account', name='usage_content_words'), migrations.RemoveField(model_name='account', name='usage_images_basic'), migrations.RemoveField(model_name='account', name='usage_images_premium'), migrations.RemoveField(model_name='account', name='usage_image_prompts'), # Add Ahrefs fields migrations.AddField( model_name='plan', name='max_ahrefs_queries', field=models.IntegerField(default=50, validators=[MinValueValidator(0)]) ), migrations.AddField( model_name='account', name='usage_ahrefs_queries', field=models.IntegerField(default=0, validators=[MinValueValidator(0)]) ), ] ``` #### 1.2 Update LimitService **File:** `backend/igny8_core/business/billing/services/limit_service.py` **Remove from MONTHLY_LIMIT_MAPPINGS:** ```python # DELETE THESE: 'content_ideas': {...}, # ❌ DELETE 'content_words': {...}, # ❌ DELETE 'images_basic': {...}, # ❌ DELETE 'images_premium': {...}, # ❌ DELETE 'image_prompts': {...}, # ❌ DELETE ``` **Keep/Add:** ```python MONTHLY_LIMIT_MAPPINGS = { # ONLY THIS ONE: 'ahrefs_queries': { 'plan_field': 'max_ahrefs_queries', 'usage_field': 'usage_ahrefs_queries', 'display_name': 'Ahrefs Keyword Research', }, } # Remove from HARD_LIMIT_MAPPINGS: 'clusters': {...}, # ❌ DELETE (or keep if needed) ``` #### 1.3 Update Serializers **File:** `backend/igny8_core/auth/serializers.py` ```python class PlanSerializer(serializers.ModelSerializer): class Meta: fields = [ 'id', 'name', 'slug', 'price', 'billing_cycle', # KEEP ONLY THESE LIMITS: 'max_sites', 'max_users', 'max_keywords', 'max_ahrefs_queries', # NEW # CREDITS: 'included_credits', 'extra_credit_price', # REMOVE ALL OTHER max_* fields ] ``` #### 1.4 Remove Limit Checks from AI Operations **Search and Remove:** ```bash # Find all places checking monthly limits grep -r "check_monthly_limit.*content_ideas" backend/ grep -r "check_monthly_limit.*content_words" backend/ grep -r "check_monthly_limit.*images" backend/ grep -r "increment_usage.*content_ideas" backend/ ``` **Replace with credit checks only:** ```python # OLD (REMOVE): LimitService.check_monthly_limit(account, 'content_ideas', 1) LimitService.increment_usage(account, 'content_ideas', 1) # NEW (KEEP): CreditService.check_credits(account, required_credits) # Then after operation: CreditService.deduct_credits_for_operation(...) ``` #### 1.5 Add Credit Balance Check to Automation **File:** `backend/igny8_core/business/automation/services.py` (or similar) ```python from igny8_core.business.billing.services.credit_service import CreditService from igny8_core.business.billing.exceptions import InsufficientCreditsError def run_automation(automation_config, account): """Run automation with credit pre-check""" # Estimate credits needed estimated_credits = estimate_automation_cost(automation_config) # Check balance BEFORE starting try: CreditService.check_credits(account, estimated_credits) except InsufficientCreditsError: raise AutomationError( f"Insufficient credits. Need {estimated_credits}, have {account.credits}. " f"Please add credits to continue." ) # Run automation stages for stage in automation_config.stages: # Each stage checks/deducts its own credits run_stage(stage, account) ``` #### 1.6 Enforce Keywords Limit Properly **Files to Update:** - `backend/igny8_core/business/planning/views.py` (keyword creation) - `backend/igny8_core/api/endpoints/seed_keywords.py` (SeedKeyword import) ```python from igny8_core.business.billing.services.limit_service import LimitService, HardLimitExceededError # Before creating keywords: try: LimitService.check_hard_limit(account, 'keywords', num_keywords_to_add) except HardLimitExceededError as e: return Response({ 'error': 'keyword_limit_exceeded', 'message': f'You have reached your keyword limit ({plan.max_keywords}). Upgrade your plan to add more.', 'current_count': current_count, 'limit': plan.max_keywords, 'upgrade_url': '/account/plans/upgrade' }, status=402) # If check passes, create keywords keywords = Keywords.objects.bulk_create([...]) ``` --- ### 6.2 Phase 2: Frontend Cleanup (Week 2) #### 2.1 Remove Duplicate Data from Plans & Billing Page **File:** `frontend/src/pages/account/PlansAndBillingPage.tsx` **Current Plan Tab - Remove:** ```tsx // ❌ DELETE: Credit balance display (move to Usage only) // ❌ DELETE: Usage breakdown charts // ❌ DELETE: Limit progress bars // ❌ DELETE: "Credits used this month" details ``` **Current Plan Tab - Keep:** ```tsx // ✅ KEEP: Plan name, price, billing cycle // ✅ KEEP: Renewal date // ✅ KEEP: Brief summary text: "50 Pages/Articles • 2 Sites • 2 Users" // ✅ KEEP: Upgrade CTA button ``` #### 2.2 Redesign Usage Page with Multi-Dimensional Insights **File:** `frontend/src/pages/account/UsageAnalyticsPage.tsx` **New Structure:** ```tsx /** * Usage & Insights Page - Complete Redesign * * Tab 1: Overview (NEW) * - Credit balance cards * - Quick stats (sites, users, keywords count) * - Period selector (7, 30, 90 days) * - Top metrics * * Tab 2: Your Limits * - Sites, Users, Keywords progress bars * - Ahrefs queries remaining * * Tab 3: Credit Insights (NEW) * - Credits by Site * - Credits by Content Type (articles, images, etc.) * - Credits by Image Quality (basic, quality, premium) * - Credits by Automation * - Timeline chart (7/30/90 days) * * Tab 4: Activity Log * - Detailed transaction history */ type TabType = 'overview' | 'limits' | 'insights' | 'activity'; ``` **Tab 1: Overview (NEW)** ```tsx
{/* Credit Balance Card */}
Credits Available
{credits.toLocaleString()}
{/* Sites Used Card */}
Sites
{sitesCount} / {maxSites}
{/* Team Members Card */}
Team Members
{usersCount} / {maxUsers}
{/* Keywords Card */}
Keywords
{keywordsCount.toLocaleString()} / {maxKeywords.toLocaleString()}
{/* Period Selector */}
``` **Tab 3: Credit Insights (NEW)** ```tsx {/* Credits by Site */}

Credits by Site

{sites.map(site => (
{site.name}
{site.credits_used.toLocaleString()} credits
))}
{/* Credits by Content Type */}

Credits by Content Type

{/* Credits by Image Quality */}

Image Generation by Quality

{imageQualityBreakdown.map(tier => (
{tier.quality} Quality
{tier.count} images • {tier.credits_used} credits
({tier.credits_per_image} credits each)
))}
{/* Credits by Automation */}

Automation Runs

{automations.map(auto => (
{auto.name}
{auto.runs_count} runs • {auto.credits_used} credits
))}
{/* Timeline Chart */}

Credit Usage Over Time

``` #### 2.3 Update API Interfaces **File:** `frontend/src/services/billing.api.ts` ```typescript // REMOVE from Plan interface: export interface Plan { id: number; name: string; price: number; // KEEP ONLY THESE LIMITS: max_sites?: number; max_users?: number; max_keywords?: number; max_ahrefs_queries?: number; // NEW included_credits?: number; // REMOVE THESE: // max_content_ideas?: number; // ❌ DELETE // max_content_words?: number; // ❌ DELETE // max_images_basic?: number; // ❌ DELETE // max_images_premium?: number; // ❌ DELETE // max_image_prompts?: number; // ❌ DELETE // max_clusters?: number; // ❌ DELETE } // UPDATE UsageSummary interface: export interface UsageSummary { account_id: number; plan_name: string; period_start: string; period_end: string; hard_limits: { sites?: LimitUsage; users?: LimitUsage; keywords?: LimitUsage; // clusters?: LimitUsage; // ❌ DELETE }; monthly_limits: { ahrefs_queries?: LimitUsage; // ONLY THIS ONE // content_ideas?: LimitUsage; // ❌ DELETE // content_words?: LimitUsage; // ❌ DELETE // images_basic?: LimitUsage; // ❌ DELETE // images_premium?: LimitUsage; // ❌ DELETE // image_prompts?: LimitUsage; // ❌ DELETE }; } // NEW: Multi-dimensional insights export interface CreditInsights { period_days: number; total_credits_used: number; by_site: Array<{ site_id: number; site_name: string; credits_used: number; percentage: number; }>; by_operation: Array<{ operation_type: string; display_name: string; credits_used: number; count: number; percentage: number; }>; by_image_quality: Array<{ quality_tier: 'basic' | 'quality' | 'premium'; model_name: string; credits_per_image: number; images_generated: number; total_credits: number; }>; by_automation: Array<{ automation_id: number; automation_name: string; runs_count: number; credits_used: number; }>; timeline: Array<{ date: string; credits_used: number; }>; } // NEW API function export async function getCreditInsights(days: number = 30): Promise { return fetchAPI(`/v1/billing/credits/insights/?days=${days}`); } ``` #### 2.4 Update UsageLimitsPanel Component **File:** `frontend/src/components/billing/UsageLimitsPanel.tsx` ```tsx // REMOVE monthly limit configs for deleted fields const monthlyLimitConfig = { // ONLY THIS ONE: ahrefs_queries: { icon: , color: 'purple' as const, description: 'Keyword research queries per month' }, // DELETE THESE: // content_ideas: {...}, // ❌ DELETE // content_words: {...}, // ❌ DELETE // images_basic: {...}, // ❌ DELETE // images_premium: {...}, // ❌ DELETE // image_prompts: {...}, // ❌ DELETE }; // KEEP hard limits const hardLimitConfig = { sites: { icon: , color: 'success' }, users: { icon: , color: 'info' }, keywords: { icon: , color: 'purple' }, // clusters: {...}, // ❌ DELETE if not needed }; ``` #### 2.5 Use User-Friendly Terminology **Global Search & Replace:** | Technical Term | User-Friendly Term | |---------------|-------------------| | "API Activity" | "Activity Log" or "Recent Actions" | | "API Operations" | "Actions" or "Activities" | | "operation_type" | "Action Type" | | "Monthly Limits" | "Monthly Allowances" | | "Hard Limits" | "Plan Limits" | | "Credits Used" | "Credits Spent" | | "Balance" | "Credits Available" | --- ### 6.3 Phase 3: Keyword Research Implementation (Week 3) #### 3.1 Backend: Ahrefs Service **Create:** `backend/igny8_core/business/keywords/ahrefs_service.py` ```python from igny8_core.business.billing.services.limit_service import LimitService, MonthlyLimitExceededError import requests class AhrefsService: """Service for Ahrefs keyword research with monthly limit enforcement""" @staticmethod def query_keywords(account, query_params): """ Query Ahrefs for keywords with limit enforcement. Args: account: Account instance query_params: dict with search parameters Returns: dict: Ahrefs API response with keyword data Raises: MonthlyLimitExceededError: If monthly Ahrefs query limit exceeded """ # Check monthly limit BEFORE querying try: LimitService.check_monthly_limit(account, 'ahrefs_queries', 1) except MonthlyLimitExceededError as e: raise AhrefsQueryLimitExceeded( f"You've used all your Ahrefs queries this month. " f"Limit: {account.plan.max_ahrefs_queries}. " f"Resets on {account.usage_period_end.strftime('%B %d, %Y')}. " f"Upgrade your plan for more queries." ) # Make Ahrefs API call try: response = requests.post( 'https://api.ahrefs.com/v3/site-explorer/keywords', headers={'Authorization': f'Bearer {settings.AHREFS_API_KEY}'}, json=query_params ) response.raise_for_status() results = response.json() # Increment usage counter LimitService.increment_usage(account, 'ahrefs_queries', 1) return results except requests.RequestException as e: logger.error(f"Ahrefs API error: {e}") raise AhrefsAPIError("Failed to fetch keyword data from Ahrefs") ``` #### 3.2 Frontend: Keyword Research Page **Create:** `frontend/src/pages/Planner/KeywordResearchPage.tsx` ```tsx /** * Keyword Research Page - Two Options * 1. Browse IGNY8 pre-researched keywords (free) * 2. Research with Ahrefs (monthly limit) */ type ResearchTab = 'browse' | 'ahrefs'; export default function KeywordResearchPage() { const [activeTab, setActiveTab] = useState('browse'); const [ahrefsLimit, setAhrefsLimit] = useState({ used: 0, limit: 50 }); return ( <> {/* Tab Selector */}
{/* Tab Content */} {activeTab === 'browse' && ( )} {activeTab === 'ahrefs' && ( { setAhrefsLimit(prev => ({ ...prev, used: prev.used + 1 })); }} /> )} ); } ``` **Browse Keywords Panel (Existing SeedKeywords):** ```tsx function BrowseKeywordsPanel() { return (

Pre-Researched High-Opportunity Keywords

Browse thousands of analyzed keywords, ready to use.

{/* Filters */}
{/* Results Table */} {keywords.map(kw => ( ))}
Keyword Search Volume Difficulty Opportunity Score
{kw.keyword} {kw.search_volume} {kw.difficulty} {kw.opportunity_score}/100
); } ``` **Ahrefs Research Panel (NEW):** ```tsx function AhrefsResearchPanel({ limit, onQuerySuccess }) { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const remaining = limit.limit - limit.used; const canQuery = remaining > 0; const handleSearch = async () => { if (!canQuery) { toast.error('You have used all your Ahrefs queries this month.'); return; } try { setLoading(true); const data = await fetchAPI('/v1/keywords/ahrefs/search/', { method: 'POST', body: JSON.stringify({ query }) }); setResults(data.keywords); onQuerySuccess(); toast.success('Keywords fetched from Ahrefs!'); } catch (error) { toast.error(error.message); } finally { setLoading(false); } }; return (

Live Keyword Research with Ahrefs

10 ? 'success' : 'warning'}> {remaining} / {limit.limit} queries remaining
{!canQuery && (
No queries remaining
Your limit resets on {resetDate}. Upgrade for more queries.
)} {canQuery && ( <>
setQuery(e.target.value)} onKeyPress={e => e.key === 'Enter' && handleSearch()} />
{/* Results */} {results.length > 0 && ( {results.map((kw, idx) => ( ))}
Keyword Volume Difficulty CPC
{kw.keyword} {kw.volume.toLocaleString()} {kw.difficulty} ${kw.cpc}
)} )}
); } ``` --- ### 6.4 Phase 4: Validation & Enforcement (Week 4) #### 4.1 Credit Balance Validation Checklist **File:** Search all AI operation services ```python # REQUIRED PATTERN for all AI operations: # 1. BEFORE operation - estimate and check estimated_credits = estimate_credits_needed(operation_params) CreditService.check_credits(account, estimated_credits) # 2. EXECUTE operation result = ai_service.execute(operation_params) # 3. AFTER operation - calculate actual and deduct actual_tokens = result.usage.total_tokens actual_credits = CreditService.calculate_credits_from_tokens( operation_type='content_generation', tokens_input=result.usage.prompt_tokens, tokens_output=result.usage.completion_tokens ) CreditService.deduct_credits_for_operation( account=account, operation_type='content_generation', amount=actual_credits, model=result.model, tokens_in=result.usage.prompt_tokens, tokens_out=result.usage.completion_tokens, metadata={'content_id': content.id} ) ``` **Locations to Verify:** - ✅ Content generation service - ✅ Image generation service - ✅ Idea generation service - ✅ Clustering service - ✅ Internal linking service - ✅ Optimization service - ✅ Automation runner (PRE-CHECK before starting!) #### 4.2 Automation Credit Pre-Check **File:** `backend/igny8_core/business/automation/services.py` ```python def estimate_automation_cost(automation_config): """Estimate credits needed for full automation run""" estimated = 0 for stage in automation_config.stages: if stage.type == 'clustering': estimated += 10 # Base estimate elif stage.type == 'idea_generation': estimated += stage.num_ideas * 2 elif stage.type == 'content_generation': estimated += stage.num_articles * 50 # Rough estimate elif stage.type == 'image_generation': estimated += stage.num_images * stage.credits_per_image elif stage.type == 'linking': estimated += stage.num_articles * 8 # Add 20% buffer return int(estimated * 1.2) def run_automation(automation_config, account, trigger='manual'): """ Run automation with upfront credit validation. Raises: InsufficientCreditsError: If not enough credits available """ # Estimate total cost estimated_credits = estimate_automation_cost(automation_config) # CHECK CREDITS BEFORE STARTING try: CreditService.check_credits(account, estimated_credits) except InsufficientCreditsError: # Log failed attempt AutomationRun.objects.create( automation=automation_config, account=account, status='failed', error_message=f'Insufficient credits. Need {estimated_credits}, have {account.credits}.', trigger=trigger ) raise # Create run record run = AutomationRun.objects.create( automation=automation_config, account=account, status='running', estimated_credits=estimated_credits, trigger=trigger ) try: # Execute stages (each deducts credits) for stage in automation_config.stages: execute_stage(stage, account, run) # Mark complete run.status = 'completed' run.actual_credits = run.credits_used run.save() except Exception as e: run.status = 'failed' run.error_message = str(e) run.save() raise ``` #### 4.3 Frontend Credit Check Before Actions **Component:** Add to all credit-consuming actions ```tsx // Hook for credit validation function useCreditCheck() { const { user } = useAuthStore(); const checkCredits = useCallback(async (estimatedCredits: number) => { if (user.account.credits < estimatedCredits) { const confirmed = await showConfirmDialog({ title: 'Insufficient Credits', message: `This action requires approximately ${estimatedCredits} credits, but you only have ${user.account.credits} available.`, confirmText: 'Add Credits', cancelText: 'Cancel', }); if (confirmed) { navigate('/account/plans'); } return false; } return true; }, [user]); return { checkCredits }; } // Usage example: function GenerateContentButton({ taskId }) { const { checkCredits } = useCreditCheck(); const handleGenerate = async () => { // Estimate credits (rough estimate: 50 for article) const canProceed = await checkCredits(50); if (!canProceed) return; // Proceed with generation await generateContent(taskId); }; return ; } ``` --- ### 6.5 Phase 5: Testing & Validation (Week 5) #### Test Cases **Limit Enforcement:** - [ ] Try to create site when at `max_sites` → Should fail with upgrade prompt - [ ] Try to invite user when at `max_users` → Should fail with upgrade prompt - [ ] Try to add 100 keywords when 50 slots remain and limit is 1000 → Should succeed - [ ] Try to add 100 keywords when 30 slots remain → Should fail - [ ] Try to query Ahrefs when at monthly limit → Should fail with reset date - [ ] Query Ahrefs successfully → Counter increments - [ ] Monthly reset → Ahrefs counter resets to 0 **Credit Validation:** - [ ] Try to generate content with 0 credits → Should fail immediately - [ ] Try to run automation with insufficient credits → Should fail before starting - [ ] Generate content → Credits deducted correctly based on tokens - [ ] Generate image (basic) → 1 credit deducted - [ ] Generate image (quality) → 5 credits deducted - [ ] Generate image (premium) → 15 credits deducted - [ ] Run automation → All stages check/deduct credits properly **Page Reorganization:** - [ ] Plans & Billing → Current Plan: NO credit usage details - [ ] Plans & Billing → Current Plan: Brief summary text only - [ ] Usage → Overview: Shows credit balance, sites/users/keywords count - [ ] Usage → Limits: Shows only 4 limits (sites, users, keywords, ahrefs) - [ ] Usage → Insights: Shows multi-dimensional breakdowns - [ ] No duplicate data between Plans & Usage **Terminology:** - [ ] No "API" references in user-facing text - [ ] "Operations" changed to "Actions" or "Activities" - [ ] User-friendly language throughout --- ## 7. Updated Page Reorganization ### 7.1 Page Reorganization (High Priority) 🔥 **Problem:** Data duplication creates confusion and maintenance overhead. **Proposed Solution:** Clear separation of concerns between Plans & Billing vs Usage. #### Plans & Billing → Focus on FINANCIAL aspects **Tab 1: Current Plan** - Show: Plan name, price, billing cycle, renewal date - Show: Brief limits summary (e.g., "50 Pages/Articles, 2 Sites, 2 Users") - **Remove:** Detailed limit progress bars (move to Usage) - **Remove:** Credits used this month breakdown (move to Usage) - Action: "Upgrade Plan" button **Tab 2: Upgrade Plan** - Show: Pricing table with plan comparison - Show: Plan features and limits (static, for comparison) - Action: Purchase/upgrade plan **Tab 3: Billing History** - Show: Invoices (with download PDF) - Show: Payment methods management - Show: Credit package purchases (financial transactions only) #### Usage → Focus on CONSUMPTION tracking **Tab 1: Limits & Usage** _(Keep as is - this is perfect)_ - Show: All hard limits with progress bars (sites, users, keywords, clusters) - Show: All monthly limits with progress bars (ideas, words, images) - Show: Days until reset for monthly limits - Show: Credit balance and monthly usage - Action: "Upgrade for more" CTA when approaching limits **Tab 2: Credit History** _(Keep as is)_ - Show: Credit transaction log (purchases, deductions, adjustments) - Show: Operation details (what consumed credits) - Filter: By operation type, date range **Tab 3: Activity Log** _(Keep as is)_ - Show: API operations by type - Show: Total operations count - Show: Breakdown by operation type #### Summary of Changes | Page | Current State | Proposed Change | |------|--------------|-----------------| | **Plans & Billing → Current Plan** | Shows credits, usage, limits | Remove detailed usage, keep financial summary only | | **Plans & Billing → History** | Invoices and payments | Keep as is (financial focus) | | **Usage → Limits & Usage** | Detailed limits panel | Keep as is (consumption focus) | | **Usage → Credit History** | Transaction log | Keep as is (consumption focus) | **Rationale:** - **Plans & Billing** = "What am I paying for?" (financial/subscription management) - **Usage** = "What am I using?" (consumption monitoring) --- ### 7.1 Plans & Billing Page (Simplified) **Purpose:** Financial management and subscription control **Tab 1: Current Plan** ```tsx

{plan.name} Plan

${plan.price}/month
Renews on {renewalDate}
{/* Brief Summary - NO detailed limits */}
Your plan includes: {plan.included_credits} credits per month • {plan.max_sites} sites • {plan.max_users} team members
{/* Upgrade CTA */}
{/* ❌ REMOVE: Credit usage charts */} {/* ❌ REMOVE: Limit progress bars */} {/* ❌ REMOVE: "Credits used this month" */} ``` **Tab 2: Upgrade Plan** _(No changes needed)_ **Tab 3: Billing History** _(No changes needed)_ --- ### 7.2 Usage Page (Multi-Dimensional Insights) **Purpose:** Monitor consumption and optimize usage **Tab 1: Overview (NEW)** ```tsx {/* Quick Stats Cards */}
} label="Credits Available" value={credits.toLocaleString()} color="brand" /> } label="Sites" value={`${sitesCount} / ${maxSites}`} color="success" /> } label="Team Members" value={`${usersCount} / ${maxUsers}`} color="info" /> } label="Keywords" value={`${keywordsCount} / ${maxKeywords}`} color="purple" />
{/* Period Selector */} {/* Top Metrics for Selected Period */}
Credits Spent
{periodCredits.toLocaleString()}
Articles Created
{periodArticles}
Images Generated
{periodImages}
``` **Tab 2: Your Limits** ```tsx {/* Only 4 limits total */} } current={sitesCount} limit={maxSites} type="permanent" /> } current={usersCount} limit={maxUsers} type="permanent" /> } current={keywordsCount} limit={maxKeywords} type="permanent" /> } current={ahrefsUsed} limit={ahrefsLimit} type="monthly" daysUntilReset={daysUntilReset} /> ``` **Tab 3: Credit Insights (NEW)** ```tsx {/* Multi-dimensional breakdowns */} {/* By Site */}

Credits by Site

See which sites consume the most credits
{insights.by_site.map(site => (
{site.name} {site.credits_used.toLocaleString()} credits ({site.percentage}%)
))}
{/* By Action Type */}

Credits by Action Type

{insights.by_operation.map(op => (
{op.display_name} {op.credits_used} credits ({op.count} times)
))}
{/* By Image Quality */}

Image Generation Breakdown

Credits vary by quality tier
{insights.by_image_quality.map(tier => (
{tier.quality_tier} Quality
{tier.images_generated} images × {tier.credits_per_image} credits = {tier.total_credits} credits
{tier.model_name}
))}
{/* By Automation */}

Automation Runs

{insights.by_automation.map(auto => (
{auto.name}
{auto.runs_count} runs • {auto.credits_used} credits total
))}
{/* Timeline Chart */}

Credit Usage Over Time

``` **Tab 4: Activity Log** _(Keep existing, but rename from "API Activity")_ --- ## 8. Backend Changes Summary ### 8.1 Files to Modify | File | Action | Description | |------|--------|-------------| | `backend/igny8_core/auth/models.py` | Edit | Remove unused limit fields from Plan & Account | | `backend/igny8_core/auth/migrations/0XXX_*.py` | Create | Migration to remove fields | | `backend/igny8_core/business/billing/services/limit_service.py` | Edit | Remove unused limit mappings | | `backend/igny8_core/auth/serializers.py` | Edit | Remove fields from PlanSerializer | | `backend/igny8_core/business/keywords/ahrefs_service.py` | Create | New service for Ahrefs integration | | `backend/igny8_core/business/automation/services.py` | Edit | Add credit pre-check | | `backend/igny8_core/business/planning/views.py` | Edit | Add keyword limit enforcement | | `backend/igny8_core/modules/billing/views.py` | Edit | Add credit insights endpoint | ### 8.2 New API Endpoints ```python # Credit Insights (NEW) GET /api/v1/billing/credits/insights/?days=30 Response: CreditInsights object with multi-dimensional breakdowns # Ahrefs Search (NEW) POST /api/v1/keywords/ahrefs/search/ Body: { "query": "digital marketing", "country": "us" } Response: { "keywords": [...], "queries_remaining": 42 } ``` --- ## 9. Frontend Changes Summary ### 9.1 Files to Modify | File | Action | Description | |------|--------|-------------| | `frontend/src/pages/account/PlansAndBillingPage.tsx` | Edit | Remove credit usage details from Current Plan tab | | `frontend/src/pages/account/UsageAnalyticsPage.tsx` | Rewrite | Add Overview & Credit Insights tabs | | `frontend/src/components/billing/UsageLimitsPanel.tsx` | Edit | Remove unused limits, add Ahrefs | | `frontend/src/services/billing.api.ts` | Edit | Remove unused fields, add new interfaces | | `frontend/src/pages/Planner/KeywordResearchPage.tsx` | Create | New page for keyword research | | `frontend/src/components/keywords/AhrefsResearchPanel.tsx` | Create | Ahrefs search component | ### 9.2 New Components ```tsx // Credit Insights Components CreditInsightsDashboard.tsx CreditsBySiteWidget.tsx CreditsByOperationWidget.tsx CreditsByImageQualityWidget.tsx CreditsByAutomationWidget.tsx CreditTimelineChart.tsx // Ahrefs Research KeywordResearchPage.tsx AhrefsResearchPanel.tsx BrowseKeywordsPanel.tsx ``` --- ## 10. Testing Checklist **Problem:** Keywords have `max_keywords` limit defined but enforcement is inconsistent. **Required Changes:** 1. **Add Pre-Create Checks:** Location: `backend/igny8_core/business/planning/views.py` (or wherever keywords are created) ```python # Before creating keywords from igny8_core.business.billing.services.limit_service import LimitService def create_keywords(account, keyword_data): # Check if adding keywords would exceed limit num_new_keywords = len(keyword_data) LimitService.check_hard_limit(account, 'keywords', num_new_keywords) # If check passes, create keywords keywords = Keywords.objects.bulk_create([...]) return keywords ``` 2. **Add Check to SeedKeyword Import:** Location: `backend/igny8_core/api/endpoints/seed_keywords.py` (or similar) ```python # In addSeedKeywordsToWorkflow endpoint def add_seed_keywords_to_workflow(seed_keyword_ids, site_id, sector_id): account = Site.objects.get(id=site_id).account # Check limit BEFORE importing LimitService.check_hard_limit(account, 'keywords', len(seed_keyword_ids)) # Import keywords for seed_kw in SeedKeyword.objects.filter(id__in=seed_keyword_ids): Keywords.objects.create(...) ``` 3. **Add Check to Bulk Import:** Ensure CSV/Excel keyword imports also check limits before processing. 4. **User-Facing Error Messages:** ```python try: LimitService.check_hard_limit(account, 'keywords', 50) except HardLimitExceededError as e: return Response({ 'error': 'keyword_limit_exceeded', 'message': 'You have reached your keyword limit. Upgrade your plan to add more keywords.', 'current': 950, 'limit': 1000, 'upgrade_url': '/account/plans/upgrade' }, status=402) ``` **Testing:** - ✅ Try to import keywords beyond limit → Should fail with clear error - ✅ Try to create single keyword at limit → Should fail - ✅ Try to bulk import → Should fail if total exceeds - ✅ Error message should show current count, limit, and upgrade CTA --- ### 6.3 Implement Ahrefs Query Limit (Medium Priority) **Problem:** Future feature needs limit definition and enforcement. **Required Changes:** 1. **Database Migration:** ```python # Create migration: 0XXX_add_ahrefs_query_limits.py operations = [ migrations.AddField( model_name='plan', name='max_ahrefs_queries', field=models.IntegerField(default=50, validators=[MinValueValidator(0)]), ), migrations.AddField( model_name='account', name='usage_ahrefs_queries', field=models.IntegerField(default=0, validators=[MinValueValidator(0)]), ), ] ``` 2. **Update Plan Admin:** ```python # backend/igny8_core/auth/admin.py - PlanAdmin fieldsets = ( # ... existing fieldsets ... ('Monthly Limits (Reset on Billing Cycle)', { 'fields': ( 'max_content_ideas', 'max_content_words', 'max_images_basic', 'max_images_premium', 'max_image_prompts', 'max_ahrefs_queries', # ADD THIS ), }), ) ``` 3. **Update LimitService:** ```python # backend/igny8_core/business/billing/services/limit_service.py MONTHLY_LIMIT_MAPPINGS = { # ... existing ... 'ahrefs_queries': { 'plan_field': 'max_ahrefs_queries', 'usage_field': 'usage_ahrefs_queries', 'display_name': 'Ahrefs Keyword Research Queries', }, } ``` 4. **Add to Plan Serializer:** ```python # backend/igny8_core/auth/serializers.py - PlanSerializer class PlanSerializer(serializers.ModelSerializer): class Meta: fields = [ # ... existing fields ... 'max_ahrefs_queries', # ADD THIS ] ``` 5. **Enforce in Ahrefs Service:** ```python # Create new service: backend/igny8_core/business/keywords/ahrefs_service.py from igny8_core.business.billing.services.limit_service import LimitService class AhrefsService: @staticmethod def query_keywords(account, query_params): # Check monthly limit LimitService.check_monthly_limit(account, 'ahrefs_queries', 1) # Execute Ahrefs API call results = ahrefs_api.search_keywords(**query_params) # Increment usage counter LimitService.increment_usage(account, 'ahrefs_queries', 1) return results ``` 6. **Update Frontend:** ```tsx // frontend/src/components/billing/UsageLimitsPanel.tsx const monthlyLimitConfig = { // ... existing ... ahrefs_queries: { icon: , color: 'purple' as const }, }; ``` **Plan Values (Suggested):** | Plan | max_ahrefs_queries/month | |------|-------------------------| | Free | 0 (no access) | | Starter | 50 queries | | Growth | 200 queries | | Scale | 500 queries | --- ### 6.4 Add Cluster Limit Enforcement (Low Priority) **Current State:** Clusters have `max_clusters` limit but may not be consistently enforced. **Recommendation:** Apply same enforcement pattern as keywords: 1. Check limit before creating clusters 2. Add to all cluster creation flows (auto-clustering, manual clustering) 3. User-facing error messages --- ### 6.5 Monthly Limits Reset Automation (Medium Priority) **Current State:** Monthly limits should reset at billing cycle, but automation may not be in place. **Check Required:** - Is there a scheduled task that calls `LimitService.reset_monthly_limits(account)`? - When do subscriptions renew? - How are usage fields reset? **Recommended:** ```python # backend/igny8_core/business/billing/tasks.py (Celery) from celery import shared_task from igny8_core.auth.models import Account from igny8_core.business.billing.services.limit_service import LimitService @shared_task def reset_monthly_limits_for_accounts(): """Reset monthly limits for accounts whose billing period has ended""" from django.utils import timezone now = timezone.now() accounts = Account.objects.filter( usage_period_end__lte=now, status='active' ) for account in accounts: LimitService.reset_monthly_limits(account) logger.info(f"Reset monthly limits for account {account.id}") ``` --- ## 7. Credit System (Working Well) ✅ **No changes needed** - the credit system is well-designed: 1. **Token-Based Calculation:** Uses `AIModelConfig.tokens_per_credit` for accurate pricing 2. **Image Fixed Pricing:** Uses `AIModelConfig.credits_per_image` (1, 5, or 15) 3. **Proper Logging:** `CreditUsageLog` tracks every operation with metadata 4. **Transaction Ledger:** `CreditTransaction` maintains audit trail 5. **Balance Tracking:** Account.credits is source of truth **Already Enforced Properly:** - ✅ Pre-flight balance checks before operations - ✅ Token-based credit calculation after API calls - ✅ Proper transaction logging - ✅ Clear error messages (402 Payment Required) --- ## 8. Summary of Required Backend Changes ### 8.1 Database Schema Changes **Add to Plan Model:** ```python max_ahrefs_queries = models.IntegerField(default=50, validators=[MinValueValidator(0)]) ``` **Add to Account Model:** ```python usage_ahrefs_queries = models.IntegerField(default=0, validators=[MinValueValidator(0)]) ``` ### 8.2 Enforcement Additions **Locations Needing Limit Checks:** | Location | Limit Type | Method to Add | |----------|-----------|--------------| | Keyword creation | Hard | `LimitService.check_hard_limit(account, 'keywords', count)` | | SeedKeyword import | Hard | `LimitService.check_hard_limit(account, 'keywords', count)` | | Bulk keyword import | Hard | `LimitService.check_hard_limit(account, 'keywords', count)` | | Ahrefs query (future) | Monthly | `LimitService.check_monthly_limit(account, 'ahrefs_queries', 1)` | | Cluster creation | Hard | `LimitService.check_hard_limit(account, 'clusters', count)` | ### 8.3 Service Updates **LimitService Mappings:** ```python # Add to MONTHLY_LIMIT_MAPPINGS 'ahrefs_queries': { 'plan_field': 'max_ahrefs_queries', 'usage_field': 'usage_ahrefs_queries', 'display_name': 'Ahrefs Keyword Research Queries', } ``` --- ## 9. Summary of Required Frontend Changes ### 9.1 Page Content Adjustments **Plans & Billing Page:** ```tsx // Remove from Current Plan tab: - ❌ Detailed credit usage breakdown (move to Usage) - ❌ Limit progress bars (move to Usage) // Keep in Current Plan tab: - ✅ Plan name, price, renewal date - ✅ Brief limits summary (text only) - ✅ Upgrade CTA // Keep History tab as is ``` **Usage Analytics Page:** ```tsx // Keep all tabs as is - no changes needed // This page is perfectly organized ``` ### 9.2 Component Updates **UsageLimitsPanel.tsx:** ```tsx // Add Ahrefs queries to monthly limits config const monthlyLimitConfig = { // ... existing ... ahrefs_queries: { icon: , color: 'purple' as const }, }; ``` **billing.api.ts:** ```typescript // Add to Plan interface export interface Plan { // ... existing fields ... max_ahrefs_queries?: number; // ADD THIS } // Add to UsageSummary interface export interface UsageSummary { // ... existing ... monthly_limits: { // ... existing ... ahrefs_queries?: LimitUsage; // ADD THIS }; } ``` --- ## 10. Implementation Priority ### Phase 1: Critical Fixes (Do First) 🔥 1. **Enforce Keywords Limit** (Backend) - Add checks to keyword creation flows - Estimated effort: 4 hours - Impact: Prevents users from exceeding limits 2. **Page Reorganization** (Frontend) - Remove duplicate data from Plans & Billing → Current Plan - Estimated effort: 2 hours - Impact: Reduces user confusion ### Phase 2: Future Features (Do When Implementing Ahrefs) 3. **Implement Ahrefs Query Limit** (Backend + Frontend) - Database migration - LimitService mapping - Enforcement in Ahrefs service - Frontend display - Estimated effort: 6 hours - Impact: Ready for Ahrefs integration ### Phase 3: Nice-to-Have Improvements 4. **Enforce Cluster Limit** (Backend) - Similar to keywords enforcement - Estimated effort: 2 hours 5. **Monthly Limits Reset Automation** (Backend) - Celery task for auto-reset - Estimated effort: 3 hours --- --- ## 11. Migration Strategy ### Week-by-Week Rollout **Week 1: Backend Foundation** - [ ] Create database migration to remove unused fields - [ ] Update LimitService mappings - [ ] Update serializers - [ ] Add Ahrefs service skeleton - [ ] Deploy to staging **Week 2: Enforcement** - [ ] Add keyword limit checks to all entry points - [ ] Add automation credit pre-checks - [ ] Test all validation flows - [ ] Deploy to staging **Week 3: Frontend Cleanup** - [ ] Remove duplicate data from Plans & Billing - [ ] Update UsageLimitsPanel - [ ] Update terminology (remove "API", "operations") - [ ] Deploy to staging **Week 4: New Features** - [ ] Build Credit Insights tab - [ ] Build Keyword Research page - [ ] Integrate Ahrefs (when ready) - [ ] Add multi-dimensional widgets - [ ] Deploy to staging **Week 5: Testing & Production** - [ ] Full regression testing - [ ] User acceptance testing - [ ] Deploy to production - [ ] Monitor for issues --- ## 12. Final Limits Configuration ### 12.1 Database Schema (After Cleanup) **Plan Model - FINAL:** ```python class Plan(models.Model): # Basic Info name = models.CharField(max_length=255) slug = models.SlugField(unique=True) price = models.DecimalField(max_digits=10, decimal_places=2) # ONLY 4 LIMITS: max_sites = models.IntegerField(default=1) max_users = models.IntegerField(default=1) max_keywords = models.IntegerField(default=1000) max_ahrefs_queries = models.IntegerField(default=50) # Credits included_credits = models.IntegerField(default=0) extra_credit_price = models.DecimalField(max_digits=10, decimal_places=2) ``` **Account Model - FINAL:** ```python class Account(models.Model): # Credits credits = models.IntegerField(default=0) # ONLY 1 Usage Tracker: usage_ahrefs_queries = models.IntegerField(default=0) # Billing Period usage_period_start = models.DateTimeField(null=True) usage_period_end = models.DateTimeField(null=True) ``` ### 12.2 Suggested Plan Values | Plan | Price | Included Credits | Sites | Users | Keywords | Ahrefs Queries/mo | |------|-------|-----------------|-------|-------|----------|-------------------| | **Free** | $0 | 2,000 | 1 | 1 | 100 | 0 | | **Starter** | $49 | 10,000 | 2 | 2 | 1,000 | 50 | | **Growth** | $149 | 40,000 | 5 | 3 | 5,000 | 200 | | **Scale** | $399 | 120,000 | Unlimited | 5 | 20,000 | 500 | --- ## 13. Success Criteria ### 13.1 Technical Success - [ ] All unused limit fields removed from database - [ ] Migration runs successfully without data loss - [ ] All 4 limits properly enforced - [ ] Credit balance checked before ALL operations - [ ] Automation pre-checks credit balance - [ ] Ahrefs queries counted and limited - [ ] No duplicate data across pages - [ ] User-friendly terminology throughout ### 13.2 User Experience Success - [ ] Users understand the simple 4-limit model - [ ] Clear separation: Plans & Billing = financial, Usage = consumption - [ ] Multi-dimensional insights provide actionable data - [ ] Keyword research flow is intuitive - [ ] Credit exhaustion messages are clear and actionable - [ ] Upgrade prompts appear at right moments ### 13.3 Business Success - [ ] Reduced support questions about limits - [ ] Clearer upgrade paths - [ ] Better credit consumption visibility drives upgrades - [ ] Ahrefs integration ready for launch - [ ] System scales without complexity --- ## 14. Risks & Mitigation ### Risk 1: Data Loss During Migration **Mitigation:** - Backup database before migration - Test migration on staging with production data clone - Keep removed fields as comments in code for 1 month ### Risk 2: Users Confused by Changes **Mitigation:** - In-app changelog notification - Update help documentation - Add tooltips to new UI elements - Gradual rollout (staging → 10% → 50% → 100%) ### Risk 3: Breaking Changes **Mitigation:** - Maintain backward compatibility in API for 2 weeks - Version API endpoints if needed - Monitor error logs closely after deployment --- ## 15. Post-Launch Monitoring ### Metrics to Track **Technical:** - API error rates (especially 402 Insufficient Credits) - Failed automation runs due to credits - Keyword limit violations - Ahrefs query usage patterns **Business:** - Upgrade conversion rate - Support tickets about limits/credits - Credit package purchase rate - User engagement with new Usage insights **User Behavior:** - Time spent on Usage page - Click-through on upgrade prompts - Ahrefs query usage distribution - Most-used insights widgets --- ## 16. Documentation Updates Required - [ ] Update `docs/10-MODULES/BILLING.md` - [ ] Update `docs/40-WORKFLOWS/CREDIT-SYSTEM.md` - [ ] Create `docs/10-MODULES/KEYWORD-RESEARCH.md` - [ ] Update API documentation - [ ] Update user help docs - [ ] Update admin guides --- ## 17. Summary ### Before (Complex) ### Keywords Limit Testing - [ ] Try to create single keyword when at limit → Should fail - [ ] Try to import 50 SeedKeywords when 30 slots remain → Should fail - [ ] Try to bulk import CSV with 1000 keywords when at limit → Should fail - [ ] Error message shows current count, limit, and upgrade link - [ ] Upgrade plan → New limit applies immediately - [ ] Delete keywords → Can add more up to new total ### Ahrefs Limit Testing (Future) - [ ] Query Ahrefs when at monthly limit → Should fail - [ ] Error message shows queries used, limit, and reset date - [ ] Monthly reset correctly resets counter - [ ] Upgrade plan → New monthly allowance applies ### Page Reorganization Testing - [ ] Plans & Billing → Current Plan shows only plan info and brief summary - [ ] Plans & Billing → Current Plan does NOT show detailed usage breakdown - [ ] Usage → Limits & Usage shows all limits with progress bars - [ ] Usage → Credit History shows transaction log - [ ] No duplicate data between the two sections --- ## 12. Conclusion ### What's Working Well ✅ 1. **Credit System:** Token-based calculation is accurate and well-implemented 2. **Usage Tracking:** Monthly limits are properly tracked in Account model 3. **Sites & Users Limits:** Fully enforced with proper error handling 4. **Frontend UI:** UsageLimitsPanel component is excellent ### What Needs Fixing ⚠️ 1. **Keywords Enforcement:** Limit exists but not consistently checked 2. **Data Duplication:** Credits and usage data shown in both Plans & Usage 3. **Missing Ahrefs Limit:** Future feature needs to be defined ### Impact of Changes | Change | User Benefit | Business Benefit | |--------|-------------|------------------| | Enforce keywords limit | Clear boundaries, upgrade prompts | Prevent unlimited usage, drive upgrades | | Remove duplication | Less confusion, faster navigation | Easier maintenance, clearer product positioning | | Add Ahrefs limit | Know usage allowance upfront | Control costs, monetize feature | ### Next Steps 1. **Backend Team:** Implement keyword limit enforcement 2. **Frontend Team:** Clean up Plans & Billing page 3. **Product Team:** Define Ahrefs query pricing tiers 4. **QA Team:** Test all limit scenarios --- **End of Report** *For questions or clarification, refer to:* - `docs/10-MODULES/BILLING.md` - Billing system documentation - `docs/40-WORKFLOWS/CREDIT-SYSTEM.md` - Credit workflows - `backend/igny8_core/business/billing/services/limit_service.py` - Limit enforcement code