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

# Lead-to-Persona Automation

> Auto-create Mavera Custom Personas for each ICP segment based on Close CRM lead data — industry, company size, and title patterns

## Scenario

Close organizes contacts under Leads (companies/accounts). You want to **auto-create Mavera Custom Personas for each ICP segment** based on lead data — industry, company size, title patterns — keeping your persona library current with your actual pipeline.

## Architecture

```mermaid theme={"dark"}
flowchart LR
    A[Close Leads + Contacts] --> B["Segment by ICP (industry, role)"] --> C["POST /api/v1/personas"] --> D[Persona library]
```

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests
  from requests.auth import HTTPBasicAuth
  from collections import defaultdict

  CLOSE_KEY = os.environ["CLOSE_API_KEY"]
  CLOSE_AUTH = HTTPBasicAuth(CLOSE_KEY, "")
  MAVERA_KEY = os.environ["MAVERA_API_KEY"]

  def close_get(path, params=None):
      r = requests.get(f"https://api.close.com/api/v1{path}", auth=CLOSE_AUTH, params=params or {})
      r.raise_for_status()
      return r.json()

  # 1. Pull leads with contacts
  leads = close_get("/lead/", {"_limit": 200, "_fields": "id,display_name,contacts,custom"}).get("data", [])

  # 2. Segment by ICP
  segments = defaultdict(list)
  for lead in leads:
      custom = lead.get("custom", {})
      industry = custom.get("Industry", custom.get("industry", "General"))
      title = (lead.get("contacts", [{}])[0]).get("title", "Unknown") if lead.get("contacts") else "Unknown"

      role = "IC"
      tl = title.lower()
      if any(t in tl for t in ["cto", "ceo", "cfo", "vp", "chief", "head"]):
          role = "Executive"
      elif any(t in tl for t in ["director", "manager", "lead"]):
          role = "Manager"

      segments[f"{industry} | {role}"].append({
          "company": lead.get("display_name", "Unknown"), "title": title,
          "size": custom.get("Company Size", "Unknown"),
      })

  # 3. Create personas in Mavera
  created = 0
  for seg_key, members in segments.items():
      if len(members) < 2:
          continue
      industry, role = seg_key.split(" | ")
      titles = list({m["title"] for m in members if m["title"] != "Unknown"})[:3]
      companies = [m["company"] for m in members][:3]

      resp = requests.post(
          "https://app.mavera.io/api/v1/personas",
          headers={"Authorization": f"Bearer {MAVERA_KEY}"},
          json={
              "name": f"{industry} {role}",
              "description": (
                  f"A {role.lower()}-level buyer in {industry}. "
                  f"Based on {len(members)} leads. Titles: {', '.join(titles)}. "
                  f"Companies: {', '.join(companies)}. Size: {members[0]['size']}."
              ),
              "metadata": {"source": "close_crm", "segment": seg_key, "lead_count": len(members)},
          },
      )
      if resp.ok:
          print(f"Created: {resp.json().get('name')} ({len(members)} leads)")
          created += 1
      else:
          print(f"Failed for {seg_key}: {resp.status_code}")

  print(f"\nTotal personas created: {created}")
  ```

  ```javascript JavaScript theme={"dark"}
  const CLOSE_KEY = process.env.CLOSE_API_KEY;
  const CLOSE_AUTH = "Basic " + Buffer.from(`${CLOSE_KEY}:`).toString("base64");
  const MAVERA_KEY = process.env.MAVERA_API_KEY;

  async function closeGet(path, params = {}) {
    const url = new URL(`https://api.close.com/api/v1${path}`);
    for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
    const res = await fetch(url, { headers: { Authorization: CLOSE_AUTH } });
    if (!res.ok) throw new Error(`Close ${res.status}`);
    return res.json();
  }

  // 1. Pull leads
  const leads = (await closeGet("/lead/", { _limit: 200, _fields: "id,display_name,contacts,custom" })).data || [];

  // 2. Segment by ICP
  const segments = {};
  for (const lead of leads) {
    const custom = lead.custom || {};
    const industry = custom.Industry || custom.industry || "General";
    const title = lead.contacts?.[0]?.title || "Unknown";
    const tl = title.toLowerCase();
    let role = "IC";
    if (["cto", "ceo", "cfo", "vp", "chief", "head"].some((t) => tl.includes(t))) role = "Executive";
    else if (["director", "manager", "lead"].some((t) => tl.includes(t))) role = "Manager";
    const key = `${industry} | ${role}`;
    (segments[key] ||= []).push({ company: lead.display_name || "Unknown", title, size: custom["Company Size"] || "Unknown" });
  }

  // 3. Create personas
  let created = 0;
  for (const [segKey, members] of Object.entries(segments)) {
    if (members.length < 2) continue;
    const [industry, role] = segKey.split(" | ");
    const titles = [...new Set(members.map((m) => m.title).filter((t) => t !== "Unknown"))].slice(0, 3);
    const companies = members.slice(0, 3).map((m) => m.company);

    const res = await fetch("https://app.mavera.io/api/v1/personas", {
      method: "POST",
      headers: { Authorization: `Bearer ${MAVERA_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify({
        name: `${industry} ${role}`,
        description: `A ${role.toLowerCase()}-level buyer in ${industry}. ${members.length} leads. ` +
          `Titles: ${titles.join(", ")}. Companies: ${companies.join(", ")}.`,
        metadata: { source: "close_crm", segment: segKey, lead_count: members.length },
      }),
    });
    if (res.ok) { console.log(`Created: ${(await res.json()).name} (${members.length} leads)`); created++; }
    else console.error(`Failed for ${segKey}: ${res.status}`);
  }
  console.log(`\nTotal created: ${created}`);
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
Created: SaaS Executive (12 leads)
Created: SaaS Manager (8 leads)
Created: Healthcare Executive (5 leads)
Created: FinTech IC (15 leads)
Created: Manufacturing Manager (4 leads)
Created: E-Commerce Executive (7 leads)

Total personas created: 6
```

## Error Handling

| Error                 | Cause                      | Fix                                                               |
| --------------------- | -------------------------- | ----------------------------------------------------------------- |
| Custom fields missing | Field keys vary by org     | List via `GET /custom_field/lead/` and map dynamically            |
| Duplicate personas    | Running sync repeatedly    | Check existing personas via `GET /personas` first; update instead |
| Empty contacts        | Lead has no contacts       | Skip or use company-level data only                               |
| Sparse segments       | Too-granular custom fields | Set minimum threshold (3+ leads per segment)                      |
