Multi-Campaign GTM Architecture

Version: 1.0 Date: 2026-02-07 Applies to: SKYMOD & Maad House GTM Engines


Problem

Both GTM engines currently support a single campaign with one ICP, one outreach sequence, and one scoring model. As we expand to new ICPs (UK Long-Service, NL Anniversaries, Professional Services for SKYMOD; Legal, DTC, Lifestyle for Maad House), each needs its own pipeline, messaging, and tracking — while sharing infrastructure and avoiding duplicate contacts.


Architecture: Campaign as First-Class Object

A campaign encapsulates everything needed to run an independent outbound motion:

ComponentWhat It Defines
ICPWho to target (firmographic + intent signals + scoring weights)
ScoringHow to score (criteria and weights specific to this ICP)
SequencesWhat to say (touch templates, tone, CTA, cadence)
CollateralSupporting materials (1-pagers, pitch decks, case studies)
GeographyWhere to target (country/region filters)
StagesPipeline stages (can vary per campaign)
TimingCadence rules (days between touches, send windows)


Structural Design

1. Campaign Registry

New file at Marketing/GTM/campaigns.json:

{
  "version": "2.0",
  "campaigns": [
    {
      "id": "retirement-archive",
      "name": "Retirement Archive (EU Manufacturing)",
      "status": "active",
      "priority": 1,
      "icpFile": "campaigns/retirement-archive/icp-definition.md",
      "positioningFile": "campaigns/retirement-archive/positioning.md",
      "sequencesDir": "campaigns/retirement-archive/outreach-sequences/",
      "collateralDir": "campaigns/retirement-archive/collateral/",
      "geography": ["DE", "NL", "CH", "AT", "BE"],
      "stages": [
        "sourced",
        "recognition_qualified",
        "contacted",
        "replied",
        "interested",
        "invoiceSent",
        "paid",
        "shipped",
        "closed"
      ],
      "touchCount": 4,
      "cadence": {
        "t1_to_t2": 3,
        "t2_to_t3": 7,
        "t3_to_t4": 14
      },
      "scoring": {
        "model": "recognition-first",
        "weights": {
          "firmographic": 30,
          "recognition": 45,
          "accessibility": 25
        }
      },
      "created": "2026-02-03",
      "metrics": {
        "prospectCount": 20,
        "contacted": 14,
        "replied": 0,
        "converted": 0
      }
    },
    {
      "id": "uk-longservice",
      "name": "UK Long-Service Awards (HMRC Tax Advantage)",
      "status": "planned",
      "priority": 2,
      "icpFile": "campaigns/uk-longservice/icp-definition.md",
      "positioningFile": "campaigns/uk-longservice/positioning.md",
      "sequencesDir": "campaigns/uk-longservice/outreach-sequences/",
      "collateralDir": "campaigns/uk-longservice/collateral/",
      "geography": ["GB"],
      "stages": [
        "sourced",
        "recognition_qualified",
        "contacted",
        "replied",
        "interested",
        "invoiceSent",
        "paid",
        "shipped",
        "closed"
      ],
      "touchCount": 4,
      "cadence": {
        "t1_to_t2": 3,
        "t2_to_t3": 7,
        "t3_to_t4": 14
      },
      "scoring": {
        "model": "recognition-first",
        "weights": {
          "firmographic": 30,
          "recognition": 40,
          "accessibility": 30
        }
      },
      "created": "2026-02-07",
      "metrics": {
        "prospectCount": 0,
        "contacted": 0,
        "replied": 0,
        "converted": 0
      }
    }
  ]
}

2. Per-Campaign Folder Structure

