LLM Deal Flow Automation in CRM A developer built a system that uses large language models to automatically extract structured deal intelligence from sales call transcripts and store it in a CRM. The system, built with PostgreSQL and the Anthropic API, analyzes transcripts to capture sentiment, objections, next steps, risk flags, and recommended stage movements with accuracy comparable to human analysts. The data model uses JSONB columns with GIN indexes to store flexible deal attributes and activity intelligence without requiring schema migrations. In This Article Most CRM systems are excellent at storing what happened — call logged, email sent, stage updated — and poor at capturing what was learned. A sales call produces qualitative intelligence that is genuinely valuable for deal strategy: what objections surfaced, how strongly the prospect signaled interest, what next steps were agreed to, and what risk flags the conversation revealed. That intelligence almost never makes it into the CRM because it requires someone to spend 15 minutes synthesizing unstructured notes into structured fields. Large language models change this equation. Given a call transcript, Claude can extract structured deal intelligence in seconds — categorizing sentiment, identifying specific objections, recommending stage movement, and flagging risk signals — with accuracy that equals or exceeds what a well-trained sales analyst would produce manually. The data model centers on two tables. The deals table stores core deal attributes as a JSONB column, which allows flexible schema evolution without migrations as the intelligence fields change over time. The deal activities table records each interaction — calls, emails, meetings — with the raw content in TEXT and the extracted intelligence in a separate JSONB column. A GIN index on both JSONB columns enables fast attribute queries across the deal pipeline. CREATE TABLE deals id UUID PRIMARY KEY DEFAULT gen random uuid , company TEXT NOT NULL, contact TEXT, stage TEXT, attributes JSONB DEFAULT '{}', created at TIMESTAMPTZ DEFAULT now , updated at TIMESTAMPTZ DEFAULT now ; CREATE TABLE deal activities id UUID PRIMARY KEY DEFAULT gen random uuid , deal id UUID REFERENCES deals id , activity type TEXT, raw content TEXT, intelligence JSONB, created at TIMESTAMPTZ DEFAULT now ; CREATE INDEX ON deals USING GIN attributes ; CREATE INDEX ON deal activities USING GIN intelligence ; The DealIntelligence class wraps the Anthropic API call and parses the structured JSON response. The prompt instructs the model to return a fixed schema — sentiment, objections, next steps, deal signals, risk flags, recommended stage, and a summary — which can be validated and stored directly without post-processing. python import anthropic, json, psycopg2 from dataclasses import dataclass from typing import Dict, List @dataclass class DealRecord: deal id: str company: str contact: str stage: str class DealIntelligence: def init self, db conn: str : self.client = anthropic.Anthropic self.db = psycopg2.connect db conn def analyze transcript self, transcript: str, deal: DealRecord - Dict: prompt = f"""Analyze this sales call transcript for deal intelligence. Return valid JSON only, with these exact keys: - sentiment: "positive" | "neutral" | "negative" - objections: list of specific objections raised - next steps: list of agreed action items - deal signals: list of positive buying signals - risk flags: list of concerns or blockers identified - recommended stage: suggested CRM stage based on this conversation - summary: 2-3 sentence executive summary Company: {deal.company} Contact: {deal.contact} Current Stage: {deal.stage} Transcript: {transcript}""" response = self.client.messages.create model="claude-opus-4-7", max tokens=1500, messages= {"role": "user", "content": prompt} return json.loads response.content 0 .text def draft followup email self, intelligence: Dict, deal: DealRecord - str: objections text = ", ".join intelligence.get 'objections', next steps text = "\n- ".join intelligence.get 'next steps', prompt = f"""Draft a professional follow-up email for this sales conversation. Contact: {deal.contact} at {deal.company} Call Summary: {intelligence.get 'summary', '' } Objections Raised: {objections text} Next Steps Agreed: - {next steps text} Write a concise, personalized follow-up that confirms next steps and addresses the main objections without being pushy. Professional but warm tone.""" response = self.client.messages.create model="claude-opus-4-7", max tokens=600, messages= {"role": "user", "content": prompt} return response.content 0 .text def save activity self, deal id: str, transcript: str, intelligence: Dict : with self.db.cursor as cur: cur.execute """INSERT INTO deal activities deal id, activity type, raw content, intelligence VALUES %s, 'call', %s, %s """, deal id, transcript, psycopg2.extras.Json intelligence Update stage if recommended stage differs recommended = intelligence.get 'recommended stage' if recommended: cur.execute "UPDATE deals SET stage = %s WHERE id = %s", recommended, deal id self.db.commit The follow-up email draft uses the intelligence output as its input — pulling objections, next steps, and the summary to generate a personalized message that is specific to the actual conversation rather than a generic template. The draft goes into a review queue rather than sending automatically. A human confirms and sends. This keeps the LLM in an assistive role and preserves the personal judgment that high-stakes business development requires. Storing intelligence as JSONB rather than normalized columns is deliberate. The schema of deal intelligence evolves as the LLM prompt evolves — new keys appear, existing ones are renamed, arrays grow. JSONB absorbs these changes without schema migrations. The GIN index makes these fields queryable: finding all deals where a pricing objection was flagged is a straightforward WHERE intelligence @ '{"objections": "pricing" }' query. The complete workflow is: transcript arrives from a recording integration, a manual paste, or an email thread → analyze transcript extracts structured intelligence → save activity persists it and updates the deal stage → draft followup email generates a draft for human review. The entire flow completes in under 10 seconds per call, turning what was a 15-minute manual task into a background process that surfaces better intelligence than most teams produce by hand. This post was originally published on White Oak Intelligence. Read the full article there for formatted diagrams, code examples, and related content.