Synthesizing 50+ Customer Interviews with Claude Code, Step by Step
Date: July 6, 2026 • Author: Second Axis
Somewhere in your Gong or Fireflies archive are fifty-plus recorded conversations with customers: discovery interviews, sales calls, CS check-ins. Inside them are the pains people actually described, the workarounds they admitted to, and the exact words they used, which are worth more than any summary. And right now that corpus is functionally write-only. Someone synthesized the first twenty interviews into a deck eight months ago; thirty calls have landed since, and nobody has read them.
Can you build a system with Claude Code that keeps up? Yes. The architecture is a close cousin of the conversation intelligence playbook: a cheap-model extraction pass per call, clustering on the extracted records, agents that read only summaries, and committed state that makes every run incremental. This post covers what's different when the text is customer interviews instead of product chats, and it does not skip the engineering. The one-off version of this task has decent scaffolding already (more on Anthropic's Product Management plugin below). The continuous version, the one where the quote library is still accurate in March, is the thing you build here.
What you're building
A system that, every week as new calls land:
- Maintains clustered themes across the whole corpus: pains, desired outcomes, and current workarounds, each with volume, trend, and which personas voice them.
- Maintains a verbatim quote library, committed to the repo as a queryable file, where every quote carries speaker attribution, a timestamp, a source call, a theme tag, and a persona tag. This is the durable asset; reports age, the quote library compounds.
- Runs persona validation: checks the evidence against your persona documents and flags where real customers contradict the personas you wrote.
- Surfaces open research questions: things multiple customers raised that no interview has probed properly, so your next discovery call has an agenda grounded in data.
- Diffs against the previous run, so the weekly output is "what changed," not the same synthesis reheated.
Why this needs the extraction architecture
An hour-long call transcript is roughly 8,000-10,000 spoken words, and once you add diarized speaker labels, timestamps, and structural markup from the export, a single transcript lands around 25,000-40,000 tokens. Fifty of them is roughly 2 million tokens, ten times a 200K context window, and the corpus grows every week.
So the pattern from the anchor playbook applies directly, and per-call extraction is mandatory, not optional: every transcript gets read exactly once by an inexpensive model, producing a small structured record; clustering runs on the records; Claude Code's analyst agents read cluster summaries and pull full transcripts only for spot-checks. I won't re-explain the three-layer architecture, the watermark mechanics, or stable cluster identity here; the anchor post covers all of it in depth. What follows is what's specific to interview synthesis: the sources, the extraction schema, the speaker-attribution problem, the quote library, and persona validation.
One scoping note that makes this workflow friendlier than conversation intelligence: volume. You're processing a 2M-token backlog once, then a handful of calls per week, not 27M tokens weekly forever. The engineering is similar; the bill is not.
Phase 0: Prerequisites
- Claude Code installed and authenticated, on a Pro, Max, Team, or Enterprise plan for the scheduling and publishing steps.
- An Anthropic API key with billing, separate from the subscription, for the extraction script. At this volume it's a small line item (numbers at the end), but it is metered API spend.
- Transcript access. Two paths, and you likely want both:
- MCP for interactive work. Community servers exist for both major tools: kenazk/gong-mcp for Gong and Props-Labs/fireflies-mcp for Fireflies. Both are community-maintained, not vendor-official, so review the code and pin a version before pointing them at customer calls.
- Export APIs for bulk. The backfill and the weekly pull should go through each vendor's export API via a script, not through MCP tool calls. Same rule as every playbook in this series: MCP is for targeted queries, scripts are for moving data.
- Python 3.11+ with
duckdb,pandas,scikit-learn, and an embeddings option (localsentence-transformersis fine at this scale). - A dedicated git repo. Committed state is the continuity mechanism for scheduled runs; this is non-negotiable.
- Your persona documents, checked into the repo as markdown. Phase 4 validates them against evidence, which only works if they're in the repo as text.
- A privacy decision. Customer calls contain names, company details, and sometimes commercials. The extraction pass sends transcript text to the API; get the same sign-off you'd need for any processor, and redact in the export script if policy requires.
On the one-off scaffolding: Anthropic's knowledge-work-plugins include a Product Management plugin with a /synthesize-research command. Install it; for a single afternoon over a dozen transcripts it's genuinely useful. Be equally clear about what it does not give you: the continuously growing corpus, the committed quote library, incremental processing, persona validation, or scheduling. That's this playbook.
Phase 1: The repo
interview-synthesis/
├── CLAUDE.md
├── personas/ # your persona docs, as markdown
├── .claude/
│ ├── agents/
│ │ ├── theme-analyst.md # digs into one theme cluster
│ │ ├── persona-auditor.md # personas vs. evidence
│ │ ├── synthesizer.md
│ │ └── critic.md # judge loop before anything ships
│ └── skills/
│ └── interview-synth/
│ └── SKILL.md
├── scripts/
│ ├── export_calls.py # Gong/Fireflies export → data/
│ ├── extract.py # per-call extraction → records
│ ├── embed_assign.py # assign records to theme clusters
│ └── build_quote_library.py # records → quotes.parquet
├── state/ # committed: the system's memory
│ ├── themes.json # stable theme IDs, centroids, names
│ ├── watermark.json # last processed call timestamp
│ └── quotes.parquet # THE quote library, committed
├── data/ # gitignored: raw transcripts, embeddings
└── runs/ # committed: weekly reports + diffs
Two deviations from the anchor repo are deliberate. First, state/quotes.parquet is committed even though it's data, because at this scale it stays small (a thousand quotes with metadata is well under a megabyte) and committing it is what makes it a team asset with history. JSONL works equally well if you want diffs to be human-readable in PRs. Second, personas/ holds the documents under test; when the persona-auditor flags a contradiction, the fix is a PR against those files, with the evidence in the PR description.
CLAUDE.md carries the standard rules (never read raw transcripts into context; work from records and summaries; stable theme IDs, never renumbered) plus one specific to this corpus:
## Source weighting
- source_type is "interview", "sales_call", or "cs_call" on every record.
- Discovery interviews are the highest-signal source.
- Sales calls oversample pain that closes deals; prospects tell reps
what justifies budget, not everything that's broken.
- Reports must show per-theme source mix and say so when a theme's
evidence is mostly sales calls.
That bias is real and unglamorous: a theme built from twelve sales calls and zero interviews is a theme about your pipeline, not your users. Make the weighting explicit in every report.
Phase 2: Ingestion and extraction
export_calls.py pulls every call newer than state/watermark.json, normalizes both vendors' formats into one transcript schema (speaker turns with labels and timestamps), and writes to data/. Run it once with --full for the backlog, then incrementally forever.
extract.py is the heart, same as in the anchor playbook: Batch API, cheapest model that passes your spot-checks, one structured record per call. The interview-specific schema:
{
"call_id": "...",
"source_type": "interview",
"pains": [{"description": "...", "severity": "blocks core workflow"}],
"desired_outcomes": ["..."],
"current_workarounds": ["exports to sheets, rebuilds by hand"],
"quotes": [{
"text": "we tried three tools before giving up",
"speaker": "customer",
"speaker_name": "participant_2",
"timestamp": "00:23:41",
"verbatim": true
}],
"persona_signals": {"role": "ops lead", "team_size": "8",
"closest_persona": "operator_olivia",
"confidence": "medium"},
"schema_version": 3
}
The speaker-attribution pitfall deserves its own paragraph. Diarization is imperfect, and transcripts routinely mislabel turns, especially on calls with crosstalk or more than two participants. The failure mode this produces is specific and embarrassing: the "customer quote" in your synthesis is actually your own sales rep pitching. The rep says "so the manual process is costing you ten hours a week, right?", the label is wrong or the extractor sloppily attributes the framing to the customer, and now your quote library contains your own talk track wearing a customer's name. Defend in two layers. In extraction: give the model the known internal participants (rep and interviewer names come with the call metadata from both Gong and Fireflies) and require it to mark any quote where the surrounding turns make attribution ambiguous. In review: the critic verifies speaker labels, which is Phase 4.
Phase 3: Themes and the quote library
embed_assign.py follows the anchor playbook exactly: embed each record's pains and outcomes, assign to existing theme clusters within a distance threshold, hold out the rest, and let dense holdout groups become candidate themes a human confirms by name. Stable theme IDs live in state/themes.json, and the mechanics (thresholds, periodic re-clustering with ID remapping) are covered there; nothing about interviews changes them.
build_quote_library.py is the addition. It flattens every extracted quote into state/quotes.parquet with columns: quote text, speaker, timestamp, call ID, call date, source type, theme ID, persona tag, verbatim flag. From then on, anyone with the repo can answer real questions in one DuckDB line: every verbatim quote about onboarding pain from ops-persona interviews in the last quarter, with timestamps deep-linking back to the recording. No dashboard, no vendor seat, just a file in git that every run appends to. This file outlives every report the system writes.
Phase 4: The agents
Same fan-out shape as the anchor (analysts, synthesizer, critic), with two role changes.
theme-analyst.md takes one theme cluster's summary plus its exemplar records and answers: what is the underlying job, how severe is it (use the extracted severity, not vibes), what workarounds do people run today, what's the source mix, and which quotes best evidence it. It may read at most two full transcripts to verify context around a quote; that's the only place raw transcripts enter a context window.
persona-auditor.md is new. It reads personas/*.md, then queries quotes.parquet and the persona signals across records, and produces three lists: claims in the persona docs the evidence supports (with counts), claims the evidence contradicts (with the contradicting quotes), and interviewees who fit no persona. That third list is where new segments announce themselves. The auditor never edits the persona docs; it flags, humans decide.
critic.md is the judge loop, and here its checklist has teeth:
1. Every quote in the draft: grep it verbatim against the extracted
records AND the source transcript. Not found → FAIL.
2. Every quote attributed to a customer: open the source transcript
at the timestamp and check the speaker turn. If the speaker is a
known internal participant, or attribution is ambiguous → FAIL.
3. Every theme claim: does the stated source mix match the records?
A "top customer pain" that is 80% sales calls must say so.
4. Persona contradictions: is each backed by ≥2 independent calls?
5. Diff check: does the report say what changed vs. last run?
Return PASS or FAIL with specific fixes.
Check 2 is the one this workflow cannot skip. A fabricated quote is bad; a real quote from your own rep presented as customer evidence is worse, because it's uncheckable by the reader and flatters exactly the narrative the team already believes.
Phase 5: Scheduling, backfill first
The /interview-synth skill mirrors the anchor's: read prior state, export new calls, extract, assign, rebuild the quote library, fan out analysts over active themes plus the persona-auditor, synthesize with last run's report in hand, critique, persist, publish.
Run it in two modes:
- Backfill, once, interactively. Point it at the full historical corpus and stay in the loop. This is where you tune the extraction schema against real transcripts, set the clustering threshold, name the first themes, and hand-grade 30 extractions including speaker labels. Do not schedule anything until a backfill report is one you'd forward.
- Incremental, weekly, scheduled. After backfill,
watermark.jsonmakes every run process only new calls. Create a weekly cloud routine with/schedule; it clones the repo, so it inheritsstate/(themes, watermark, quote library) automatically. That committed state is the continuity mechanism: nothing passes between scheduled runs except what you persist to the repo, so persisting well is the design. Configure the Gong/Fireflies and Anthropic API keys in the routine's environment. - Self-hosted alternative: cron plus headless mode (
claude -p "/interview-synth") on a machine you own, or a GitHub Actions cron with claude-code-action. Customer call content then flows through whatever runs the job; for many teams that decides it. - Failure alerting: a
Stophook posting run status to Slack, because a synthesis system that quietly died in August is worse than none.
Phase 6: Sharing with the team
- Artifact publish for the weekly synthesis page, with the standard caveats: stable URL after first approval, org-scoped visibility needs Team or Enterprise, and publishing works from the CLI and desktop app rather than every automated surface, so a fully autonomous pipeline may keep a human on the publish step.
- Slack delivery of the movers: new themes, persona contradictions, and this week's best three quotes with links.
- The quote library is the real distribution. Reports get skimmed; a searchable, committed quote file gets used. Teach the team the one-liner ("query quotes.parquet for theme X, persona Y") or wrap it in a tiny read-only skill. When a PM writing a spec can pull five verbatim, speaker-verified, timestamped quotes in thirty seconds, the system has paid for itself regardless of whether anyone read Monday's report.
Phase 7: Maintenance (what you own now)
The anchor playbook's list applies wholesale: threshold tuning, re-cluster reviews, extraction quality audits, schema migrations, candidate-theme hygiene, secrets rotation. Interview synthesis adds:
- Persona doc lifecycle. The auditor flags contradictions; a human must actually update
personas/or the flags repeat weekly until everyone ignores them. - Interviewer roster upkeep. Speaker verification depends on knowing who's internal. New rep joins, the roster in the export script lags, and attribution checks silently weaken.
- Source-mix drift. If sales calls start dominating the corpus, every theme quietly tilts toward deal-closing pain. The per-theme source mix in reports is your alarm; the fix is running more actual interviews, which no pipeline does for you.
- Quote hygiene. Customers occasionally say things in calls they'd never want quoted in a company-wide doc. Decide the redaction policy, and put it in the export script, not in people's judgment at report time.
What this costs
Transparent assumptions: 50 historical calls at ~35K tokens each, then 4-6 new calls per week.
- Backfill extraction: ~1.75M input tokens through a small model on the Batch API (which discounts roughly 50%). At small-model batch rates that is low single digits of dollars, once. If spot-checks force a mid-tier model, several times that, still a one-time cost.
- Weekly increment: 4-6 calls is roughly 150-200K tokens of extraction. Well under a dollar a week at the same rates. This is the payoff of the watermark design: the recurring API cost is proportional to new calls, not corpus size.
- The analysis session: analysts read cluster summaries and at most a couple of transcripts; comfortably inside subscription usage, same caveats as always about agents wandering into
data/. - Embeddings: negligible at this volume, free with a local model.
- The real invoice: an extraction schema someone tuned against real transcripts, a speaker roster someone maintains, persona docs someone actually revises, and a weekly candidate-theme triage. Cheaper than the conversation-intelligence system by an order of magnitude in tokens, but the human duties are the same shape.
The four practical challenges
- Setup at scale: a 2M-token backlog and a growing corpus mean per-call extraction, embedding-based themes, and agents on summaries, the anchor architecture applied without shortcuts, plus the speaker-attribution defenses this corpus uniquely needs.
- Sharing with the team: artifact publish and Slack for the weekly synthesis, but the committed quote library is the asset that actually gets used, and it needs a repo your team can reach.
- Maintenance: everything from the anchor playbook plus persona lifecycle, interviewer roster upkeep, source-mix vigilance, and quote redaction policy. The system flags; humans still decide.
- Cost: the cheapest text-at-scale workflow in this series to run, single-digit dollars for the backfill and cents weekly after, which means the binding cost is entirely the engineering and upkeep time. Budget for that time explicitly.
The managed alternative
The managed home for this work is a research repository, and Dovetail is the category's reference point: transcripts in, tagging, highlights, and shareable insights out. It's genuinely good at organizing research a team actively curates. The distance between that and this playbook is the curation itself: repositories organize what researchers tag, while the system above extracts, clusters, and accumulates without waiting for anyone to tag anything, and folds in sales and CS calls that never get research attention. If your team has a researcher who lives in the repo, buy the repo. If insights die because nobody has time to tag, the pipeline is the missing piece.
Doing this with Second Axis
Everything above is the do-it-yourself path. On Second Axis, interview synthesis is a workflow in the marketplace: connect Gong or Fireflies, choose the workflow, and set the goal ("a living theme and quote library across all customer calls, refreshed weekly"). It runs as calls land, with the extraction, theme clustering, speaker checks, and quote library built in, and it monitors itself: missing transcripts, failed runs, or a source that stops syncing get caught and alerted rather than eroding the corpus silently.