# 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 */}
| Keyword |
Search Volume |
Difficulty |
Opportunity Score |
|
{keywords.map(kw => (
| {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 && (
| Keyword |
Volume |
Difficulty |
CPC |
|
{results.map((kw, idx) => (
| {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