9.2 KiB
9.2 KiB
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
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
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:
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
# 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
{
"success": true,
"data": { ... },
"credits_used": {
"type": "content",
"amount": 1
},
"balance": {
"content_credits": 49
}
}
Insufficient Credits
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
// 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:
{
"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:
{
"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:
- Monthly Reset Job runs at period end
- Unused credits do not roll over
- Purchased credits may have different expiry
Celery Task
@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:
# 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