Scenario
Your sales team writes deal notes at every pipeline stage. Each stage has a different conversational texture. You want to know which messaging patterns correlate with wins at each stage so you can coach reps.Architecture
Code
import os, requests
from collections import defaultdict
from openai import OpenAI
DOMAIN = os.environ["PIPEDRIVE_DOMAIN"]
PD_TOKEN = os.environ["PIPEDRIVE_API_TOKEN"]
PD_BASE = f"https://{DOMAIN}.pipedrive.com"
def pd_get(path, params=None):
params = params or {}
params["api_token"] = PD_TOKEN
r = requests.get(f"{PD_BASE}{path}", params=params)
r.raise_for_status()
return r.json()
# 1. Pull deals for a pipeline, group by stage
deals = pd_get("/api/v2/deals", {"pipeline_id": 1, "limit": 100}).get("data", [])
stage_deals = defaultdict(list)
for d in deals:
stage_deals[d["stage_id"]].append(d["id"])
# 2. Pull notes per deal, bucket by stage
stage_notes = defaultdict(list)
for stage_id, deal_ids in stage_deals.items():
for deal_id in deal_ids:
notes = pd_get("/api/v1/notes", {"deal_id": deal_id, "limit": 50}).get("data", []) or []
for note in notes:
if note.get("content"):
stage_notes[stage_id].append(note["content"][:500])
# 3. Analyze each stage with Mavera Chat
mavera = OpenAI(api_key=os.environ["MAVERA_API_KEY"], base_url="https://app.mavera.io/api/v1")
stage_names = {1: "Prospecting", 2: "Proposal", 3: "Negotiation", 4: "Closed Won"}
for stage_id, notes in stage_notes.items():
combined = "\n---\n".join(notes[:30])
response = mavera.responses.create(
model="mavera-1",
input=[{
"role": "user",
"content": (
f"You are a senior sales coach. Below are deal notes from the "
f"'{stage_names.get(stage_id, stage_id)}' stage.\n\n"
f"Analyze language patterns. What messaging resonates at this stage? "
f"What should reps use or avoid?\n\n{combined}"
),
}],
)
print(f"\n=== {stage_names.get(stage_id, stage_id)} ===")
print(response.output[0].content[0].text[:600])
const OpenAI = require("openai");
const DOMAIN = process.env.PIPEDRIVE_DOMAIN;
const PD_TOKEN = process.env.PIPEDRIVE_API_TOKEN;
const PD_BASE = `https://${DOMAIN}.pipedrive.com`;
async function pdGet(path, params = {}) {
const url = new URL(`${PD_BASE}${path}`);
url.searchParams.set("api_token", PD_TOKEN);
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
const res = await fetch(url);
if (!res.ok) throw new Error(`Pipedrive ${res.status}: ${await res.text()}`);
return res.json();
}
// 1. Pull deals, group by stage
const deals = (await pdGet("/api/v2/deals", { pipeline_id: 1, limit: 100 })).data || [];
const stageDeals = {};
for (const d of deals) (stageDeals[d.stage_id] ||= []).push(d.id);
// 2. Pull notes per deal, bucket by stage
const stageNotes = {};
for (const [stageId, dealIds] of Object.entries(stageDeals)) {
stageNotes[stageId] = [];
for (const dealId of dealIds) {
const notes = (await pdGet("/api/v1/notes", { deal_id: dealId, limit: 50 })).data || [];
for (const n of notes) if (n.content) stageNotes[stageId].push(n.content.slice(0, 500));
}
}
// 3. Analyze each stage
const mavera = new OpenAI({ apiKey: process.env.MAVERA_API_KEY, baseURL: "https://app.mavera.io/api/v1" });
const stageNames = { 1: "Prospecting", 2: "Proposal", 3: "Negotiation", 4: "Closed Won" };
for (const [stageId, notes] of Object.entries(stageNotes)) {
const combined = notes.slice(0, 30).join("\n---\n");
const response = await mavera.responses.create({
model: "mavera-1",
input: [{ role: "user", content:
`You are a senior sales coach. Below are deal notes from '${stageNames[stageId] || stageId}'.\n\n` +
`Analyze language patterns. What messaging resonates? What should reps avoid?\n\n${combined}` }],
});
console.log(`\n=== ${stageNames[stageId] || stageId} ===`);
console.log(response.output[0].content[0].text.slice(0, 600));
}
Example Output
=== Prospecting ===
Notes that led to advancement use open-ended discovery questions
("What does your current workflow look like?") rather than feature-dumps.
Avoid: "Just checking in" and "circling back" — these correlate with stalls.
=== Negotiation ===
Winning notes reference the prospect's own language ("as you mentioned,
speed-to-deploy is critical") rather than internal jargon. Avoid premature
discounting. Use value-anchoring ("the ROI model we built shows…").
Error Handling
| Error | Cause | Fix |
|---|---|---|
401 Unauthorized | Expired API token | Regenerate in Pipedrive settings or refresh OAuth token |
429 Too Many Requests | Exceeded 30,000 daily tokens | Batch requests across days; implement backoff |
| Empty notes list | Deals with no notes | Filter out stages with < 3 notes before analysis |