Overview
This example shows how to create a personalized video campaign using templates, perfect for sales outreach, marketing, and customer engagement.Use Case
Scenario: Send personalized product demo videos to 100 potential customers, each featuring their name, company, and specific pain points.Step 1: Create Your Template
First, create a template in the SlideVid dashboard with variables:Copy
Hi {{name}}, I'm reaching out from {{our_company}}.
I noticed {{company}} is working on {{industry}} solutions,
and I thought you'd be interested in how we've helped companies
like yours with {{pain_point}}.
Specifically, {{case_study_company}} saw {{result}} after implementing
our {{product_name}}. I'd love to show you how we can help {{company}}
achieve similar results.
Would you be open to a quick 15-minute demo this week?
{{name}}- Recipient’s first name{{our_company}}- Your company name{{company}}- Recipient’s company{{industry}}- Their industry{{pain_point}}- Specific challenge they face{{case_study_company}}- Similar company name{{result}}- Quantified success metric{{product_name}}- Your product name
Step 2: Prepare Recipient Data
Copy
// recipients-data.js
export const recipients = [
{
name: "Sarah Johnson",
email: "[email protected]",
company: "TechCorp",
industry: "SaaS",
pain_point: "scaling their data infrastructure",
case_study_company: "DataFlow Inc",
result: "a 60% reduction in infrastructure costs",
product_name: "CloudScale Platform"
},
{
name: "Michael Chen",
email: "[email protected]",
company: "StartupHub",
industry: "fintech",
pain_point: "improving security compliance",
case_study_company: "SecureBank",
result: "SOC 2 certification in just 3 months",
product_name: "ComplianceGuard"
},
{
name: "Emma Davis",
email: "[email protected]",
company: "RetailPro",
industry: "e-commerce",
pain_point: "reducing cart abandonment",
case_study_company: "ShopEasy",
result: "a 40% increase in conversion rates",
product_name: "ConversionBoost"
},
// ... 97 more recipients
];
Step 3: Generate Personalized Videos
Copy
// generate-campaign.js
import { SlideVidSDK } from './slidevid-sdk';
import { recipients } from './recipients-data';
const slidevid = new SlideVidSDK(process.env.SLIDEVID_API_KEY);
async function generatePersonalizedCampaign() {
// 1. Get the template
const templates = await slidevid.listTemplates({ type: 'class' });
const template = templates.find(t => t.name === 'Sales Outreach Template');
if (!template) {
throw new Error('Template not found. Create it in the dashboard first.');
}
console.log(`Using template: ${template.name} (${template.id})`);
console.log(`Template has ${template.variables.length} variables`);
// 2. Validate all recipients have required data
const requiredFields = template.variables;
const invalidRecipients = recipients.filter(r =>
requiredFields.some(field => !r[field])
);
if (invalidRecipients.length > 0) {
console.error(`${invalidRecipients.length} recipients have missing data`);
return;
}
// 3. Generate videos for each recipient
const results = [];
for (let i = 0; i < recipients.length; i++) {
const recipient = recipients[i];
try {
console.log(`[${i + 1}/${recipients.length}] Generating video for ${recipient.name}...`);
const projectId = await slidevid.generateFromTemplate(
template.id,
{
name: recipient.name,
our_company: "Your Company",
company: recipient.company,
industry: recipient.industry,
pain_point: recipient.pain_point,
case_study_company: recipient.case_study_company,
result: recipient.result,
product_name: recipient.product_name
},
{
title: `Demo for ${recipient.name} - ${recipient.company}`,
aspectRatio: 'ratio_16_9',
caption: {
enabled: true,
preset: 'wrap1',
alignment: 'bottom'
},
webhook: `https://myapp.com/webhook?recipientId=${recipient.email}`
}
);
results.push({
recipient: recipient.name,
email: recipient.email,
company: recipient.company,
projectId,
status: 'queued'
});
console.log(`✅ Video queued: ${projectId}`);
// Rate limiting: wait 1 second between requests
if (i < recipients.length - 1) {
await new Promise(r => setTimeout(r, 1000));
}
} catch (error) {
console.error(`❌ Failed for ${recipient.name}:`, error.message);
results.push({
recipient: recipient.name,
email: recipient.email,
status: 'failed',
error: error.message
});
}
}
// 4. Summary
const successful = results.filter(r => r.status === 'queued').length;
const failed = results.filter(r => r.status === 'failed').length;
console.log('\n📊 Campaign Summary:');
console.log(`Total: ${results.length}`);
console.log(`Queued: ${successful}`);
console.log(`Failed: ${failed}`);
console.log(`Success Rate: ${Math.round((successful / results.length) * 100)}%`);
// Save results to file
const fs = require('fs');
fs.writeFileSync(
'campaign-results.json',
JSON.stringify(results, null, 2)
);
console.log('\n💾 Results saved to campaign-results.json');
return results;
}
// Run the campaign
generatePersonalizedCampaign()
.then(() => console.log('✨ Campaign generation complete!'))
.catch(error => console.error('Campaign failed:', error));
Step 4: Handle Webhook Notifications
Copy
// webhook-handler.js
import express from 'express';
import { sendEmail } from './email-service';
import { updateDatabase } from './database';
const app = express();
app.use(express.json());
// Track video completions
const campaignVideos = new Map();
app.post('/webhook', async (req, res) => {
const { event, projectId, status, video } = req.body;
const recipientEmail = req.query.recipientId;
console.log(`📨 Webhook: ${event} for ${projectId} (${recipientEmail})`);
if (event === 'project.completed' && status === 'completed') {
try {
// 1. Save video to database
await updateDatabase({
recipientEmail,
projectId,
videoUrl: video.url,
thumbnail: video.thumbnail,
status: 'ready',
completedAt: new Date()
});
// 2. Send personalized email with video
await sendEmail({
to: recipientEmail,
subject: 'Your Personalized Demo is Ready',
html: `
<h2>Hi there!</h2>
<p>I've created a personalized demo just for you.</p>
<p><a href="${video.url}">Watch Your Demo Video</a></p>
<img src="${video.thumbnail}" alt="Video thumbnail" style="max-width: 600px;" />
<p>Looking forward to connecting!</p>
`
});
console.log(`✅ Email sent to ${recipientEmail}`);
// 3. Track for campaign metrics
campaignVideos.set(projectId, {
recipientEmail,
completedAt: new Date(),
videoUrl: video.url
});
} catch (error) {
console.error(`❌ Failed to process completion for ${recipientEmail}:`, error);
}
} else if (event === 'project.failed') {
// Handle failure
console.error(`❌ Video failed for ${recipientEmail}:`, req.body.message);
await updateDatabase({
recipientEmail,
projectId,
status: 'failed',
error: req.body.message
});
}
// Always return 200
res.status(200).send('OK');
});
app.listen(3000, () => {
console.log('Webhook server running on port 3000');
});
Step 5: Track Campaign Performance
Copy
// track-engagement.js
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function getCampaignMetrics(campaignId) {
const videos = await prisma.video.findMany({
where: { campaignId },
include: {
recipient: true,
analytics: true
}
});
const metrics = {
total: videos.length,
completed: videos.filter(v => v.status === 'completed').length,
sent: videos.filter(v => v.emailSentAt).length,
opened: videos.filter(v => v.analytics?.emailOpened).length,
watched: videos.filter(v => v.analytics?.videoWatched).length,
replied: videos.filter(v => v.analytics?.replied).length,
};
metrics.completionRate = (metrics.completed / metrics.total) * 100;
metrics.emailOpenRate = (metrics.opened / metrics.sent) * 100;
metrics.videoWatchRate = (metrics.watched / metrics.opened) * 100;
metrics.replyRate = (metrics.replied / metrics.watched) * 100;
return metrics;
}
// Get metrics
const metrics = await getCampaignMetrics('campaign_123');
console.log('📊 Campaign Performance:');
console.log(`Videos Generated: ${metrics.completed}/${metrics.total}`);
console.log(`Email Open Rate: ${metrics.emailOpenRate.toFixed(1)}%`);
console.log(`Video Watch Rate: ${metrics.videoWatchRate.toFixed(1)}%`);
console.log(`Reply Rate: ${metrics.replyRate.toFixed(1)}%`);
Advanced: Multi-Scene Personalization
For more complex templates with multiple scenes:Copy
async function generateMultiSceneVideo(templateId, recipient) {
// Get template details
const template = await slidevid.getTemplate(templateId);
// Prepare variables for each scene
const scenesData = [
{
// Scene 1: Introduction
script: template.scenes[0].script,
variables: [
{ key: "name", value: recipient.name },
{ key: "company", value: recipient.company }
]
},
{
// Scene 2: Problem
script: template.scenes[1].script,
variables: [
{ key: "pain_point", value: recipient.painPoint },
{ key: "industry", value: recipient.industry }
]
},
{
// Scene 3: Solution
script: template.scenes[2].script,
variables: [
{ key: "product_name", value: "CloudScale" },
{ key: "feature_1", value: recipient.relevantFeature1 },
{ key: "feature_2", value: recipient.relevantFeature2 }
]
},
{
// Scene 4: Social Proof
script: template.scenes[3].script,
variables: [
{ key: "case_study", value: recipient.similarCompany },
{ key: "result", value: recipient.expectedResult }
]
},
{
// Scene 5: Call to Action
script: template.scenes[4].script,
variables: [
{ key: "name", value: recipient.name },
{ key: "meeting_link", value: recipient.customMeetingLink }
]
}
];
// Create video with personalized scenes
return await slidevid.createVideo({
type: 'class',
templateId,
title: `Multi-scene demo for ${recipient.name}`,
scenes: scenesData,
aspectRatio: 'ratio_16_9',
webhook: `https://myapp.com/webhook?email=${recipient.email}`
});
}
Complete Example with Database
Copy
// complete-campaign.js
import { PrismaClient } from '@prisma/client';
import { SlideVidSDK } from './slidevid-sdk';
const prisma = new PrismaClient();
const slidevid = new SlideVidSDK(process.env.SLIDEVID_API_KEY);
async function runPersonalizedCampaign(campaignId) {
// 1. Get campaign and recipients from database
const campaign = await prisma.campaign.findUnique({
where: { id: campaignId },
include: {
template: true,
recipients: {
where: { status: 'pending' }
}
}
});
console.log(`Starting campaign: ${campaign.name}`);
console.log(`Template: ${campaign.template.name}`);
console.log(`Recipients: ${campaign.recipients.length}`);
// 2. Generate videos
for (const recipient of campaign.recipients) {
try {
// Create personalized video
const projectId = await slidevid.generateFromTemplate(
campaign.template.slidevidTemplateId,
{
name: recipient.firstName,
company: recipient.company,
...recipient.customData
},
{
title: `${campaign.name} - ${recipient.firstName}`,
webhook: `${process.env.WEBHOOK_URL}?recipientId=${recipient.id}`
}
);
// Save to database
await prisma.video.create({
data: {
projectId,
recipientId: recipient.id,
campaignId: campaign.id,
status: 'processing'
}
});
// Update recipient status
await prisma.recipient.update({
where: { id: recipient.id },
data: { status: 'video_queued' }
});
console.log(`✅ Video queued for ${recipient.firstName}`);
} catch (error) {
console.error(`❌ Failed for ${recipient.firstName}:`, error);
await prisma.recipient.update({
where: { id: recipient.id },
data: {
status: 'failed',
errorMessage: error.message
}
});
}
// Rate limiting
await new Promise(r => setTimeout(r, 1000));
}
// 3. Update campaign status
await prisma.campaign.update({
where: { id: campaignId },
data: {
status: 'videos_generating',
startedAt: new Date()
}
});
console.log('✨ Campaign generation complete!');
}
// Run campaign
runPersonalizedCampaign('campaign_xyz').catch(console.error);
Key Takeaways
Templates First
Create and test your template before bulk generation
Data Quality
Validate recipient data to ensure all variables are present
Use Webhooks
Track completions with webhooks, not polling
Monitor Progress
Save results to database for tracking and analytics