Scenario
Pipedrive tracks every touchpoint — calls, emails, meetings. You want to map activity patterns to buyer personas and discover how many touchpoints each persona type needs before they’re ready for a demo.Architecture
Code
import os, requests
from collections import defaultdict
DOMAIN = os.environ["PIPEDRIVE_DOMAIN"]
PD_TOKEN = os.environ["PIPEDRIVE_API_TOKEN"]
PD_BASE = f"https://{DOMAIN}.pipedrive.com"
MAVERA_KEY = os.environ["MAVERA_API_KEY"]
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 activities grouped by person
person_acts = defaultdict(lambda: defaultdict(int))
for act_type in ["call", "email", "meeting"]:
for act in (pd_get("/api/v2/activities", {"type": act_type, "limit": 200, "done": 1}).get("data", []) or []):
if act.get("person_id"):
person_acts[act["person_id"]][act_type] += 1
# 2. Classify into persona archetypes
def classify(counts):
c, e, m = counts.get("call", 0), counts.get("email", 0), counts.get("meeting", 0)
if m >= 3 and c >= 5: return "Relationship Buyer (exec, high-touch)"
if e >= 10 and c <= 2: return "Self-Serve Researcher (dev/IC, async-first)"
if c >= 5 and e >= 5: return "Engaged Evaluator (mid-level, multi-channel)"
return "Low-Touch Prospect (early stage)"
groups = defaultdict(list)
for pid, counts in person_acts.items():
groups[classify(counts)].append({"person_id": pid, **counts})
# 3. Build and run Focus Group
personas = []
for archetype, members in groups.items():
personas.append({
"name": archetype.split("(")[0].strip(),
"description": (
f"{archetype}. {len(members)} contacts. "
f"Avg calls: {sum(m.get('call',0) for m in members)//max(len(members),1)}, "
f"Avg emails: {sum(m.get('email',0) for m in members)//max(len(members),1)}."
),
})
fg_resp = requests.post(
"https://app.mavera.io/api/v1/focus-groups",
headers={"Authorization": f"Bearer {MAVERA_KEY}"},
json={
"title": "Pipedrive Activity-Based Persona Scoring",
"personas": personas,
"questions": [
"How many touchpoints (calls, emails, meetings) before considering a demo?",
"What outreach feels helpful vs. intrusive at each stage?",
"At what point does follow-up become too aggressive?",
],
},
)
fg_resp.raise_for_status()
fg = fg_resp.json()
for resp in fg.get("responses", []):
print(f"\n[{resp['persona_name']}]")
for a in resp.get("answers", []):
print(f" Q: {a['question']}\n A: {a['answer'][:300]}")
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}`);
return res.json();
}
// 1. Pull activities grouped by person
const personActs = {};
for (const actType of ["call", "email", "meeting"]) {
const resp = await pdGet("/api/v2/activities", { type: actType, limit: 200, done: 1 });
for (const act of resp.data || []) {
if (!act.person_id) continue;
personActs[act.person_id] ||= { call: 0, email: 0, meeting: 0 };
personActs[act.person_id][actType]++;
}
}
// 2. Classify personas
function classify({ call = 0, email = 0, meeting = 0 }) {
if (meeting >= 3 && call >= 5) return "Relationship Buyer (exec, high-touch)";
if (email >= 10 && call <= 2) return "Self-Serve Researcher (dev/IC, async-first)";
if (call >= 5 && email >= 5) return "Engaged Evaluator (mid-level, multi-channel)";
return "Low-Touch Prospect (early stage)";
}
const groups = {};
for (const [pid, counts] of Object.entries(personActs)) {
const arch = classify(counts);
(groups[arch] ||= []).push({ person_id: pid, ...counts });
}
// 3. Run Focus Group
const personas = Object.entries(groups).map(([arch, members]) => ({
name: arch.split("(")[0].trim(),
description: `${arch}. ${members.length} contacts. Avg calls: ${Math.round(
members.reduce((s, m) => s + m.call, 0) / members.length)}.`,
}));
const fgRes = await fetch("https://app.mavera.io/api/v1/focus-groups", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.MAVERA_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
title: "Pipedrive Activity-Based Persona Scoring",
personas,
questions: [
"How many touchpoints before considering a demo?",
"What outreach feels helpful vs. intrusive?",
"When does follow-up become too aggressive?",
],
}),
});
const fg = await fgRes.json();
for (const resp of fg.responses || []) {
console.log(`\n[${resp.persona_name}]`);
for (const a of resp.answers || []) console.log(` Q: ${a.question}\n A: ${a.answer.slice(0, 300)}`);
}
Example Output
[Relationship Buyer]
Q: How many touchpoints before considering a demo?
A: I'd want 2-3 meaningful conversations — a call where you understand
my business, a follow-up with a relevant case study, and a brief
meeting with my team. 5-6 total touchpoints over 2-3 weeks.
[Self-Serve Researcher]
Q: How many touchpoints before considering a demo?
A: Just send me docs and a sandbox. I don't want calls. Maybe 1-2 emails
with technical content, then I'll reach out when ready.
Error Handling
| Error | Cause | Fix |
|---|---|---|
400 Bad Request | Invalid activity type filter | Verify type values match Pipedrive’s activity type keys |
Empty person_id | Unlinked activities | Skip unlinked activities |
| Focus Group timeout | Too many personas | Limit to 5-8 personas per Focus Group |