# 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
<Card>
  <h3>Current Plan: {planName}</h3>
  <div>Credits: {creditBalance.credits}</div>
  <div>Used This Month: {creditBalance.credits_used_this_month}</div>
  <div>Monthly Allowance: {creditBalance.plan_credits_per_month}</div>
</Card>
```

**Usage Analytics - Limits Tab:**
```tsx
<Card>
  <div>Credits Left: {creditBalance.credits}</div>
  <div>Credits Used This Month: {creditBalance.credits_used_this_month}</div>
  <div>Your Monthly Limit: {creditBalance.plan_credits_per_month}</div>
</Card>
```

### 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
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
  {/* Credit Balance Card */}
  <Card>
    <ZapIcon />
    <div>Credits Available</div>
    <div className="text-3xl">{credits.toLocaleString()}</div>
  </Card>

  {/* Sites Used Card */}
  <Card>
    <GlobeIcon />
    <div>Sites</div>
    <div className="text-3xl">{sitesCount} / {maxSites}</div>
  </Card>

  {/* Team Members Card */}
  <Card>
    <UsersIcon />
    <div>Team Members</div>
    <div className="text-3xl">{usersCount} / {maxUsers}</div>
  </Card>

  {/* Keywords Card */}
  <Card>
    <TagIcon />
    <div>Keywords</div>
    <div className="text-3xl">{keywordsCount.toLocaleString()} / {maxKeywords.toLocaleString()}</div>
  </Card>
</div>

{/* Period Selector */}
<div className="flex gap-2">
  <Button onClick={() => setPeriod(7)}>Last 7 Days</Button>
  <Button onClick={() => setPeriod(30)}>Last 30 Days</Button>
  <Button onClick={() => setPeriod(90)}>Last 90 Days</Button>
</div>
```

**Tab 3: Credit Insights (NEW)**
```tsx
{/* Credits by Site */}
<Card>
  <h3>Credits by Site</h3>
  {sites.map(site => (
    <div key={site.id}>
      <div>{site.name}</div>
      <ProgressBar value={site.credits_used} max={totalCredits} />
      <div>{site.credits_used.toLocaleString()} credits</div>
    </div>
  ))}
</Card>

{/* Credits by Content Type */}
<Card>
  <h3>Credits by Content Type</h3>
  <DonutChart data={[
    { label: 'Articles', value: contentCredits },
    { label: 'Images', value: imageCredits },
    { label: 'Ideas & Planning', value: ideaCredits },
    { label: 'Other', value: otherCredits },
  ]} />
</Card>

{/* Credits by Image Quality */}
<Card>
  <h3>Image Generation by Quality</h3>
  {imageQualityBreakdown.map(tier => (
    <div key={tier.quality}>
      <div>{tier.quality} Quality</div>
      <div>{tier.count} images • {tier.credits_used} credits</div>
      <div className="text-xs">({tier.credits_per_image} credits each)</div>
    </div>
  ))}
</Card>

{/* Credits by Automation */}
<Card>
  <h3>Automation Runs</h3>
  {automations.map(auto => (
    <div key={auto.id}>
      <div>{auto.name}</div>
      <div>{auto.runs_count} runs • {auto.credits_used} credits</div>
    </div>
  ))}
</Card>

{/* Timeline Chart */}
<Card>
  <h3>Credit Usage Over Time</h3>
  <LineChart 
    data={usageTimeline}
    xAxis="date"
    yAxis="credits_used"
  />
</Card>
```

