> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Competitive Displacement Tracker

> Query competitor mentions from Opportunity custom fields, aggregate by competitor, and generate AI-powered competitive battle cards via Mave Agent

## Scenario

Sales reps mention competitor names in opportunity notes and custom fields, but that intelligence never reaches marketing. This job queries a custom "Competitor" field on Opportunities, aggregates which competitors appear most in active and lost deals, then feeds competitor names plus deal context into Mave Agent. The output is AI-generated competitive battle cards.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A["Salesforce Opportunities (Competitor__c)"] --> B[Aggregate by competitor] --> C["POST /api/v1/mave/chat (per competitor)"] --> D[Battle Cards]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests
  from collections import Counter

  SF   = os.environ["SALESFORCE_INSTANCE"]
  SF_T = os.environ["SALESFORCE_ACCESS_TOKEN"]
  MV_K = os.environ["MAVERA_API_KEY"]
  SF_H = {"Authorization": f"Bearer {SF_T}"}

  opps = requests.get(
      f"https://{SF}/services/data/v66.0/query", headers=SF_H,
      params={"q": "SELECT Name, StageName, Amount, Competitor__c, Description FROM Opportunity WHERE Competitor__c != null ORDER BY CloseDate DESC LIMIT 100"},
  ).json()["records"]

  comp_deals = {}
  for opp in opps:
      comp = (opp.get("Competitor__c") or "").strip()
      if comp:
          comp_deals.setdefault(comp, []).append(opp)

  for comp_name, deals in sorted(comp_deals.items(), key=lambda x: -len(x[1]))[:5]:
      won  = [d for d in deals if d["StageName"] == "Closed Won"]
      lost = [d for d in deals if d["StageName"] == "Closed Lost"]
      deal_ctx = "\n".join(f"- {d['Name']} ({d['StageName']}): {(d.get('Description') or '')[:120]}" for d in deals[:10])

      prompt = f"""Generate a competitive battle card for {comp_name}.

  CRM DATA: {len(deals)} mentions | Won: {len(won)} | Lost: {len(lost)}

  DEAL CONTEXT
  {deal_ctx}

  Produce: 1) Overview 2) Their strengths 3) Their weaknesses (where we win) 4) Common objections 5) Killer questions for reps 6) Landmine questions to watch 7) Why customers switch to us"""

      card = requests.post(
          "https://app.mavera.io/api/v1/mave/chat",
          headers={"Authorization": f"Bearer {MV_K}", "Content-Type": "application/json"},
          json={"message": prompt},
      ).json()

      win_rate = f"{len(won)}/{len(won)+len(lost)}" if (won or lost) else "N/A"
      print(f"\n{'='*50}")
      print(f"BATTLE CARD: {comp_name} | Mentions: {len(deals)} | Win rate: {win_rate}")
      print(f"{'='*50}")
      print(card.get("content", "")[:800])
  ```

  ```javascript JavaScript theme={"dark"}
  const SF   = process.env.SALESFORCE_INSTANCE;
  const SF_T = process.env.SALESFORCE_ACCESS_TOKEN;
  const MV_K = process.env.MAVERA_API_KEY;

  const { records: opps } = await fetch(
    `https://${SF}/services/data/v66.0/query?q=${encodeURIComponent(
      "SELECT Name, StageName, Amount, Competitor__c, Description FROM Opportunity WHERE Competitor__c != null ORDER BY CloseDate DESC LIMIT 100"
    )}`,
    { headers: { Authorization: `Bearer ${SF_T}` } }
  ).then((r) => r.json());

  const compDeals = {};
  for (const opp of opps) {
    const comp = (opp.Competitor__c || "").trim();
    if (comp) (compDeals[comp] ??= []).push(opp);
  }

  const topComps = Object.entries(compDeals).sort(([, a], [, b]) => b.length - a.length).slice(0, 5);

  for (const [compName, deals] of topComps) {
    const won = deals.filter((d) => d.StageName === "Closed Won");
    const lost = deals.filter((d) => d.StageName === "Closed Lost");
    const dealCtx = deals.slice(0, 10)
      .map((d) => `- ${d.Name} (${d.StageName}): ${(d.Description || "").slice(0, 120)}`)
      .join("\n");

    const card = await fetch("https://app.mavera.io/api/v1/mave/chat", {
      method: "POST",
      headers: { Authorization: `Bearer ${MV_K}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        message: `Generate a battle card for ${compName}.\nCRM: ${deals.length} mentions | Won: ${won.length} | Lost: ${lost.length}\n\n${dealCtx}\n\nProduce: 1) Overview 2) Strengths 3) Weaknesses 4) Objections 5) Killer questions 6) Landmines 7) Switch reasons`,
      }),
    }).then((r) => r.json());

    const winRate = won.length + lost.length > 0 ? `${won.length}/${won.length + lost.length}` : "N/A";
    console.log(`\n${"=".repeat(50)}`);
    console.log(`BATTLE CARD: ${compName} | Mentions: ${deals.length} | Win rate: ${winRate}`);
    console.log(card.content?.slice(0, 800) || "");
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
==================================================
BATTLE CARD: CompetitorX | Mentions: 23 | Win rate: 14/19
==================================================

### Overview
Series C startup ($45M raised) positioning as "all-in-one revenue platform."
Strong in SMB, weaker in enterprise.

### Their Strengths
- Lower entry price ($99/seat vs our $149/seat)
- Faster self-serve onboarding
- Strong Slack community and developer advocacy

### Where We Win
- No SSO/SCIM below Enterprise tier — dealbreaker for InfoSec
- Dashboard-only reporting; no API export for BI tools
- No dedicated CSM below $50K ACV

### Killer Questions
- "How important is SSO to your security team?"
- "Do you need to pipe data into your existing BI stack?"

### Why Customers Switch
Compliance gaps (SOC 2, SSO) and reporting limitations that
surface 3-6 months after purchase.

Sources: 3
```

<Info>
  `Competitor__c` is a common custom field. If your org uses a different name, run `GET /services/data/v66.0/sobjects/Opportunity/describe` to discover your schema.
</Info>

***

<CardGroup cols={2}>
  <Card title="Salesforce Overview" icon="salesforce" href="/integrations/salesforce">
    Back to all 8 Salesforce jobs
  </Card>

  <Card title="Customer Success Interview Prep" icon="clipboard-check" href="/integrations/salesforce/cs-interview-prep">
    Next: QBR prep from Account health and Cases
  </Card>
</CardGroup>
