> ## Documentation Index
> Fetch the complete documentation index at: https://docs.slidevid.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Template Personalization

> Generate personalized videos at scale from templates

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

```javascript theme={null}
// recipients-data.js
export const recipients = [
  {
    name: "Sarah Johnson",
    email: "sarah@techcorp.com",
    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: "mchen@startuphub.com",
    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: "emma@retailpro.com",
    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

```javascript theme={null}
// 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

```javascript theme={null}
// 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

```javascript theme={null}
// 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:

```javascript theme={null}
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

```javascript theme={null}
// 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

<CardGroup cols={2}>
  <Card title="Templates First" icon="layer-group">
    Create and test your template before bulk generation
  </Card>

  <Card title="Data Quality" icon="circle-check">
    Validate recipient data to ensure all variables are present
  </Card>

  <Card title="Use Webhooks" icon="webhook">
    Track completions with webhooks, not polling
  </Card>

  <Card title="Monitor Progress" icon="chart-line">
    Save results to database for tracking and analytics
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={3}>
  <Card title="Bulk Generation" icon="layer-group" href="/guides/bulk-generation">
    Scale to thousands of videos
  </Card>

  <Card title="Personalization Guide" icon="sparkles" href="/guides/personalization">
    Advanced personalization techniques
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    Production-ready patterns
  </Card>
</CardGroup>