#### 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<CreditInsights> {
  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: <SearchIcon className="w-5 h-5" />, 
    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: <GlobeIcon />, color: 'success' },
  users: { icon: <UsersIcon />, color: 'info' },
  keywords: { icon: <TagIcon />, 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<ResearchTab>('browse');
  const [ahrefsLimit, setAhrefsLimit] = useState({ used: 0, limit: 50 });

  return (
    <>
      <PageHeader 
        title="Keyword Research" 
        description="Find high-opportunity keywords for your content strategy"
      />

      {/* Tab Selector */}
      <div className="flex gap-4 mb-6">
        <Button
          variant={activeTab === 'browse' ? 'primary' : 'outline'}
          onClick={() => setActiveTab('browse')}
        >
          <DatabaseIcon className="w-4 h-4" />
          Browse Pre-Researched Keywords
        </Button>
        
        <Button
          variant={activeTab === 'ahrefs' ? 'primary' : 'outline'}
          onClick={() => setActiveTab('ahrefs')}
        >
          <SearchIcon className="w-4 h-4" />
          Research with Ahrefs
          <Badge variant="soft" tone="purple">
            {ahrefsLimit.limit - ahrefsLimit.used} remaining
          </Badge>
        </Button>
      </div>

      {/* Tab Content */}
      {activeTab === 'browse' && (
        <BrowseKeywordsPanel />
      )}

      {activeTab === 'ahrefs' && (
        <AhrefsResearchPanel 
          limit={ahrefsLimit}
          onQuerySuccess={() => {
            setAhrefsLimit(prev => ({ 
              ...prev, 
              used: prev.used + 1 
            }));
          }}
        />
      )}
    </>
  );
}
```

**Browse Keywords Panel (Existing SeedKeywords):**
```tsx
function BrowseKeywordsPanel() {
  return (
    <Card>
      <h3>Pre-Researched High-Opportunity Keywords</h3>
      <p>Browse thousands of analyzed keywords, ready to use.</p>
      
      {/* Filters */}
      <div className="flex gap-4">
        <Select label="Industry" options={industries} />
        <Select label="Sector" options={sectors} />
        <Select label="Country" options={countries} />
        <Input placeholder="Search keywords..." />
      </div>

      {/* Results Table */}
      <Table>
        <thead>
          <tr>
            <th>Keyword</th>
            <th>Search Volume</th>
            <th>Difficulty</th>
            <th>Opportunity Score</th>
            <th></th>
          </tr>
        </thead>
        <tbody>
          {keywords.map(kw => (
            <tr key={kw.id}>
              <td>{kw.keyword}</td>
              <td>{kw.search_volume}</td>
              <td><Badge>{kw.difficulty}</Badge></td>
              <td>{kw.opportunity_score}/100</td>
              <td>
                <Button size="sm" onClick={() => addToWorkspace(kw)}>
                  Add to Workspace
                </Button>
              </td>
            </tr>
          ))}
        </tbody>
      </Table>
    </Card>
  );
}
```

**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 (
    <Card>
      <div className="flex items-center justify-between mb-4">
        <h3>Live Keyword Research with Ahrefs</h3>
        <Badge variant="soft" tone={remaining > 10 ? 'success' : 'warning'}>
          {remaining} / {limit.limit} queries remaining
        </Badge>
      </div>

      {!canQuery && (
        <Alert tone="warning">
          <AlertCircleIcon />
          <div>
            <div>No queries remaining</div>
            <div>Your limit resets on {resetDate}. Upgrade for more queries.</div>
            <Button size="sm" onClick={() => navigate('/account/plans/upgrade')}>
              Upgrade Plan
            </Button>
          </div>
        </Alert>
      )}

      {canQuery && (
        <>
          <div className="flex gap-4 mb-4">
            <Input
              placeholder="Enter seed keyword (e.g., 'digital marketing')"
              value={query}
              onChange={e => setQuery(e.target.value)}
              onKeyPress={e => e.key === 'Enter' && handleSearch()}
            />
            <Button onClick={handleSearch} loading={loading}>
              <SearchIcon /> Research Keywords
            </Button>
          </div>

          {/* Results */}
          {results.length > 0 && (
            <Table>
              <thead>
                <tr>
                  <th>Keyword</th>
                  <th>Volume</th>
                  <th>Difficulty</th>
                  <th>CPC</th>
                  <th></th>
                </tr>
              </thead>
              <tbody>
                {results.map((kw, idx) => (
                  <tr key={idx}>
                    <td>{kw.keyword}</td>
                    <td>{kw.volume.toLocaleString()}</td>
                    <td><Badge>{kw.difficulty}</Badge></td>
                    <td>${kw.cpc}</td>
                    <td>
                      <Button size="sm" onClick={() => addToWorkspace(kw)}>
                        Add to Workspace
                      </Button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </Table>
          )}
        </>
      )}
    </Card>
  );
}
```

---

