This commit is contained in:
IGNY8 VPS (Salman)
2026-03-23 17:20:51 +00:00
parent e78a41f11c
commit 0570052fec
21 changed files with 15889 additions and 0 deletions

View File

@@ -0,0 +1,734 @@
# IGNY8 Phase 2: External Linker & Backlinks (02E)
## SAG-Based External Backlink Campaign Engine
**Document Version:** 1.0
**Date:** 2026-03-23
**Phase:** IGNY8 Phase 2 — Feature Expansion
**Status:** Build Ready
**Source of Truth:** Codebase at `/data/app/igny8/`
**Audience:** Claude Code, Backend Developers, Architects
---
## 1. CURRENT STATE
### External Linking Today
There is **no** backlink management in IGNY8. No external API integrations exist for link building platforms. No campaign generation, tracking, or KPI monitoring. Backlink building is entirely manual and external to the platform.
### What Exists
- `SAGBlueprint` and `SAGCluster` models (01A) — provide the hierarchy and cluster assignments for target page identification
- `Keywords` model (planner app) — provides search volume data for tier assignment
- `Content` model with `content_type` and `content_structure` — classifies pages for tiering
- `GSCMetricsCache` (02C) — provides organic traffic and impression data for KPI tracking
- `SAGLink` and `LinkMap` models (02D) — internal link data complements external strategy
- The `linker` app (02D) — provides app namespace for related models
### What Does Not Exist
- No SAGCampaign model, SAGBacklink model, or CampaignKPISnapshot model
- No page tier assignment system
- No country-specific strategy profiles
- No marketplace API integrations (FatGrid, PRNews.io, etc.)
- No campaign generation algorithm
- No anchor text planning
- No quality scoring for backlink opportunities
- No tipping point detection
- No dead link monitoring for placed backlinks
---
## 2. WHAT TO BUILD
### Overview
Build a SAG-based backlink campaign engine that generates country-specific link-building campaigns targeting hub pages. The system leverages the SAG hierarchy to focus backlinks on high-value pages (T1-T3) and lets internal linking (02D) distribute authority to supporting content.
### 2.1 Hub-Only Strategy (Core Principle)
Backlinks target ONLY T1-T3 pages (homepage + cluster hubs + key service/product pages). SAG internal linking (02D) distributes authority from hubs downstream to 70+ supporting pages per cluster.
**Budget Allocation:**
- 70-85% to T1-T3 (homepage, top hubs, products/services)
- 15-30% to T4-T5 (authority magnets: guides, tools, supporting articles)
- 0% to term/taxonomy pages (get authority via internal links)
Typically 20-30 target pages per site.
### 2.2 Page Tier Assignment
| Tier | Pages | Links/Page Target | Description |
|------|-------|-------------------|-------------|
| **T1** | Homepage (1 page) | 10-30 | Brand authority anchor |
| **T2** | Top 40% hubs by search volume | 5-15 | Primary money pages |
| **T3** | Remaining hubs + products/services | 3-10 | Supporting money pages |
| **T4** | Supporting blog articles | 1-4 | Content authority |
| **T5** | Authority magnets (guides, tools) | 2-6 | Link bait pages |
**Tier Assignment Algorithm:**
1. Load SAGBlueprint → identify all published hub pages
2. Sort hubs by total cluster keyword search volume (from Keywords model)
3. Top 40% of hubs = T2, remaining hubs = T3
4. Products/services pages = T3
5. Supporting blog articles = T4
6. Content with `content_structure` in (`guide`, `comparison`, `listicle`) and high word count = T5
### 2.3 Country-Specific Strategy Profiles
Four pre-built profiles with different timelines, budgets, and quality thresholds:
| Parameter | Pakistan (PK) | Canada (CA) | UK | USA |
|-----------|--------------|-------------|-----|------|
| Timeline | 8 months | 12 months | 14 months | 18 months |
| Budget range | $2-5K | $3-7K | $3-9K | $5-13K |
| Target DR | 25-30 | 35-40 | 35-40 | 40-45 |
| Quality threshold | ≥5/11 | ≥6/11 | ≥6/11 | ≥7/11 |
| Exact match anchor | 5-10% | 3-7% | 3-7% | 2-5% |
| Velocity phases | ramp→peak→cruise→maintenance | Same 4 phases | Same | Same |
**Anchor Text Mix by Country:**
| Anchor Type | PK | CA | UK | USA |
|-------------|-----|-----|-----|------|
| Branded | 30-35% | 35-40% | 35-40% | 35-45% |
| Naked URL | 15-20% | 15-20% | 15-20% | 15-20% |
| Generic | 15-20% | 15-20% | 15-20% | 15-20% |
| Partial Match | 15-20% | 12-18% | 12-18% | 10-15% |
| Exact Match | 5-10% | 3-7% | 3-7% | 2-5% |
| LSI/Topical | 5-10% | 5-10% | 5-10% | 5-8% |
| Brand+Keyword | — | 3-5% | 3-5% | 3-5% |
### 2.4 Campaign Generation Algorithm
1. Load SAGBlueprint → identify all published hub pages
2. Assign tiers based on search volume data (from Keywords model)
3. Select country profile → calculate `referring_domains_needed`
4. `links_per_tier = referring_domains_needed × tier_allocation_%`
5. `budget_estimate = links × cost_per_link × link_mix_%`
6. Distribute across monthly velocity curve (ramp → peak → cruise → maintenance)
7. Assign pages to months by priority (keyword difficulty, search volume, commercial value)
8. Pre-generate 3 anchor text variants per page per anchor type
9. Set quality requirements per country threshold
### 2.5 Marketplace Integrations
#### FatGrid API
- **Base URL:** `https://api.fatgrid.com/api/public`
- **Auth:** API key in request header
- **Endpoints:**
- Domain Lookup — DR, DA, traffic, niche for a domain
- Marketplace Browse — filterable by DR, traffic, price, niche
- Bulk Domain Lookup — up to 1,000 domains per request
- **15+ Aggregated Marketplaces:** Collaborator.pro, PRNews.io, Adsy.com, WhitePress.com, Bazoom.com, MeUp.com, etc.
- **Usage:** IGNY8 proxies FatGrid API calls to find and filter link placement opportunities
#### PR Distribution (3 Tiers)
| Tier | Provider | Price Range | Reach |
|------|----------|-------------|-------|
| PR Basic | EIN Presswire | $99-499/release | AP News, Bloomberg, 115+ US TV |
| PR Premium | PRNews.io | $500-5K/placement | Yahoo Finance, Forbes-tier publications |
| PR Enterprise | Linking News (white-label) | $500-2K/distribution | ABC, NBC, FOX, Yahoo, Bloomberg |
### 2.6 Quality Scoring (Per Backlink Opportunity)
**Auto-Checkable Factors (7 points):**
1. Organic traffic >500/month
2. Domain Rating / Domain Authority > country threshold
3. Indexed in Google
4. Not on known PBN/spam farm blocklist
5. Traffic trend stable or growing
6. Niche relevance to content topic
7. Dofollow link confirmed
**Manual Review Factors (4 points):**
8. Outbound links <100 on linking page
9. Niche relevance (editorial check)
10. Editorial quality of surrounding content
11. Dofollow confirmed (manual verification)
**Total: 0-11 points. Country thresholds:** PK ≥5, CA ≥6, UK ≥6, USA ≥7
### 2.7 Authority Tipping Point Detection
Monitor for 3+ simultaneous indicators:
- Domain Rating reached country target
- Pages with GSC impressions >100 but 0 SAGBacklinks start getting organic clicks
- Un-linked pages rank on page 2-3 (positions 11-30)
- New content ranks passively without dedicated backlinks
- Keywords in top 10 exceed threshold: PK 10+, UK/CA 15+, USA 20+
**When triggered:** Recommend: reduce link-building velocity, shift budget to content creation, enter maintenance mode.
### 2.8 Dead Link Monitoring
- Periodic HTTP checks on all placed backlinks (status=`live`)
- Status tracking: `live``dead` (404/403/removed) → `replaced`
- Impact scoring: estimate authority loss based on source DR and link type
- Auto-generate replacement recommendations
- Reserve 10-15% monthly budget for replacements
---
## 3. DATA MODELS & APIS
### 3.1 New Models
All models in the `linker` app (same app as 02D).
#### SAGCampaign (linker app)
```python
class SAGCampaign(SiteSectorBaseModel):
"""
Backlink campaign generated from SAG data + country profile.
"""
blueprint = models.ForeignKey(
'planner.SAGBlueprint',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='backlink_campaigns'
)
country_code = models.CharField(
max_length=3,
help_text='PK, CA, UK, or USA'
)
status = models.CharField(
max_length=15,
choices=[
('draft', 'Draft'),
('active', 'Active'),
('paused', 'Paused'),
('completed', 'Completed'),
],
default='draft'
)
tier_assignments = models.JSONField(
default=dict,
help_text='{content_id: tier_level (T1/T2/T3/T4/T5)}'
)
total_links_target = models.IntegerField(default=0)
budget_estimate_min = models.DecimalField(
max_digits=10, decimal_places=2, default=0
)
budget_estimate_max = models.DecimalField(
max_digits=10, decimal_places=2, default=0
)
timeline_months = models.IntegerField(default=12)
monthly_plan = models.JSONField(
default=list,
help_text='[{month, links_target, pages[], budget}]'
)
anchor_text_plan = models.JSONField(
default=dict,
help_text='{content_id: [{text, type, allocated}]}'
)
country_profile = models.JSONField(
default=dict,
help_text='Full profile snapshot at campaign creation'
)
kpi_data = models.JSONField(
default=dict,
help_text='Monthly KPI snapshots summary'
)
started_at = models.DateTimeField(null=True, blank=True)
class Meta:
app_label = 'linker'
db_table = 'igny8_sag_campaigns'
```
**PK:** BigAutoField (integer) — inherits from SiteSectorBaseModel
#### SAGBacklink (linker app)
```python
class SAGBacklink(SiteSectorBaseModel):
"""
Individual backlink record within a campaign.
Tracks from planning through placement to ongoing monitoring.
"""
campaign = models.ForeignKey(
'linker.SAGCampaign',
on_delete=models.CASCADE,
related_name='backlinks'
)
blueprint = models.ForeignKey(
'planner.SAGBlueprint',
on_delete=models.SET_NULL,
null=True,
blank=True
)
target_content = models.ForeignKey(
'writer.Content',
on_delete=models.CASCADE,
related_name='backlinks'
)
target_url = models.URLField()
target_tier = models.CharField(
max_length=3,
choices=[
('T1', 'Tier 1 — Homepage'),
('T2', 'Tier 2 — Top Hubs'),
('T3', 'Tier 3 — Other Hubs/Products'),
('T4', 'Tier 4 — Supporting Articles'),
('T5', 'Tier 5 — Authority Magnets'),
]
)
source_url = models.URLField(
blank=True,
default='',
help_text='May not be known at planning stage'
)
source_domain = models.CharField(max_length=255, blank=True, default='')
source_dr = models.IntegerField(null=True, blank=True)
source_traffic = models.IntegerField(null=True, blank=True)
anchor_text = models.CharField(max_length=200)
anchor_type = models.CharField(
max_length=20,
choices=[
('branded', 'Branded'),
('naked_url', 'Naked URL'),
('generic', 'Generic'),
('partial_match', 'Partial Match'),
('exact_match', 'Exact Match'),
('lsi', 'LSI/Topical'),
('brand_keyword', 'Brand + Keyword'),
]
)
link_type = models.CharField(
max_length=20,
choices=[
('guest_post', 'Guest Post'),
('niche_edit', 'Niche Edit'),
('pr_distribution', 'PR Distribution'),
('directory', 'Directory'),
('resource_page', 'Resource Page'),
('broken_link', 'Broken Link Building'),
('haro', 'HARO/Journalist Query'),
]
)
marketplace = models.CharField(
max_length=20,
blank=True,
default='',
help_text='fatgrid, prnews, ein, linking_news, manual'
)
cost = models.DecimalField(
max_digits=10, decimal_places=2, null=True, blank=True
)
quality_score = models.FloatField(
null=True,
blank=True,
help_text='0-11 quality score'
)
country_relevant = models.BooleanField(default=True)
date_ordered = models.DateField(null=True, blank=True)
date_live = models.DateField(null=True, blank=True)
date_last_checked = models.DateField(null=True, blank=True)
status = models.CharField(
max_length=15,
choices=[
('planned', 'Planned'),
('ordered', 'Ordered'),
('live', 'Live'),
('dead', 'Dead'),
('replaced', 'Replaced'),
('rejected', 'Rejected'),
],
default='planned'
)
notes = models.TextField(blank=True, default='')
class Meta:
app_label = 'linker'
db_table = 'igny8_sag_backlinks'
```
**PK:** BigAutoField (integer) — inherits from SiteSectorBaseModel
#### CampaignKPISnapshot (linker app)
```python
class CampaignKPISnapshot(SiteSectorBaseModel):
"""
Monthly KPI snapshot for a backlink campaign.
Tracks domain metrics, link counts, keyword rankings, and tipping point indicators.
"""
campaign = models.ForeignKey(
'linker.SAGCampaign',
on_delete=models.CASCADE,
related_name='kpi_snapshots'
)
snapshot_date = models.DateField()
dr = models.FloatField(null=True, blank=True, help_text='Domain Rating')
da = models.FloatField(null=True, blank=True, help_text='Domain Authority')
referring_domains = models.IntegerField(default=0)
new_links_this_month = models.IntegerField(default=0)
links_by_tier = models.JSONField(default=dict, help_text='{T1: count, T2: count, ...}')
cost_this_month = models.DecimalField(max_digits=10, decimal_places=2, default=0)
cost_per_link_avg = models.DecimalField(max_digits=10, decimal_places=2, default=0)
keywords_top_10 = models.IntegerField(default=0)
keywords_top_20 = models.IntegerField(default=0)
keywords_top_50 = models.IntegerField(default=0)
organic_traffic = models.IntegerField(
null=True, blank=True,
help_text='From GSC via 02C GSCMetricsCache'
)
impressions = models.IntegerField(
null=True, blank=True,
help_text='From GSC via 02C GSCMetricsCache'
)
pages_ranking_without_backlinks = models.IntegerField(default=0)
tipping_point_indicators = models.JSONField(
default=dict,
help_text='{indicator_name: True/False}'
)
tipping_point_triggered = models.BooleanField(default=False)
class Meta:
app_label = 'linker'
db_table = 'igny8_campaign_kpi_snapshots'
```
**PK:** BigAutoField (integer) — inherits from SiteSectorBaseModel
### 3.2 Migration
```
igny8_core/migrations/XXXX_add_backlink_models.py
```
**Operations:**
1. `CreateModel('SAGCampaign', ...)` — with index on country_code, status
2. `CreateModel('SAGBacklink', ...)` — with indexes on campaign, status, target_content
3. `CreateModel('CampaignKPISnapshot', ...)` — with index on campaign, snapshot_date
### 3.3 API Endpoints
All endpoints under `/api/v1/linker/` (extending the linker URL namespace from 02D):
#### Campaign Management
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/linker/campaigns/?site_id=X` | List backlink campaigns |
| POST | `/api/v1/linker/campaigns/generate/` | AI-generate campaign from blueprint + country. Body: `{site_id, blueprint_id, country_code}`. |
| GET | `/api/v1/linker/campaigns/{id}/` | Campaign detail with monthly plan |
| PUT | `/api/v1/linker/campaigns/{id}/` | Update campaign (adjust plan, budget) |
| POST | `/api/v1/linker/campaigns/{id}/activate/` | Start campaign (set status=active, started_at) |
| POST | `/api/v1/linker/campaigns/{id}/pause/` | Pause active campaign |
#### KPI Tracking
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/linker/campaigns/{id}/kpi/` | KPI snapshot timeline for campaign |
| POST | `/api/v1/linker/campaigns/{id}/kpi/snapshot/` | Record monthly KPI. Body: `{dr, da, referring_domains, ...}`. |
| GET | `/api/v1/linker/tipping-point/?campaign_id=X` | Tipping point analysis — current indicator state |
#### Backlink Records
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/linker/backlinks/?campaign_id=X` | List backlinks with filters (status, tier, anchor_type) |
| POST | `/api/v1/linker/backlinks/` | Add backlink record. Body: `{campaign_id, target_content_id, ...}`. |
| PUT | `/api/v1/linker/backlinks/{id}/` | Update backlink status/details |
| POST | `/api/v1/linker/backlinks/check/` | Trigger dead link check for campaign. Body: `{campaign_id}`. |
#### Marketplace Proxy
| Method | Path | Description |
|--------|------|-------------|
| GET | `/api/v1/linker/marketplace/search/` | FatGrid marketplace search. Query params: `dr_min, traffic_min, price_max, niche`. |
| GET | `/api/v1/linker/marketplace/domain/{domain}/` | FatGrid domain lookup — DR, DA, traffic, niche. |
**Permissions:** All endpoints use `SiteSectorModelViewSet` permission patterns.
### 3.4 Campaign Generation Service
**Location:** `igny8_core/business/campaign_generation.py`
```python
class CampaignGenerationService:
"""
Generates a backlink campaign from SAG data + country profile.
"""
COUNTRY_PROFILES = {
'PK': {
'timeline_months': 8,
'budget_min': 2000, 'budget_max': 5000,
'target_dr': 25, 'quality_threshold': 5,
'exact_match_max': 0.10,
'tier_allocation': {'T1': 0.25, 'T2': 0.35, 'T3': 0.25, 'T4': 0.10, 'T5': 0.05},
# ... full profile
},
'CA': { ... },
'UK': { ... },
'USA': { ... },
}
def generate(self, site_id, blueprint_id, country_code):
"""
1. Load blueprint → published hub pages
2. Assign tiers by search volume
3. Select country profile
4. Calculate total links needed + budget
5. Build monthly plan with velocity curve
6. Pre-generate anchor text variants
7. Create SAGCampaign record
Returns SAGCampaign instance.
"""
pass
def _assign_tiers(self, blueprint, published_content):
"""Sort hubs by search volume, assign T1-T5."""
pass
def _build_monthly_plan(self, tier_assignments, country_profile):
"""Distribute links across months using velocity curve."""
pass
def _generate_anchor_plans(self, tier_assignments, country_profile):
"""Pre-generate 3 anchor text variants per page per type."""
pass
```
### 3.5 Quality Scoring Service
**Location:** `igny8_core/business/backlink_quality.py`
```python
class BacklinkQualityService:
"""
Scores backlink opportunities on 0-11 scale.
"""
def score(self, domain_data, country_code):
"""
Auto-check 7 factors:
1. Organic traffic >500/mo
2. DR/DA > country threshold
3. Indexed in Google
4. Not on blocklist
5. Traffic trend stable/growing
6. Niche relevance
7. Dofollow confirmed
Returns (score, breakdown).
"""
pass
def meets_threshold(self, score, country_code):
"""Check if score meets country minimum."""
pass
```
### 3.6 Tipping Point Detector
**Location:** `igny8_core/business/tipping_point.py`
```python
class TippingPointDetector:
"""
Monitors campaign KPI snapshots for authority tipping point indicators.
"""
def evaluate(self, campaign_id):
"""
Check 5 indicators against latest KPI data + GSC data.
If 3+ triggered, set tipping_point_triggered=True.
Returns {indicators: {name: bool}, triggered: bool, recommendation: str}
"""
pass
```
### 3.7 FatGrid API Client
**Location:** `igny8_core/integration/fatgrid_client.py`
```python
class FatGridClient:
"""
Client for FatGrid marketplace API.
API key stored in SiteIntegration or account-level settings.
"""
BASE_URL = 'https://api.fatgrid.com/api/public'
def __init__(self, api_key):
self.api_key = api_key
def search_marketplace(self, dr_min=None, traffic_min=None,
price_max=None, niche=None, limit=50):
"""Browse marketplace with filters."""
pass
def lookup_domain(self, domain):
"""Get DR, DA, traffic, niche for a domain."""
pass
def bulk_lookup(self, domains):
"""Lookup up to 1,000 domains."""
pass
```
### 3.8 Celery Tasks
**Location:** `igny8_core/tasks/backlink_tasks.py`
```python
@shared_task(name='check_backlink_status')
def check_backlink_status(campaign_id):
"""Weekly HTTP check on all 'live' backlinks. Updates status to 'dead' if 4xx/5xx."""
pass
@shared_task(name='record_kpi_snapshot')
def record_kpi_snapshot(campaign_id):
"""Monthly KPI recording: pull GSC data, calculate keyword rankings."""
pass
@shared_task(name='evaluate_tipping_point')
def evaluate_tipping_point(campaign_id):
"""Monthly, after KPI snapshot. Check tipping point indicators."""
pass
@shared_task(name='generate_replacement_recommendations')
def generate_replacement_recommendations(campaign_id):
"""Triggered when dead links detected. Generate replacement suggestions."""
pass
```
**Beat Schedule Additions:**
| Task | Schedule | Notes |
|------|----------|-------|
| `check_backlink_status` | Weekly (Thursday 3:00 AM) | HTTP check all live backlinks |
| `record_kpi_snapshot` | Monthly (1st of month, 5:00 AM) | Record KPI for all active campaigns |
| `evaluate_tipping_point` | Monthly (1st of month, 6:00 AM) | After KPI snapshot |
---
## 4. IMPLEMENTATION STEPS
### Step 1: Models
1. Create SAGCampaign, SAGBacklink, CampaignKPISnapshot in linker app (extends 02D)
2. Run migration
### Step 2: Country Profiles
1. Define 4 country profiles (PK, CA, UK, USA) as configuration data
2. Store as constants in `CampaignGenerationService` or as a seed data migration
### Step 3: Services
1. Implement `CampaignGenerationService` in `igny8_core/business/campaign_generation.py`
2. Implement `BacklinkQualityService` in `igny8_core/business/backlink_quality.py`
3. Implement `TippingPointDetector` in `igny8_core/business/tipping_point.py`
4. Implement `FatGridClient` in `igny8_core/integration/fatgrid_client.py`
### Step 4: API Endpoints
1. Add campaign, backlink, KPI, and marketplace endpoints to `igny8_core/urls/linker.py`
2. Create views: `CampaignViewSet`, `BacklinkViewSet`, `KPISnapshotView`, `TippingPointView`
3. Create `MarketplaceSearchView`, `MarketplaceDomainLookupView` (FatGrid proxy)
### Step 5: Celery Tasks
1. Implement 4 tasks in `igny8_core/tasks/backlink_tasks.py`
2. Add to Celery beat schedule (weekly backlink check, monthly KPI + tipping point)
### Step 6: Serializers & Admin
1. Create DRF serializers for SAGCampaign, SAGBacklink, CampaignKPISnapshot
2. Register models in Django admin
### Step 7: Credit Cost Configuration
Add to `CreditCostConfig` (billing app):
| operation_type | default_cost | description |
|---------------|-------------|-------------|
| `campaign_generation` | 2 | AI-generate campaign from blueprint + country |
| `backlink_audit` | 1 | Dead link check for campaign |
| `kpi_snapshot` | 0.5 | Monthly KPI recording |
| `dead_link_detection` | 1 | Dead link detection + replacement recommendations |
**Note:** FatGrid API calls consume FatGrid credits (separate billing — not IGNY8 credits). Store FatGrid API key in account-level settings or SiteIntegration.
---
## 5. ACCEPTANCE CRITERIA
### Campaign Generation
- [ ] Campaign generated from SAGBlueprint + country code
- [ ] Page tiers assigned based on search volume from Keywords model
- [ ] Budget estimates calculated from country profile + tier allocation
- [ ] Monthly plan distributed across velocity curve (ramp → peak → cruise → maintenance)
- [ ] Anchor text plan pre-generated with 3 variants per page per anchor type
- [ ] Country profile snapshot stored in campaign record
### Country Profiles
- [ ] All 4 profiles (PK, CA, UK, USA) defined with correct parameters
- [ ] Anchor text mix enforced per country (branded %, exact match %, etc.)
- [ ] Quality thresholds enforced per country (PK ≥5, CA ≥6, UK ≥6, USA ≥7)
- [ ] Timeline and budget ranges match specification
### Quality Scoring
- [ ] 7 auto-checkable factors scored per backlink opportunity
- [ ] Country-specific threshold enforcement
- [ ] Quality score stored on SAGBacklink record
### Tipping Point
- [ ] 5 indicators monitored from KPI data + GSC data
- [ ] Triggered when 3+ indicators simultaneous
- [ ] Recommendation generated when triggered (reduce velocity, shift to content)
- [ ] tipping_point_triggered flag set on KPI snapshot
### Dead Link Monitoring
- [ ] Weekly HTTP checks on all live backlinks
- [ ] Status transitions: live → dead, with date tracking
- [ ] Replacement recommendations generated for dead links
- [ ] 10-15% budget reserve recommended in campaign plan
### Marketplace Integration
- [ ] FatGrid marketplace search proxied through IGNY8 API
- [ ] FatGrid domain lookup returns DR, DA, traffic, niche
- [ ] API key stored securely (not exposed to frontend)
---
## 6. CLAUDE CODE INSTRUCTIONS
### File Locations
```
igny8_core/
├── modules/
│ └── linker/
│ └── models.py # Add SAGCampaign, SAGBacklink, CampaignKPISnapshot
├── business/
│ ├── campaign_generation.py # CampaignGenerationService
│ ├── backlink_quality.py # BacklinkQualityService
│ └── tipping_point.py # TippingPointDetector
├── integration/
│ └── fatgrid_client.py # FatGridClient
├── tasks/
│ └── backlink_tasks.py # Celery tasks
├── urls/
│ └── linker.py # Extend with campaign/backlink endpoints
└── migrations/
└── XXXX_add_backlink_models.py
```
### Conventions
- **PKs:** BigAutoField (integer) — do NOT use UUIDs
- **Table prefix:** `igny8_` on all new tables
- **App label:** `linker` (same app as 02D)
- **Celery app name:** `igny8_core`
- **URL pattern:** `/api/v1/linker/campaigns/...`, `/api/v1/linker/backlinks/...`, `/api/v1/linker/marketplace/...`
- **Permissions:** Use `SiteSectorModelViewSet` permission pattern
- **Model inheritance:** All new models extend `SiteSectorBaseModel`
- **Frontend:** `.tsx` files with Zustand stores
### Cross-References
| Doc | Relationship |
|-----|-------------|
| **02D** | Internal linking distributes authority from hub pages (backlink targets) to supporting content |
| **02C** | GSC data feeds KPIs (organic traffic, impressions, keyword positions) |
| **01A** | SAGBlueprint provides cluster hierarchy for tier assignment |
| **04A** | Managed services package includes backlink campaign management for clients |
| **01G** | SAG health monitoring can incorporate backlink campaign progress |
### Key Decisions
1. **Same `linker` app as 02D** — Internal and external linking share the same app since they're conceptually related; campaigns reference the same Content and Blueprint models
2. **Country profiles as code constants** — Stored in CampaignGenerationService class, not database, to prevent accidental modification; versioned with code
3. **FatGrid proxy** — Never expose FatGrid API key to frontend; all marketplace calls routed through IGNY8 backend
4. **KPI snapshots are manual + automatic** — Monthly auto-recording via Celery, but manual recording also supported for ad-hoc updates
5. **Separate billing for marketplace** — FatGrid credits are external; IGNY8 credits cover campaign generation, audits, and AI-powered anchor text generation