Skip to main content

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:
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?
Variables in this template:
  • {{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

// 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

// 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

// 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

// 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:
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

// 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

Next Steps