Skill: /investor-shortlist — Investor Shortlist Builder
Objective
Full pipeline questionnaire → multi-source research → Dropcontact + Outlook + Firecrawl enrichment → branded Excel output with transaction pipeline columns.
The output file matches a standardized M&A advisory format used for sell-side, buy-side and fundraising mandates.
Configuration is read from config.yaml in the same folder as this skill. See config.example.yaml for the schema. Important keys:
dropcontact.api_key(or envDROPCONTACT_API_KEY)local_databases— list of Excel files to scanoutput.default_folder— suggested save pathbrand.*— firm name, colors, font for the cover page + table
Step 1 — Structured questionnaire (MANDATORY before any action)
Ask ALL questions below, grouped in 2-3 AskUserQuestion calls, never as bullet plain-text. If the user skips a question, re-ask with an explicit default value.
Block A — Target company & mandate
- Client company name (e.g. "Acme SA") + website (e.g. "acme.com")
- Company country:
🇨🇭 Switzerland/🇫🇷 France/🇲🇨 Monaco/🇱🇺 Luxembourg/Other - Mandate type:
Fundraise(capital growth, product development, geographic expansion)M&A sell-side(full or partial sale)M&A buy-side(external growth — target search)Refinancing / Debt(senior, mezzanine)Restructuring(capital, debt)
Block B — Target profile
Sector: e.g. "Real estate / Construction", "Industrial", "Tech / SaaS", "Healthcare / MedTech", "Energy", "B2B services", etc.
Sub-sector (optional): e.g. "Residential development", "B2B HR SaaS"
If Fundraise → Round:
Pre-seed(<500k)Seed(500k - 2M)Series A(2-10M)Series B(10-30M)Series C / Growth(>30M)Late stage / Pre-IPO
If M&A → Valuation range: e.g. "2-10M", "10-50M", "50-200M", "200M+"
Block C — Volume & typology
- Number of target investors in the shortlist:
15-20/20-30/30-50/50+ - Typology (multi-select):
Private / HNWI / Family officesFinancial institutional(PE funds, VC, AM, pension funds, banks)Strategic / Industrial(sector players, competitors, suppliers/customers)Public / Para-public(cantonal banks, sovereign funds, foundations)
- Investor geography: same country as target / pan-European / global
Block D — Sources & execution
- Source mix:
Local only(local Excel databases + Outlook contacts only)Internet only(Firecrawl/Brave search + Dropcontact)Hybrid(recommended — local + internet in parallel)
- Save path: ask for absolute Windows path, or propose default from
config.yaml > output.default_folder - Filename: auto-generated from
config.yaml > output.filename_templateor ask for custom
Step 2 — Multi-source research
2.1 — Local databases (if Local or Hybrid)
Read every file listed in config.yaml > local_databases. The skill auto-detects the header row by looking for columns matching patterns like:
company,name,firm,investor→ investor nameemail,mail,contact email→ contact emailcountry,pays→ countryfocus,sector,theme,specialty→ investment focusphone,tel,mobile→ phonefirst name,prénom,last name,nom,surname→ person
Filter by sub-sector / country / typology matching the brief. Score relevance 1-5.
If no local databases configured → skip this step and rely on internet + Dropcontact.
2.2 — Outlook contacts (Windows only, if outlook.scan_contacts: true)
Scan local Outlook contacts via PowerShell COM to identify investors already in your address book. Cross-reference by:
CompanyName(fuzzy match with identified investors)- Email domain (match with investor websites)
Reference PowerShell script (inline):
$outlook=New-Object -ComObject Outlook.Application
$ns=$outlook.GetNamespace("MAPI")
$contactsFolder=$ns.GetDefaultFolder(10)
$results=New-Object System.Collections.ArrayList
function ProcessFolder($folder, $list){
foreach($item in $folder.Items){
if($item.Class -eq 40){
[void]$list.Add([PSCustomObject]@{
FullName=$item.FullName; FirstName=$item.FirstName; LastName=$item.LastName
CompanyName=$item.CompanyName; JobTitle=$item.JobTitle
Email1=$item.Email1Address; Email2=$item.Email2Address
BusinessPhone=$item.BusinessTelephoneNumber; MobilePhone=$item.MobileTelephoneNumber
WebPage=$item.WebPage; Categories=$item.Categories
})
}
}
foreach($sf in $folder.Folders){ ProcessFolder $sf $list }
}
ProcessFolder $contactsFolder $results
foreach($store in $ns.Stores){
try{
$root=$store.GetRootFolder()
foreach($f in $root.Folders){
if($f.DefaultItemType -eq 2){ ProcessFolder $f $results }
}
}catch{}
}
$results | ConvertTo-Json -Depth 3 | Out-File -FilePath "$env:TEMP\_outlook_contacts.json" -Encoding utf8
Read the JSON in utf-8-sig (Windows BOM).
A hit in Outlook = the user already has a direct relationship with the contact → score boost, no Dropcontact call needed (Outlook email is more reliable than enriched email).
2.3 — Internet search (if Internet or Hybrid)
For each target with no clear decision-maker, use Firecrawl MCP (or your preferred web search) with focused queries:
"COMPANY_NAME" CEO managing director 2025 OR 2026
"COMPANY_NAME" managing partner OR président
"COMPANY_NAME" investor relations contact
Always double-check names (current CEO vs former, role changes, company rebrandings). Common pitfalls:
- A CEO can have left up to 12 months ago — cross-check via news
- Funds rebrand (e.g. acquisition-driven name changes) — verify current legal name
- Asset managers often have multiple legal entities — pick the one matching the investment thesis
2.4 — Dropcontact enrichment (ALWAYS, unless Outlook email already validated)
API key from config.yaml > dropcontact.api_key or env DROPCONTACT_API_KEY. Never hardcode the key in the script.
Endpoint: POST https://api.dropcontact.io/batch
Payload:
{
"data": [
{"first_name": "First", "last_name": "Last", "company": "Company", "website": "domain.com"}
],
"siren": false
}
Poll GET /batch/{request_id} every 15s (up to 40 attempts).
Returned fields: email[0].email, email[0].qualification (nominative@pro, catch-all@pro, not_found), phone[0].number.
Multi-pass strategy: if Dropcontact returns "Not found", search alternative decision-makers (CFO, Head of M&A, Managing Partner) via Firecrawl, then re-run Dropcontact.
Step 3 — Excel file generation
3.1 — File structure (2 tabs — NO Synthesis tab)
Tab 1: "Cover page" — minimal format (REQUIRED)
No content beyond the 6 lines below. Do NOT add enrichment sources, statistics, score legend, or important notes (this content goes in the chat summary post-generation, not in the file).
| Cell | Content | Style |
|---|---|---|
| B2 | {brand.firm_name} (from config) |
font_family 14, primary_color, italic |
| B5 | {Client Company} — {Mandate type} |
font_family 25, primary_color |
| B6 | Investor shortlist {sector context} |
font_family 18, secondary_color, italic |
| B8 | Prepared for: {Title First Last [and Co-founder]}, {Company} |
font_family 11 |
| B9 | Date: {DD MMM YYYY in English, e.g. "21 May 2026"} |
font_family 11 |
| B10 | Targets: {N} investors {Country} ({typologies comma-separated}) |
font_family 11 |
Concrete example (placeholder):
- B2: "Acme M&A LLP"
- B5: "Example SA — Series A+ Fundraise"
- B6: "Investor shortlist MedTech / FemTech Switzerland"
- B8: "Prepared for: Dr. John Doe and Jane Smith, Example SA"
- B9: "Date: 21 May 2026"
- B10: "Targets: 48 investors Switzerland (financial, family offices, strategic, public)"
Tab 2: "Investors" — 31 columns (header row = 4)
Identification block (cols 1-14):
#(rank)Category(e.g. "Developer / Promoter CH", "Asset Manager RE CH", "Pension fund CH", etc.)Investor(legal name)Country(with flag emoji)City / RegionType(e.g. "Real estate developer", "Listed RE fund manager")Focus / Specialty(sector detail + ticket size)Civ.(Mr / Ms)First nameLast nameTitle / RoleEmail(hyperlink mailto:)PhoneWebsite(hyperlink https://)
Scoring block (col 15):
15. Score (1-5 with color coding from brand.accent_*: 5=green, 4=pale blue, 3=grey, 2=orange)
Transaction pipeline block (cols 16-31):
16. Type (empty at creation — fill manually: "Hot lead", "Warm lead", etc.)
17. Status (empty — "Not contacted", "Contacted", "Replied", "Meeting", "Pitch", "DD", "Closed-Won", "Closed-Lost")
18. Contacted by (initials — from defaults.prepared_by_initials)
19. Last contact (date of last exchange)
20. First intent email sent (X = sent)
21. Email date
22. Teaser sent (X)
23. NDA sent (X)
24. Teaser & NDA date
25. IM and process letter sent (X)
26. IM & process letter date
27. Management presentation (pitch) date
28. MBO/LOI reception date
29. Dataroom opening
30. Signing
31. Closing
All pipeline columns (16-31) are empty at creation — filled in as the transaction progresses.
IMPORTANT: do NOT create a "Synthesis" tab. The file contains only 2 tabs: "Cover page" + "Investors". The statistical summary (count by category, email quality, etc.) is delivered in the post-generation chat message, not in the file.
3.2 — Branding (from config.yaml)
Read brand.* from config.yaml. Defaults shown below match Hectelion SA original styling; override in config for your firm.
# Loaded from config['brand']:
primary_color = "182E4E" # main navy
secondary_color = "0E2841" # darker navy
accent_pale = "DCEAF7" # score 4
accent_green = "6FCF9A" # score 5
accent_orange = "FFC000" # score 2
accent_grey = "F2F2F2" # score 3
alert_red = "C00000" # alerts
border_grey = "D9D9D9" # cell borders
font_family = "Cardo" # cover page + table
# Grid: Cover page 25 / 18 / 14 / 11
# Table: header 11 bold white, data 10 (dense cells)
# Same font across the whole file — single source of truth.
3.3 — Header row style (Investors table)
- Fill:
primary_colorsolid - Text: White,
font_family11pt Bold - Alignment: left, center vertical, wrap_text
- Border: thin
border_grey - Header row height: 32
3.4 — Data rows (Investors table)
- Font:
font_family10pt Investor(col 3):font_family10pt Boldprimary_colorEmail(col 12): hyperlink mailto,font_family10pt color#0563C1underlineWebsite(col 14): hyperlink https,font_family10pt color#0563C1underlineScore(col 15): centered,font_family10ptprimary_colorbold, fill by value- Data row height: 70 (wrap text)
- Freeze panes: header row + first 3 columns
Step 4 — Save
- Verify destination folder exists; create it if not
- If file already open in Excel: close Excel via PowerShell
Get-Process EXCEL | ForEach-Object { $_.CloseMainWindow() }before saving - Save the
.xlsxfile - Optionally open the file automatically:
Start-Process "{path}" - Confirm to the user with a clickable markdown link:
[{filename}.xlsx]({relative_or_absolute_path})
Step 5 — Final report to the user
After generation, present:
## Enrichment summary
**N/Total operational emails (X%)**
| Source | Count |
|---|---|
| ✅ Dropcontact nominative | ... |
| 🟦 Dropcontact catch-all | ... |
| 📇 Outlook (your base) | ... |
| ⚠ Standard to confirm | ... |
| ❓ To clarify | ... |
## Top X high-priority targets (score 5)
1. ...
2. ...
List any emails not found + reason (opaque public funds, low-visibility companies, etc.) with a recommended action (LinkedIn DM, formal letter, etc.).
Best practices
Deduplication
- Before export, dedup by email (one email = one row max)
- If same company but 2 decision-makers: keep the most senior / sector-relevant
- NEVER contact 2 people at the same company with separate emails
Contact details
- If Dropcontact email =
catch-all@pro→ flag in column, keep but mark - If
not found→ fallbackinfo@{domain}+ alternative decision-maker search (CFO, MD) - Phone: prefer direct mobile if Dropcontact returns one
Name verification
- Always validate that the decision-maker currently holds the position (CEOs change)
- Cross-reference Dropcontact name with Firecrawl news if any doubt
- For large institutions: scan the latest annual report / press release if available
LinkedIn fallback
- For non-Outlook contacts, optionally add a "LinkedIn" column with a pre-generated search URL:
https://www.linkedin.com/search/results/people/?keywords={urllib.parse.quote(f"{first} {last} {company}")} - Cell display: "🔗 Search" hyperlink colored
#0A66C2
Data security
- NEVER commit the Dropcontact API key to a public repo (config.yaml is in .gitignore)
- Excel files containing emails and phones should be treated as confidential client data
- Save shortlists to a controlled folder (OneDrive/SharePoint with client access controls)
Reference script
See build_shortlist.py in this skill folder — Python reference implementation, configurable via config.yaml.
Anti-patterns to avoid
- ❌ Skipping the questionnaire and inventing a generic list
- ❌ Calling Dropcontact without checking Outlook first (existing contacts are highest quality)
- ❌ Defaulting all emails to
info@out of laziness — always try Dropcontact first - ❌ Hardcoding the Dropcontact API key in the script — use config.yaml or env var
- ❌ Hardcoding firm branding in the script — read from
config.brand - ❌ Creating a "Synthesis" tab — the file has EXACTLY 2 tabs (Cover + Investors); the stats summary goes in the chat
- ❌ Overloading the cover page (sources, score legend, statistics) — minimal format, 6 lines only (B2/B5/B6/B8/B9/B10)
- ❌ Keeping rows with no email — by default, filter all rows without operational email before export (unless user explicitly asks to keep them)
- ❌ Forgetting the pipeline columns (16-31) — that's the main value-add of the file
- ❌ Listing CEO names without verifying they're still in post
- ❌ Skipping the cover page — it's the visual recap of the file