Files
igny8/v2/V2-Execution-Docs/02E-linker-external-backlinks.md
IGNY8 VPS (Salman) 0570052fec 1
2026-03-23 17:20:51 +00:00

735 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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