Scenario
Your winning email copy is locked inside HubSpot engagement records. You extract emails from Closed Won deals, distill a Brand Voice from the highest-converting messaging, then generate new variations that carry the same tone and persuasion.Architecture
Code
import os, requests
from openai import OpenAI
HS = os.environ["HUBSPOT_ACCESS_TOKEN"]
MV = os.environ["MAVERA_API_KEY"]
HB = "https://api.hubapi.com/crm/v3"
MB = "https://app.mavera.io/api/v1"
# 1. Closed Won deals
deals = requests.post(f"{HB}/objects/deals/search",
headers={"Authorization": f"Bearer {HS}"},
json={
"filterGroups": [{"filters": [{"propertyName": "dealstage", "operator": "EQ", "value": "closedwon"}]}],
"properties": ["dealname", "amount", "closedate"],
"sorts": [{"propertyName": "closedate", "direction": "DESCENDING"}],
"limit": 15,
},
).json().get("results", [])
# 2. Collect winning email copy
winning_emails = []
for deal in deals:
assocs = requests.get(f"{HB}/objects/deals/{deal['id']}/associations/emails",
headers={"Authorization": f"Bearer {HS}"}).json().get("results", [])
for a in assocs[:5]:
email = requests.get(f"{HB}/objects/emails/{a['id']}",
headers={"Authorization": f"Bearer {HS}"},
params={"properties": "hs_email_subject,hs_email_text,hs_email_direction"},
).json().get("properties", {})
if email.get("hs_email_subject") and email.get("hs_email_direction") == "EMAIL":
winning_emails.append(f"Subject: {email['hs_email_subject']}\n{email.get('hs_email_text','')[:800]}")
# 3. Create Brand Voice
samples = "\n\n---\n\n".join(winning_emails[:10])
bv = requests.post(f"{MB}/brand-voices",
headers={"Authorization": f"Bearer {MV}", "Content-Type": "application/json"},
json={"name": "HubSpot Won Email Voice", "samples": [samples]},
).json()
print(f"Brand Voice: {bv['id']}")
# 4. Generate variations
gen = requests.post(f"{MB}/generations",
headers={"Authorization": f"Bearer {MV}", "Content-Type": "application/json"},
json={
"brand_voice_id": bv["id"],
"prompt": "Write 3 sales email variants for a mid-market SaaS prospect. Subject line + 150-word body each. Match the winning tone.",
"count": 3,
},
).json()
for i, g in enumerate(gen.get("results", [gen]), 1):
print(f"\nVariant {i}:\n{g.get('content', g.get('text',''))[:300]}")
const HS = process.env.HUBSPOT_ACCESS_TOKEN;
const MV = process.env.MAVERA_API_KEY;
const HB = "https://api.hubapi.com/crm/v3";
const MB = "https://app.mavera.io/api/v1";
const h = (tok) => ({ Authorization: `Bearer ${tok}`, "Content-Type": "application/json" });
// 1. Closed Won deals
const deals = await fetch(`${HB}/objects/deals/search`, {
method: "POST", headers: h(HS),
body: JSON.stringify({
filterGroups: [{ filters: [{ propertyName: "dealstage", operator: "EQ", value: "closedwon" }] }],
properties: ["dealname", "amount", "closedate"],
sorts: [{ propertyName: "closedate", direction: "DESCENDING" }],
limit: 15,
}),
}).then(r => r.json()).then(d => d.results || []);
// 2. Collect winning emails
const winningEmails = [];
for (const deal of deals) {
const assocs = await fetch(`${HB}/objects/deals/${deal.id}/associations/emails`,
{ headers: h(HS) }).then(r => r.json()).then(d => d.results || []);
for (const a of assocs.slice(0, 5)) {
const email = await fetch(`${HB}/objects/emails/${a.id}?properties=hs_email_subject,hs_email_text,hs_email_direction`,
{ headers: h(HS) }).then(r => r.json()).then(d => d.properties || {});
if (email.hs_email_subject && email.hs_email_direction === "EMAIL")
winningEmails.push(`Subject: ${email.hs_email_subject}\n${(email.hs_email_text || "").slice(0, 800)}`);
}
}
// 3. Brand Voice
const bv = await fetch(`${MB}/brand-voices`, {
method: "POST", headers: h(MV),
body: JSON.stringify({ name: "HubSpot Won Email Voice", samples: [winningEmails.slice(0, 10).join("\n\n---\n\n")] }),
}).then(r => r.json());
console.log(`Brand Voice: ${bv.id}`);
// 4. Generate
const gen = await fetch(`${MB}/generations`, {
method: "POST", headers: h(MV),
body: JSON.stringify({
brand_voice_id: bv.id,
prompt: "Write 3 sales email variants for a mid-market SaaS prospect. Subject + 150-word body. Match the winning tone.",
count: 3,
}),
}).then(r => r.json());
(gen.results || [gen]).forEach((g, i) =>
console.log(`\nVariant ${i+1}:\n${(g.content || g.text || "").slice(0, 300)}`)
);
Example Output
Brand Voice: bv_won_email_9f3a
Variant 1: Subject: Quick question about your Q3 pipeline
Hey [Name], I noticed [Company] is scaling — congrats. We helped [Similar Co]
cut pipeline review time by 40%. Worth a 15-min call?
Variant 2: Subject: [Company] + [Product] — a thought
Your growth caught my eye. We work with companies at your stage to solve
[specific pain]. I put together a quick brief — want it?
Error Handling
No email associations
No email associations
Not all deals have associated emails. Confirm your team logs emails to deals in HubSpot Settings.
Brand Voice sample size
Brand Voice sample size
Under ~200 words total produces a generic voice. Aim for 5–10 emails, 100+ words each.