Marketing/GTM/
├── README.md                           # Engine overview (updated)
├── campaigns.json                      # Campaign registry
├── research-process.md                 # Shared research methodology
├── log.md                              # Activity log (all campaigns)
│
├── campaigns/
│   ├── retirement-archive/             # EXISTING (migrated from root)
│   │   ├── icp-definition.md
│   │   ├── positioning.md
│   │   ├── outreach-sequences/
│   │   │   ├── t1-cold-intro.md
│   │   │   ├── t2-follow-up.md
│   │   │   ├── t3-value-add.md
│   │   │   └── t4-breakup.md
│   │   └── collateral/
│   │       └── retirement-batch-1pager.md
│   │
│   ├── uk-longservice/                 # NEW
│   │   ├── icp-definition.md           # UK manufacturing, HMRC angle
│   │   ├── positioning.md              # Tax-advantage framing
│   │   ├── outreach-sequences/
│   │   │   ├── t1-cold-intro.md        # HMRC hook
│   │   │   ├── t2-follow-up.md
│   │   │   ├── t3-value-add.md
│   │   │   └── t4-breakup.md
│   │   └── collateral/
│   │       └── hmrc-tax-guide.md
│   │
│   ├── nl-anniversaries/               # NEW
│   │   ├── icp-definition.md           # NL companies with jubileum culture
│   │   ├── positioning.md              # 12.5-year tradition angle
│   │   ├── outreach-sequences/
│   │   └── collateral/
│   │
│   └── prof-services/                  # NEW
│       ├── icp-definition.md           # UK/EU law & accounting firms
│       ├── positioning.md              # Partner gifting angle
│       ├── outreach-sequences/
│       └── collateral/
│
Sales/
├── pipeline/
│   ├── index.json                      # SINGLE pipeline (all campaigns)
│   └── SCHEMA.md                       # Updated schema
├── prospects/                          # Per-prospect folders (unchanged)
│   ├── _template/
│   ├── miele/
│   └── ...

3. Pipeline: Single Index, Campaign Field Per Prospect

Design decision: One index.json with a campaign field on each prospect — NOT separate files per campaign.

Why single file:

Updated prospect object (additions in bold):

{
  "id": "SKY-2026-021",
  "campaign": "uk-longservice",          // ← NEW: which campaign
  "company": {
    "name": "Rolls-Royce plc",
    "website": "https://rolls-royce.com",
    "industry": "Manufacturing",
    "size": "5000+",
    "country": "GB",
    "city": "Derby"
  },
  "contact": { ... },
  "scoring": {
    "total": 88,
    "firmographic": 28,
    "recognition": 35,
    "accessibility": 25,
    "notes": "HMRC long-service eligible. 25+ year award program."
  },
  "tier": "A",
  "stage": "sourced",
  "recognitionSignal": "...",
  "source": "firecrawl",
  "personalization": { ... },
  "dates": { ... },
  "outreach": { ... },
  "notes": [],
  "folderPath": "prospects/rolls-royce/"
}

4. Updated Stats Object

{
  "version": "2.0",
  "lastUpdated": "2026-02-07T12:00:00Z",
  "stats": {
    "total": 35,
    "byCampaign": {
      "retirement-archive": {
        "total": 20,
        "byTier": { "A": 5, "B": 15 },
        "byStage": { "sourced": 1, "contacted": 14, "recognition_qualified": 5 },
        "byCountry": { "DE": 14, "NL": 1, "CH": 1, "AT": 2, "BE": 2 }
      },
      "uk-longservice": {
        "total": 15,
        "byTier": { "A": 5, "B": 10 },
        "byStage": { "sourced": 15 },
        "byCountry": { "GB": 15 }
      }
    }
  },
  "campaigns": ["retirement-archive", "uk-longservice"],
  "prospects": [ ... ]
}


Cross-Campaign Rules

Deduplication