### 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 <Button onClick={handleGenerate}>Generate Content</Button>;
}
```

---

### 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
<Card>
  <h2>{plan.name} Plan</h2>
  <div className="text-3xl">${plan.price}/month</div>
  <div>Renews on {renewalDate}</div>
  
  {/* Brief Summary - NO detailed limits */}
  <div className="text-sm text-gray-600">
    Your plan includes: {plan.included_credits} credits per month • 
    {plan.max_sites} sites • {plan.max_users} team members
  </div>
  
  {/* Upgrade CTA */}
  <Button onClick={() => navigate('/account/plans/upgrade')}>
    Upgrade Plan
  </Button>
</Card>

{/* ❌ 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 */}
<div className="grid grid-cols-4 gap-4">
  <StatCard 
    icon={<ZapIcon />}
    label="Credits Available"
    value={credits.toLocaleString()}
    color="brand"
  />
  <StatCard 
    icon={<GlobeIcon />}
    label="Sites"
    value={`${sitesCount} / ${maxSites}`}
    color="success"
  />
  <StatCard 
    icon={<UsersIcon />}
    label="Team Members"
    value={`${usersCount} / ${maxUsers}`}
    color="info"
  />
  <StatCard 
    icon={<TagIcon />}
    label="Keywords"
    value={`${keywordsCount} / ${maxKeywords}`}
    color="purple"
  />
</div>

{/* Period Selector */}
<ButtonGroup>
  <Button onClick={() => setPeriod(7)}>Last 7 Days</Button>
  <Button onClick={() => setPeriod(30)}>Last 30 Days</Button>
  <Button onClick={() => setPeriod(90)}>Last 90 Days</Button>
</ButtonGroup>

{/* Top Metrics for Selected Period */}
<div className="grid grid-cols-3 gap-4">
  <Card>
    <div>Credits Spent</div>
    <div className="text-2xl">{periodCredits.toLocaleString()}</div>
  </Card>
  <Card>
    <div>Articles Created</div>
    <div className="text-2xl">{periodArticles}</div>
  </Card>
  <Card>
    <div>Images Generated</div>
    <div className="text-2xl">{periodImages}</div>
  </Card>
</div>
```

**Tab 2: Your Limits**
```tsx
{/* Only 4 limits total */}
<LimitCard
  title="Sites"
  icon={<GlobeIcon />}
  current={sitesCount}
  limit={maxSites}
  type="permanent"
/>

<LimitCard
  title="Team Members"
  icon={<UsersIcon />}
  current={usersCount}
  limit={maxUsers}
  type="permanent"
/>

<LimitCard
  title="Keywords in Workspace"
  icon={<TagIcon />}
  current={keywordsCount}
  limit={maxKeywords}
  type="permanent"
/>

<LimitCard
  title="Ahrefs Research Queries"
  icon={<SearchIcon />}
  current={ahrefsUsed}
  limit={ahrefsLimit}
  type="monthly"
  daysUntilReset={daysUntilReset}
/>
```

**Tab 3: Credit Insights (NEW)**
```tsx
{/* Multi-dimensional breakdowns */}

{/* By Site */}
<Card>
  <h3>Credits by Site</h3>
  <div>See which sites consume the most credits</div>
  {insights.by_site.map(site => (
    <div key={site.id}>
      <div className="flex justify-between">
        <span>{site.name}</span>
        <span>{site.credits_used.toLocaleString()} credits ({site.percentage}%)</span>
      </div>
      <ProgressBar value={site.percentage} />
    </div>
  ))}
</Card>

{/* By Action Type */}
<Card>
  <h3>Credits by Action Type</h3>
  <DonutChart data={insights.by_operation} />
  <div>
    {insights.by_operation.map(op => (
      <div key={op.type}>
        <span>{op.display_name}</span>
        <span>{op.credits_used} credits ({op.count} times)</span>
      </div>
    ))}
  </div>
</Card>

{/* By Image Quality */}
<Card>
  <h3>Image Generation Breakdown</h3>
  <div>Credits vary by quality tier</div>
  {insights.by_image_quality.map(tier => (
    <div key={tier.quality_tier}>
      <div>{tier.quality_tier} Quality</div>
      <div>{tier.images_generated} images × {tier.credits_per_image} credits = {tier.total_credits} credits</div>
      <Badge>{tier.model_name}</Badge>
    </div>
  ))}
</Card>

{/* By Automation */}
<Card>
  <h3>Automation Runs</h3>
  {insights.by_automation.map(auto => (
    <div key={auto.id}>
      <div>{auto.name}</div>
      <div>{auto.runs_count} runs • {auto.credits_used} credits total</div>
    </div>
  ))}
</Card>

{/* Timeline Chart */}
<Card>
  <h3>Credit Usage Over Time</h3>
  <LineChart 
    data={insights.timeline}
    xAxis="date"
    yAxis="credits_used"
  />
</Card>
```

**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: <SearchIcon className="w-5 h-5" />, 
           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: <SearchIcon className="w-5 h-5" />, 
        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
