# Usage & Content System **Last Verified:** December 25, 2025 --- ## Overview IGNY8 uses a content-based allowance system. Users see "Content Pieces" while the backend tracks detailed credit consumption for internal cost monitoring. **User View:** `47/50 Content Pieces Remaining` **Backend Tracks:** Idea credits, content credits, image credits (for cost analysis) --- ## How It Works ### User-Facing (Simple) | What Users See | Description | |----------------|-------------| | **Content Pieces** | Monthly allowance of pages/articles | | **X/Y Remaining** | Used vs total for the month | | **Upgrade Plan** | Get more content pieces | ### Backend (Detailed - Internal Only) | Credit Type | Used For | Tracked For | |-------------|----------|-------------| | Idea Credits | Clustering, idea generation | Cost analysis | | Content Credits | Article generation | Usage limits | | Image Credits | Image generation | Cost analysis | | Optimization Credits | SEO optimization (future) | Cost analysis | --- ## Plan Allowances | Plan | Content Pieces/Month | Sites | Users | |------|---------------------|-------|-------| | Starter | 50 | 2 | 2 | | Growth | 200 | 5 | 3 | | Scale | 500 | Unlimited | 5 | **Included with every content piece:** - AI keyword clustering - AI idea generation - AI content writing (1000-2000 words) - 3 images (1 featured + 2 in-article) - Internal linking - SEO optimization - WordPress publishing --- ## Backend Soft Limits (Hidden from Users) To prevent abuse, the backend enforces hidden limits: | Limit | Starter | Growth | Scale | |-------|---------|--------|-------| | Keyword imports/mo | 500 | 2,000 | 5,000 | | Clustering operations | 100 | 400 | 1,000 | | Idea generations | 150 | 600 | 1,500 | | Images generated | 200 | 800 | 2,000 | If users hit these limits, they see: "You've reached your preparation limit for this month." --- ## Content Deduction Flow ``` ┌─────────────────────────────────────────────────────────────────┐ │ CONTENT CREATION FLOW │ ├─────────────────────────────────────────────────────────────────┤ │ │ │ User clicks Check Generate │ │ "Generate" ──────► Allowance ──────► Content │ │ │ │ │ │ │ Limit │ │ │ │ Reached ▼ │ │ ▼ Deduct 1 │ │ Show Upgrade Content │ │ Modal Piece │ │ │ └─────────────────────────────────────────────────────────────────┘ ``` --- ## Operations & Credit Costs ### Planner Operations | Operation | Credits | Type | |-----------|---------|------| | Add keyword | 0 | Free | | Auto-cluster keywords | 1 | Idea | | Generate content ideas | 1 per idea | Idea | ### Writer Operations | Operation | Credits | Type | |-----------|---------|------| | Create task | 0 | Free | | Generate content | 1 | Content | | Regenerate content | 1 | Content | | Generate images | 1 per image | Image | | Regenerate image | 1 | Image | | Edit content | 0 | Free | ### Automation Operations | Operation | Credits | Type | |-----------|---------|------| | Run automation | Sum of operations | Mixed | | Pause/resume | 0 | Free | ### Publisher Operations | Operation | Credits | Type | |-----------|---------|------| | Publish to WordPress | 0 | Free | | Sync from WordPress | 0 | Free | ### Optimizer Operations (Future) | Operation | Credits | Type | |-----------|---------|------| | Optimize content | 1 | Optimization | | Batch optimize | 1 per item | Optimization | --- ## Database Models ### CreditBalance ```python class CreditBalance(models.Model): account = models.ForeignKey(Account) site = models.ForeignKey(Site, null=True) idea_credits = models.IntegerField(default=0) content_credits = models.IntegerField(default=0) image_credits = models.IntegerField(default=0) optimization_credits = models.IntegerField(default=0) period_start = models.DateField() period_end = models.DateField() ``` ### CreditUsage ```python class CreditUsage(models.Model): account = models.ForeignKey(Account) site = models.ForeignKey(Site, null=True) user = models.ForeignKey(User) credit_type = models.CharField() # idea/content/image/optimization amount = models.IntegerField() operation = models.CharField() # generate_content, etc. created_at = models.DateTimeField(auto_now_add=True) ``` --- ## Business Logic ### CreditService Location: `backend/igny8_core/business/billing/services.py` **Key Methods:** ```python class CreditService: def check_balance(account, site, credit_type, amount) -> bool: """Check if sufficient credits available""" def deduct_credits(account, site, user, credit_type, amount, operation) -> bool: """Deduct credits and log usage""" def get_balance(account, site) -> CreditBalance: """Get current balance""" def reset_monthly_credits(account) -> None: """Reset credits at period start""" def add_credits(account, credit_type, amount, reason) -> None: """Add credits (admin/purchase)""" ``` ### Usage in AI Operations ```python # In content generation service def generate_content(task, user): # 1. Check balance if not credit_service.check_balance( account=task.site.account, site=task.site, credit_type='content', amount=1 ): raise InsufficientCreditsError() # 2. Execute AI function content = ai_engine.generate_content(task) # 3. Deduct credits credit_service.deduct_credits( account=task.site.account, site=task.site, user=user, credit_type='content', amount=1, operation='generate_content' ) return content ``` --- ## API Responses ### Successful Deduction ```json { "success": true, "data": { ... }, "credits_used": { "type": "content", "amount": 1 }, "balance": { "content_credits": 49 } } ``` ### Insufficient Credits ```json HTTP 402 Payment Required { "success": false, "error": "Insufficient content credits", "code": "INSUFFICIENT_CREDITS", "required": 1, "available": 0 } ``` --- ## Frontend Handling ### Balance Display - Header shows credit balances - Updates after each operation - Warning at low balance (< 10%) ### Error Handling ```typescript // In writer store async generateContent(taskId: string) { try { const response = await api.generateContent(taskId); // Update billing store billingStore.fetchBalance(); return response; } catch (error) { if (error.code === 'INSUFFICIENT_CREDITS') { // Show upgrade modal uiStore.showUpgradeModal(); } throw error; } } ``` --- ## Usage Tracking ### Usage Summary Endpoint ``` GET /api/v1/billing/usage/summary/?period=month ``` Response: ```json { "period": "2025-01", "usage": { "idea_credits": 45, "content_credits": 23, "image_credits": 67, "optimization_credits": 0 }, "by_operation": { "auto_cluster": 12, "generate_ideas": 33, "generate_content": 23, "generate_images": 67 } } ``` --- ## Automation Credit Estimation Before running automation: ``` GET /api/v1/automation/estimate/?site_id=... ``` Response: ```json { "estimated_credits": { "idea_credits": 25, "content_credits": 10, "image_credits": 30 }, "stages": { "clustering": 5, "ideas": 20, "content": 10, "images": 30 }, "has_sufficient_credits": true } ``` --- ## Credit Reset Credits reset monthly based on billing cycle: 1. **Monthly Reset Job** runs at period end 2. **Unused credits** do not roll over 3. **Purchased credits** may have different expiry ### Celery Task ```python @celery.task def reset_monthly_credits(): """ Run daily, resets credits for accounts whose period_end is today """ today = date.today() balances = CreditBalance.objects.filter(period_end=today) for balance in balances: credit_service.reset_monthly_credits(balance.account) ``` --- ## Admin Operations ### Manual Credit Adjustment Via Django Admin or API: ```python # Add credits credit_service.add_credits( account=account, credit_type='content', amount=100, reason='Customer support adjustment' ) ``` ### Usage Audit All credit changes logged in `CreditUsage` with: - Timestamp - User who triggered - Operation type - Amount deducted - Related object ID