# Competitor Intel Database

> Centralized competitor information for sales battle cards

**Created:** Jan 20, 2026
**Status:** Plan Ready
**Effort:** 5-7 hours
**Ongoing Cost:** $0 (occasional Perplexity research ~$5/mo)

---

## Problem

Reps encounter competitors but intel is scattered:
- "What do we say when they mention HiPages?"
- "How does our pricing compare to Airtasker?"
- "What are Hipages' weaknesses?"

No central place to store and retrieve competitor battle cards.

---

## Solution

Competitor database with battle cards, win/loss tracking, and real-time alerts.

### Competitor Profile

```json
{
  "id": "uuid",
  "name": "HiPages",
  "website": "hipages.com.au",
  "category": "lead_generation",
  "pricing": {
    "model": "pay_per_lead",
    "range": "$20-80 per lead",
    "notes": "No subscription, but leads expensive"
  },
  "strengths": [
    "Large brand awareness",
    "Big marketing budget",
    "Established 10+ years"
  ],
  "weaknesses": [
    "Expensive leads",
    "No quality guarantee",
    "Shared leads (multiple tradies)"
  ],
  "our_advantages": [
    "Exclusive leads (not shared)",
    "Quality verified customers",
    "Lower cost per acquisition"
  ],
  "common_objections": [
    {
      "objection": "I already use HiPages",
      "response": "Many of our best tradies switched from HiPages. They found our leads convert 3x better because they're exclusive - you're not competing with 5 other quotes."
    }
  ],
  "mentions_count": 45,
  "win_rate_against": 0.65,
  "last_updated": "2026-01-15"
}
```

---

## Database Schema

```sql
CREATE TABLE IF NOT EXISTS competitors (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name VARCHAR(100) NOT NULL UNIQUE,
  website VARCHAR(255),
  logo_url VARCHAR(255),
  category VARCHAR(50), -- 'lead_generation', 'marketplace', 'software'
  pricing JSONB,
  strengths TEXT[],
  weaknesses TEXT[],
  our_advantages TEXT[],
  target_audience TEXT,
  market_position TEXT,
  notes TEXT,
  perplexity_research JSONB,
  research_updated_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS competitor_objections (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  competitor_id UUID REFERENCES competitors(id) ON DELETE CASCADE,
  objection TEXT NOT NULL,
  response TEXT NOT NULL,
  effectiveness_score DECIMAL(3,2) DEFAULT 0.5,
  times_used INTEGER DEFAULT 0,
  times_worked INTEGER DEFAULT 0,
  created_by UUID REFERENCES auth.users(id),
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS competitor_mentions (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  competitor_id UUID REFERENCES competitors(id) ON DELETE CASCADE,
  lead_id UUID REFERENCES leads(id) ON DELETE SET NULL,
  source VARCHAR(50), -- 'call', 'sms', 'research'
  context TEXT,
  outcome VARCHAR(50), -- 'won', 'lost', 'pending'
  mentioned_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indexes
CREATE INDEX idx_competitor_mentions_competitor ON competitor_mentions(competitor_id);
CREATE INDEX idx_competitor_mentions_outcome ON competitor_mentions(outcome);
```

---

## Auto-Detection

Detect competitor mentions in:
- Call transcripts
- SMS messages
- Perplexity research

```javascript
const COMPETITOR_PATTERNS = {
  'hipages': ['hipages', 'hi pages', 'hi-pages'],
  'airtasker': ['airtasker', 'air tasker'],
  'serviceseeker': ['service seeking', 'serviceseeker'],
  'oneflare': ['oneflare', 'one flare']
};

function detectCompetitors(text) {
  const found = [];
  for (const [competitor, patterns] of Object.entries(COMPETITOR_PATTERNS)) {
    if (patterns.some(p => text.toLowerCase().includes(p))) {
      found.push(competitor);
    }
  }
  return found;
}
```

---

## API Endpoints

```
GET    /api/competitors                    # List all competitors
GET    /api/competitors/:id                # Get competitor with battle card
POST   /api/competitors                    # Create competitor
PATCH  /api/competitors/:id                # Update competitor
GET    /api/competitors/:id/objections     # Get objection responses
POST   /api/competitors/:id/objections     # Add objection response
POST   /api/competitors/:id/mention        # Log mention
GET    /api/competitors/stats              # Win/loss stats
POST   /api/competitors/:id/research       # Trigger Perplexity research
```

---

## UI Components

### CompetitorBattleCard.jsx
- Shows during calls when competitor detected
- Quick access to objection responses
- "This worked" / "Didn't work" feedback

### CompetitorsPage.jsx
- List all competitors with win rates
- Click to view/edit full profile
- Add new competitor button

### CompetitorMentionAlert.jsx
- Toast notification when competitor detected in call
- Quick link to battle card

---

## Implementation Steps

### Phase 1: Database + Seed Data (2 hours)
1. [ ] Create migration
2. [ ] Seed 5 main competitors (HiPages, Airtasker, ServiceSeeking, Oneflare, Gumtree)
3. [ ] Add initial objection responses

### Phase 2: API + Detection (2 hours)
1. [ ] Create competitors routes
2. [ ] Add detectCompetitors() to AI service
3. [ ] Hook into call transcript processing
4. [ ] Hook into SMS processing

### Phase 3: Frontend (2-3 hours)
1. [ ] Create CompetitorsPage
2. [ ] Create CompetitorBattleCard
3. [ ] Add to LiveCopilot (show when detected)
4. [ ] Add competitor filter to Leads page

---

## Seed Data

### HiPages
- **Weakness:** Shared leads, expensive, no guarantee
- **Our advantage:** Exclusive leads, verified customers

### Airtasker
- **Weakness:** Race to bottom on price, low quality jobs
- **Our advantage:** Pre-qualified customers, fair pricing

### ServiceSeeking
- **Weakness:** Low volume, outdated platform
- **Our advantage:** Modern platform, growing marketplace

---

## Success Metrics

- Battle cards viewed 20+ times/week
- Win rate against competitors tracked
- Objection response effectiveness >70%