Before adding ANY prospect to ANY campaign:

  • Search index.json for matching company.website or company.name
  • If match found in SAME campaign → skip (already exists)
  • If match found in DIFFERENT campaign → flag for review:
  • - Same contact? → Don't send from two campaigns simultaneously - Different contact? → OK if different department/decision-maker - Log cross-campaign overlap in prospect notes

    Contact Collision Prevention

    A single contact should NEVER receive emails from two campaigns at the same time. Rules:

    ScenarioAction
    Same person, same company, different campaignChoose highest-priority campaign. Pause the other.
    Different person, same company, different campaignOK — proceed with both (different decision-makers)
    Same person moved to new companyOK — new company context

    Campaign Priority

    When conflicts arise, defer to campaign priority in campaigns.json. Lower number = higher priority.


    Workflow Commands (Updated)

    Discovery

    "Source SKYMOD prospects for [campaign]"
    → Load ICP from campaigns/{campaign}/icp-definition.md
    → Run Firecrawl Agent with campaign-specific query
    → Dedupe against FULL pipeline (all campaigns)
    → Score using campaign-specific weights
    → Add to index.json with campaign field
    

    Outreach

    "Draft T1 for [company] in [campaign]"
    → Load T1 template from campaigns/{campaign}/outreach-sequences/t1-cold-intro.md
    → Read prospect research + persona
    → Generate personalized email
    → Save to prospects/{company}/outreach/t1.md
    

    Status

    "Show SKYMOD pipeline status"
    → Read campaigns.json → list all campaigns
    → For each: show prospect count, stage breakdown, next actions

    "Show SKYMOD pipeline for [campaign]" → Filter index.json by campaign → Show that campaign's metrics and upcoming actions

    "Show all SKYMOD next actions" → Scan ALL campaigns for nextActionDate ≤ today → Group by campaign, sorted by date

    Campaign Lifecycle

    "Create SKYMOD campaign [name]"
    → Add entry to campaigns.json
    → Create campaign folder structure
    → Generate ICP definition template
    → Generate outreach sequence templates
    → Status: "planned"

    "Activate SKYMOD campaign [name]" → Set status: "active" → Run first prospecting batch

    "Pause SKYMOD campaign [name]" → Set status: "paused" → Skip all scheduled outreach for that campaign

    "Archive SKYMOD campaign [name]" → Set status: "archived" → Move stats to archive, keep data for reference


    Migration Plan (Existing Data)

    SKYMOD Migration (retirement-archive)

  • Move existing files to campaign subfolder:
  • - Marketing/GTM/icp-definition.mdcampaigns/retirement-archive/icp-definition.md - Marketing/GTM/positioning.mdcampaigns/retirement-archive/positioning.md - Marketing/GTM/outreach-sequences/campaigns/retirement-archive/outreach-sequences/ - Sales/collateral/retirement-batch-1pager.mdcampaigns/retirement-archive/collateral/

  • Add campaign field to each existing prospect in index.json:
  •    "campaign": "retirement-archive"
       

  • Update stats to nested byCampaign structure
  • Create campaigns.json with retirement-archive as first entry
  • Update README.md to reference new structure
  • Maad House Migration (hospitality)

  • Create campaigns.json with hospitality as first entry
  • Add campaign: "hospitality" field to each existing prospect
  • Create campaign folder: campaigns/hospitality/
  • Move/create ICP and sequence files into campaign subfolder

  • Maad House Adaptation

    Maad House follows the same architecture with different campaign definitions:

    Maad House/Marketing/GTM/
    ├── campaigns.json
    ├── campaigns/
    │   ├── hospitality/              # EXISTING (migrated)
    │   │   ├── icp-definition.md
    │   │   ├── outreach-sequences/
    │   │   └── collateral/
    │   ├── legal-lawtech/            # NEW (Priority #1 expansion)
    │   │   ├── icp-definition.md     # Linkilaw as proof point
    │   │   ├── outreach-sequences/
    │   │   └── collateral/
    │   │       └── linkilaw-case-study.md
    │   ├── ecommerce-dtc/            # NEW (Priority #2)
    │   │   ├── icp-definition.md     # SKYMOD as proof point
    │   │   ├── outreach-sequences/
    │   │   └── collateral/
    │   │       └── skymod-case-study.md
    │   └── lifestyle-wellness/       # NEW (Priority #3)
    │       ├── icp-definition.md
    │       ├── outreach-sequences/
    │       └── collateral/
    

    Same pipeline rules: single index.json, campaign field per prospect, cross-campaign deduplication.


    Dashboard Commands

    After migration, the portfolio dashboard supports:

    "Show SKYMOD pipeline status"
    → All campaigns summary:
      retirement-archive: 20 prospects (14 contacted, 0 replies)
      uk-longservice: 0 prospects (planned)
      nl-anniversaries: 0 prospects (planned)
      prof-services: 0 prospects (planned)

    "Execute SKYMOD ICP expansion (UK → NL → Prof Services)" → Creates campaign entries → Generates ICP + sequence templates → Sets priority order → Ready for prospecting


    Key Design Principles

  • Single source of truth: One pipeline file per business, not per campaign
  • Campaign isolation: Each campaign has its own ICP, messaging, and metrics
  • Shared infrastructure: Same Firecrawl, Hunter, Outlook tools across campaigns
  • Cross-campaign intelligence: A reply from Campaign A informs Campaign B for the same company
  • Incremental adoption: Migrate existing first, add new campaigns one at a time
  • No code required: Everything is file-based (JSON + Markdown). Claude reads the campaign config and executes accordingly.

  • Implementation Order

    StepActionEffort
    1Create campaigns.json for both businesses10 min
    2Add campaign field to existing prospects5 min
    3Restructure folders (move existing files to campaign subfolders)15 min
    4Update README files10 min
    5Update SCHEMA.md with campaign field5 min
    6Create first new campaign (UK Long-Service)30 min
    7Run first prospecting batch for new campaign15 min

    Total: ~90 minutes to go from single-campaign to multi-campaign for both businesses.