Generic sales emails have notoriously low response rates. Personalized video consistently outperforms text — but recording a custom video for each prospect doesn’t scale past a handful per day.
CRM/prospect data → Personalized prompt per contact → Batch generate → Deliver via email/LinkedIn
You define a prompt template with variables (name, company, pain point). For each prospect, the template fills in their details and Video Agent generates a personalized video.
Export from your CRM or create a structured list. Each entry needs enough context to personalize the video.
prospects = [ { "name": "Sarah Chen", "company": "Acme Corp", "role": "VP of Marketing", "pain_point": "spending 40+ hours/month on video content creation", "value_prop": "cut video production time by 90% with AI-generated content", }, { "name": "Marcus Johnson", "company": "TechStart Inc", "role": "Head of Sales", "pain_point": "low response rates on cold outreach", "value_prop": "personalized video messages that get 10x more replies", }, # ...]
2
Build a prompt template
The template should produce a video that feels personally recorded — not like a mail merge.
def build_outreach_prompt(prospect): return f"""Create a 30-second personalized sales video.The presenter should speak directly to the viewer as if recordinga quick personal message. Warm, genuine, not salesy.Script direction:- Open: "Hi {prospect['name']}, I was looking at what {prospect['company']} is doing and had a quick thought for you."- Problem: Briefly mention that many {prospect['role']}s are {prospect['pain_point']}.- Solution: Share that there's a way to {prospect['value_prop']}.- CTA: "Would love to show you how this works — mind if I send over a 5-minute demo? Just reply to this email."Tone: Conversational and genuine. This should feel like a real personwho took 30 seconds to record a message, not a polished ad.Keep it under 35 seconds. Landscape orientation."""
The key to personalized video: The prompt should include specific details about the prospect (company name, role, pain point) but the delivery should feel natural and unrehearsed. Avoid making it sound like a template — that defeats the purpose.
Once all videos are rendered, pair each video URL with the prospect for delivery.
import timefor job in jobs: while True: resp = requests.get( f"https://api.heygen.com/v3/videos/{job['video_id']}", headers={"X-Api-Key": HEYGEN_API_KEY}, ).json()["data"] if resp["status"] == "completed": job["video_url"] = resp["video_url"] job["thumbnail_url"] = resp["thumbnail_url"] print(f"Ready for {job['prospect']['name']}: {resp['video_url']}") break elif resp["status"] == "failed": job["video_url"] = None print(f"Failed for {job['prospect']['name']}") break time.sleep(10)# Now you have a list of prospects with their personalized video URLs# Feed this into your email/LinkedIn outreach tool
When generating videos for many prospects, you want every video to feel on-brand:
Same avatar: Pass a specific avatar_id to ensure the same “salesperson” appears in every video. See Avatars to browse options.
Same voice: Pass a specific voice_id for consistent voice. See Voices.
Same style instructions: Include brand colors, background, and tone in every prompt.
def build_outreach_prompt(prospect): return f"""Create a 30-second personalized sales video.Avatar: Use avatar look_id "josh_lite3_20230714".Background: Clean, modern office with warm lighting.Brand: Professional but approachable. No flashy graphics....rest of the prompt..."""