Automation · Playbook 01

Sync pipeline company financials to CRM.

Keep every CRM company record current with the latest financials and round history without manual entry.

Sourcing

Helper code snippet

Reusable code that powers a step inside this automation — loop bodies, parsers, routers, and other glue.

  • PitchBook Company Enrichment
    Python · pitchbook_enrich.py

    One-shot PitchBook enrichment: investors, geography, round type, total raised, last round size & valuation — with USD enforcement and a configurable top-investor watchlist.

Reference build

A working reference build that runs in production. When a deal team flips "Get Financial Data = Yes" on a company in the CRM pipeline, the orchestrator resolves the company against the data provider, pulls investor roster, geography, and full round history, normalizes everything to USD, and writes the structured fields back onto the CRM record — plus updates each matched top-investor's portfolio list. End-to-end in ~30 seconds, no analyst touch.

Vendors below are our choices. The flow is roles-not-vendors; every layer swaps cleanly.

Flow
01 · Trigger
Deal-pipeline entry updated in CRM
Listens for changes to the 'Get Financial Data' attribute on the Deal Pipeline list
02 · Gate
Filter: only continue if Get Financial Data = Yes
Prevents unrelated field edits from triggering paid API calls
03 · Resolve & fetch
Python step → data provider API
Search by website (preferred) or company name → /search → /companies/{id}/investors, /bio, /most-recent-financing. USD enforced on all money fields.
04 · No-data gate
Stop if data_status = 'no data'
Resolution miss returns a full no-op shape — no write happens, no field gets nulled
05 · Write-back to deal record
Update CRM list entry
Writes investors, geography, last round type, totals, last round size + valuation + date back onto the same deal-pipeline entry
06 · Gate
Stop if matched_investors = 'None'
Only proceed when the cap table overlaps the firm's top-investor watchlist
Loop — repeat steps 07 → 08 for every matched investor (cap 500)
↻ per iteration
07 · Find investor record
Lookup the investor in the CRM Company object
Matches by name; skips silently if no record exists
08 · Write portfolio link
Append this company to the investor's portfolio list
Closes the loop: every top investor on the cap table now points back at the deal record

Fields written back to the CRM

The Python step always returns the same shape, whether resolution succeeded or not. Stable output contract = stable downstream filters and write steps.

FieldWhat it holds
investorsFull investor roster from PB, comma-joined
matched_investorsSubset that maps to our top-investor watchlist (canonical names + aliases)
match_countHow many watchlist investors are on the cap table
company_geographyHQ city, state, country from PB bio
last_round_typeMost recent financing deal type (e.g. Series B)
total_funding_amountLifetime money raised (USD-enforced)
last_funding_amountMost recent round size (USD-enforced)
last_funding_valuationPost-money valuation, falls back to last known valuation
last_funding_dateDate of the most recent financing

Gotchas

The things you only learn by running this in anger for a year.

  • 01USD-only enforcement: any non-USD currency on totalMoneyRaised, lastFinancingSize, lastFinancingValuation, or lastKnownValuation raises. Multi-currency funds break field comparability — fail loud instead of silently writing GBP into a USD column.
  • 02Graceful no-data path: every PB call is wrapped in a safe_get that returns {} on error. If search returns nothing, the flow still emits a full output shape with data_status = 'no data' so downstream filters and writes don't NPE.
  • 03Company-first search resolution: prefer pbId of an item with primaryFirmType.type = 'COMPANY'. Otherwise fall back to the first item. Avoids resolving to investors/people with the same name.
  • 04Investor matching uses two passes: an exact case-insensitive canonical map plus a hand-maintained ALIASES dict ('Coatue Management' → 'Coatue', 'M12 - Microsoft's venture fund' → 'M12'). Without aliases, ~15% of matches are missed.
  • 05Loop step iterates matched_investors and finds-or-skips each in the Company object, then writes the portfolio company onto the investor record. Iteration cap = 500 to prevent runaway loops on bad parses.
  • 06Trigger gate: only runs when 'Get Financial Data' attribute = Yes on the deal-pipeline entry. Prevents accidental re-runs from unrelated field edits burning PB API budget.

Swap matrix

Every layer is replaceable. The orchestrator owns the wiring so the CRM, the data provider, and the transform layer all move independently.