Working with Templates
Learn how to use Synthesia templates to create personalized videos at scale with consistent branding and design.
📖 Official API Documentation: Synthesia Templates
What are Templates?
Templates are pre-designed video structures with customizable elements (variables) that can be filled with your content. They enable:
- Scalable personalization - Create hundreds of personalized videos
- Consistent branding - Maintain visual consistency across videos
- Efficient workflows - Reduce setup time for similar videos
- Dynamic content - Text, images, avatars, and audio can be customized
Template Basics
Finding Available Templates
import { Synthesia } from '@androettop/synthesia-sdk';
const synthesia = new Synthesia({ apiKey: process.env.SYNTHESIA_API_KEY,});
// Get all templatesconst allTemplates = await synthesia.templates.listTemplates();
// Get only Synthesia public templatesconst publicTemplates = await synthesia.templates.getSynthesiaTemplates();
// Get your workspace templatesconst workspaceTemplates = await synthesia.templates.getWorkspaceTemplates();
console.log(`Found ${allTemplates.data?.count} total templates`);Exploring Template Details
// Get detailed information about a templateconst templateResponse = await synthesia.templates.getTemplate('template-id');
if (templateResponse.data) { const template = templateResponse.data;
console.log('Template:', template.name); console.log('Description:', template.description); console.log('Source:', template.source); // 'synthesia' or 'workspace'
// Examine available variables console.log('\nTemplate Variables:'); template.variables.forEach(variable => { console.log(`- ${variable.name} (${variable.type})`); console.log(` Required: ${variable.required}`); console.log(` Description: ${variable.description || 'N/A'}`); });}Creating Videos from Templates
Basic Template Usage
async function createFromTemplate() { // First, get template details to understand required variables const template = await synthesia.templates.getTemplate('welcome-template');
if (!template.data) { throw new Error('Template not found'); }
// Prepare data for template variables const templateData = { customer_name: 'John Doe', company_name: 'Acme Corporation', welcome_message: 'Welcome to our premium service!', avatar: 'anna_costume1_cameraA' };
// Create video from template const videoResponse = await synthesia.videos.createVideoFromTemplate( 'welcome-template', templateData, { title: `Welcome Video for ${templateData.customer_name}`, test: true, visibility: 'private' } );
if (videoResponse.data) { console.log('Video created:', videoResponse.data.id); return videoResponse.data.id; }}Advanced Template Usage with Validation
async function createValidatedTemplateVideo(templateId: string, userData: any) { // Get template structure const templateResponse = await synthesia.templates.getTemplate(templateId);
if (!templateResponse.data) { throw new Error('Template not found'); }
const template = templateResponse.data;
// Validate required variables const missingRequired = template.variables .filter(v => v.required && !(v.name in userData)) .map(v => v.name);
if (missingRequired.length > 0) { throw new Error(`Missing required variables: ${missingRequired.join(', ')}`); }
// Validate variable types for (const variable of template.variables) { const value = userData[variable.name]; if (value !== undefined && value !== null) { if (!validateVariableType(variable, value)) { throw new Error(`Invalid type for variable ${variable.name}`); } } }
// Create video with validated data return synthesia.videos.createVideoFromTemplate(templateId, userData, { title: generateVideoTitle(template.name, userData), test: process.env.NODE_ENV === 'development' });}
function validateVariableType(variable: any, value: any): boolean { switch (variable.type) { case 'text': return typeof value === 'string'; case 'image': case 'video': case 'avatar': return typeof value === 'string'; // Should be URL or asset ID default: return true; }}
function generateVideoTitle(templateName: string, userData: any): string { const customerName = userData.customer_name || userData.name || 'Customer'; return `${templateName} - ${customerName}`;}Template Variable Types
Text Variables
Used for customizable text content:
const templateData = { // Simple text replacement customer_name: 'Sarah Johnson', company_name: 'Tech Solutions Inc.',
// Longer text content welcome_message: ` Thank you for choosing our service. We're excited to help you achieve your goals! `,
// Numbers and formatted text discount_amount: '25%', expiry_date: 'December 31, 2024'};Image Variables
Replace placeholder images with custom content:
const templateData = { // Using URLs company_logo: 'https://your-site.com/logo.png', product_image: 'https://your-site.com/product.jpg',
// Using uploaded asset IDs background_image: 'asset-123', // From uploads API profile_picture: 'asset-456'};Avatar Variables
Customize which avatar appears in the video:
const templateData = { // Professional avatars presenter_avatar: 'anna_costume1_cameraA', support_avatar: 'james_costume1_cameraA',
// Casual avatars welcome_avatar: 'sophia_costume2_cameraA'};Video Variables
Include custom video content:
const templateData = { // Background videos background_video: 'asset-789', // Uploaded video asset
// Product demo clips demo_video: 'https://your-site.com/demo.mp4'};Common Template Patterns
Customer Onboarding
async function createOnboardingVideo(customer: { name: string; email: string; plan: string; companyLogo?: string;}) { const templateData = { customer_name: customer.name, plan_name: customer.plan, support_email: 'support@yourcompany.com', company_logo: customer.companyLogo || 'default-logo-asset-id', avatar: 'anna_costume1_cameraA' };
return synthesia.videos.createVideoFromTemplate( 'customer-onboarding-template', templateData, { title: `Welcome ${customer.name} - Getting Started`, visibility: 'private' } );}
// Usageconst video = await createOnboardingVideo({ name: 'Alice Cooper', email: 'alice@example.com', plan: 'Premium', companyLogo: 'https://example.com/logo.png'});Product Updates
async function createProductUpdateVideo(update: { featureName: string; description: string; releaseDate: string; demoVideo?: string;}) { const templateData = { feature_name: update.featureName, feature_description: update.description, release_date: update.releaseDate, demo_video: update.demoVideo || 'default-demo-asset-id', avatar: 'james_costume1_cameraA' };
return synthesia.videos.createVideoFromTemplate( 'product-update-template', templateData, { title: `New Feature: ${update.featureName}`, visibility: 'public' } );}Educational Content
async function createLessonVideo(lesson: { title: string; content: string; studentName: string; courseProgress: number; materials?: string[];}) { const templateData = { lesson_title: lesson.title, lesson_content: lesson.content, student_name: lesson.studentName, progress_percentage: `${lesson.courseProgress}%`, course_materials: lesson.materials?.join('\n• ') || 'No additional materials', avatar: 'sophia_costume1_cameraA' };
return synthesia.videos.createVideoFromTemplate( 'educational-lesson-template', templateData, { title: `${lesson.title} - ${lesson.studentName}`, visibility: 'private' } );}Batch Video Creation
Sequential Processing
async function createPersonalizedVideos( templateId: string, customers: Array<{ name: string; email: string; plan: string }>) { const videoIds: string[] = [];
for (const customer of customers) { try { const response = await synthesia.videos.createVideoFromTemplate( templateId, { customer_name: customer.name, customer_email: customer.email, plan_name: customer.plan, avatar: 'anna_costume1_cameraA' }, { title: `Personalized Video - ${customer.name}`, visibility: 'private' } );
if (response.data) { videoIds.push(response.data.id); console.log(`✅ Created video for ${customer.name}: ${response.data.id}`); }
// Rate limiting - wait between requests await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) { console.error(`❌ Failed to create video for ${customer.name}:`, error); } }
return videoIds;}Parallel Processing (with Rate Limiting)
import pLimit from 'p-limit';
async function createVideosInParallel( templateId: string, dataList: Array<Record<string, any>>, concurrency = 3) { const limit = pLimit(concurrency); // Limit concurrent requests
const videoPromises = dataList.map((data, index) => limit(async () => { try { const response = await synthesia.videos.createVideoFromTemplate( templateId, data, { title: `Video ${index + 1}`, test: true } );
return response.data?.id; } catch (error) { console.error(`Failed to create video ${index + 1}:`, error); return null; } }) );
const results = await Promise.all(videoPromises); const successfulIds = results.filter(id => id !== null);
console.log(`Created ${successfulIds.length}/${dataList.length} videos`); return successfulIds;}Template Helper Functions
Template Discovery
async function findTemplateByCategory(category: string) { const response = await synthesia.templates.listTemplates();
if (!response.data) return [];
return response.data.templates.filter(template => template.name.toLowerCase().includes(category.toLowerCase()) || template.description?.toLowerCase().includes(category.toLowerCase()) );}
// Usageconst marketingTemplates = await findTemplateByCategory('marketing');const educationTemplates = await findTemplateByCategory('education');const onboardingTemplates = await findTemplateByCategory('onboarding');Variable Analysis
function analyzeTemplateVariables(template: any) { const analysis = { required: template.variables.filter((v: any) => v.required), optional: template.variables.filter((v: any) => !v.required), byType: {} as Record<string, any[]> };
// Group by type template.variables.forEach((variable: any) => { if (!analysis.byType[variable.type]) { analysis.byType[variable.type] = []; } analysis.byType[variable.type].push(variable); });
return analysis;}
// Usageconst template = await synthesia.templates.getTemplate('template-id');if (template.data) { const analysis = analyzeTemplateVariables(template.data); console.log('Required variables:', analysis.required.map(v => v.name)); console.log('Text variables:', analysis.byType.text?.length || 0); console.log('Image variables:', analysis.byType.image?.length || 0);}Data Validation Helper
class TemplateValidator { async validateData(templateId: string, data: Record<string, any>) { const templateResponse = await synthesia.templates.getTemplate(templateId);
if (!templateResponse.data) { throw new Error('Template not found'); }
const template = templateResponse.data; const errors: string[] = [];
// Check required fields template.variables.forEach(variable => { if (variable.required && !(variable.name in data)) { errors.push(`Missing required variable: ${variable.name}`); } });
// Validate types Object.entries(data).forEach(([key, value]) => { const variable = template.variables.find(v => v.name === key); if (variable && !this.isValidType(variable.type, value)) { errors.push(`Invalid type for ${key}: expected ${variable.type}`); } });
return { isValid: errors.length === 0, errors, template }; }
private isValidType(expectedType: string, value: any): boolean { switch (expectedType) { case 'text': return typeof value === 'string'; case 'image': case 'video': case 'avatar': return typeof value === 'string' && value.length > 0; default: return true; } }}
// Usageconst validator = new TemplateValidator();const validation = await validator.validateData('template-id', templateData);
if (!validation.isValid) { console.error('Validation errors:', validation.errors);} else { console.log('Data is valid, creating video...');}Best Practices
1. Template Selection Strategy
async function selectBestTemplate(useCase: string, requirements: string[]) { const templates = await synthesia.templates.listTemplates();
if (!templates.data) return null;
// Score templates based on name/description match const scored = templates.data.templates.map(template => { let score = 0;
// Check name match if (template.name.toLowerCase().includes(useCase.toLowerCase())) { score += 10; }
// Check description match if (template.description?.toLowerCase().includes(useCase.toLowerCase())) { score += 5; }
// Check if template has required variables requirements.forEach(req => { if (template.variables.some(v => v.name.includes(req))) { score += 2; } });
return { template, score }; });
// Return highest scoring template const best = scored.sort((a, b) => b.score - a.score)[0]; return best.score > 0 ? best.template : null;}2. Caching Template Metadata
class TemplateCache { private cache = new Map<string, any>(); private cacheExpiry = 5 * 60 * 1000; // 5 minutes
async getTemplate(templateId: string) { const cached = this.cache.get(templateId);
if (cached && Date.now() - cached.timestamp < this.cacheExpiry) { return cached.template; }
const response = await synthesia.templates.getTemplate(templateId);
if (response.data) { this.cache.set(templateId, { template: response.data, timestamp: Date.now() }); }
return response.data; }
clearCache() { this.cache.clear(); }}3. Environment-Specific Template Usage
const getTemplateConfig = (environment: 'development' | 'staging' | 'production') => { const configs = { development: { test: true, visibility: 'private', avatar: 'anna_costume1_cameraA' // Consistent for testing }, staging: { test: true, visibility: 'private', avatar: 'anna_costume1_cameraA' }, production: { test: false, visibility: 'public', // Use dynamic avatar selection in production } };
return configs[environment];};
// Usageconst config = getTemplateConfig(process.env.NODE_ENV as any);const video = await synthesia.videos.createVideoFromTemplate( templateId, templateData, config);Troubleshooting
Template Not Found
const response = await synthesia.templates.getTemplate('template-id');if (!response.data) { console.error('Template not found. Check template ID.'); // List available templates to find correct ID const available = await synthesia.templates.listTemplates(); console.log('Available templates:', available.data?.templates.map(t => t.id));}Missing Variable Values
// Always validate before creatingif (!templateData.customer_name) { throw new Error('customer_name is required for this template');}Variable Type Mismatch
// Ensure correct typesconst templateData = { customer_name: String(userData.name), // Ensure string avatar: userData.avatar || 'default_avatar', // Provide fallback company_logo: userData.logo_url || null // Handle optional fields};