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

27 KiB
Raw Blame History

IGNY8 Phase 2: External Linker & Backlinks (02E)

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

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.

  • Periodic HTTP checks on all placed backlinks (status=live)
  • Status tracking: livedead (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)

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

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)

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
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

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

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

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

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

@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
  • 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