Skip to main content

Overview

Upload once. Use everywhere. Assets are the media files (videos, images, audio) you reuse across multiple video projects. Upload your brand assets, product shots, and B-roll, then reference them in prompts.

Supported Asset Types

Video Files

MP4

Most common format, widely supported

MOV

QuickTime format, high quality

AVI

Windows video format

WebM

Web-optimized video format

Image Files

PNG

Transparent backgrounds supported

JPEG

Compressed photos and graphics

GIF

Animated graphics

WebP

Modern web image format

Audio Files

MP3

Most common audio format

WAV

Uncompressed, high quality

OGG

Open-source audio format

AAC

Advanced audio coding
Maximum file size: 100MB per upload

Uploading Assets

Single File Upload

curl -X POST https://api.babou.ai/api/v1/assets \
  -H "Authorization: Bearer $BABOU_API_KEY" \
  -H "Content-Type: image/png" \
  --data-binary "@./company-logo.png"

Batch Upload

Upload multiple assets at once:
async function uploadAssets(files: { path: string; type: string }[]) {
  const assets = [];

  for (const file of files) {
    try {
      const asset = await uploadAsset(file.path, file.type);
      assets.push(asset);
    } catch (error) {
      console.error(`Failed to upload ${file.path}:`, error);
    }
  }

  return assets;
}

const files = [
  { path: './logo.png', type: 'image/png' },
  { path: './background.jpg', type: 'image/jpeg' },
  { path: './music.mp3', type: 'audio/mpeg' },
  { path: './intro.mp4', type: 'video/mp4' }
];

const uploadedAssets = await uploadAssets(files);
console.log(`✓ Uploaded ${uploadedAssets.length} assets`);

Managing Assets

List All Assets

View all your uploaded assets:
const response = await fetch('https://api.babou.ai/api/v1/assets', {
  headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
});

const { assets, pagination } = await response.json();

console.log(`Total assets: ${pagination.total}`);
assets.forEach(asset => {
  console.log(`- ${asset.content_type}: ${asset.url}`);
});

Filter by Type

Get specific types of assets:
// Get only images
const images = await fetch(
  'https://api.babou.ai/api/v1/assets?contentType=image/png',
  {
    headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
  }
).then(r => r.json());

// Get only videos
const videos = await fetch(
  'https://api.babou.ai/api/v1/assets?contentType=video/mp4',
  {
    headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
  }
).then(r => r.json());

Organize with Naming Conventions

Use descriptive names in your file paths to keep assets organized:
assets/
├── branding/
│   ├── logo-primary.png
│   ├── logo-white.png
│   └── brand-colors.png
├── products/
│   ├── product-hero.jpg
│   ├── product-detail-1.jpg
│   └── product-detail-2.jpg
├── audio/
│   ├── background-music.mp3
│   └── sound-effects.mp3
└── video/
    ├── testimonial-1.mp4
    └── b-roll.mp4

Using Assets in Videos

Reference in Prompts

Reference uploaded assets in your video prompts:
// Upload logo first
const logo = await uploadAsset('./logo.png', 'image/png');

// Reference in prompt
const prompt = `
Create an intro video:
- Use the company logo at this URL: ${logo.url}
- Fade in the logo over 2 seconds
- Add tagline below the logo
- Professional corporate style
`;

await submitPrompt(projectId, chapterId, prompt);

Asset Library Pattern

Create a reusable asset library:
class AssetLibrary {
  private assets: Map<string, string> = new Map();

  async upload(name: string, filePath: string, contentType: string) {
    const asset = await uploadAsset(filePath, contentType);
    this.assets.set(name, asset.url);
    return asset;
  }

  get(name: string): string | undefined {
    return this.assets.get(name);
  }

  buildPrompt(template: string): string {
    let prompt = template;

    for (const [name, url] of this.assets.entries()) {
      prompt = prompt.replace(`{${name}}`, url);
    }

    return prompt;
  }
}

// Usage
const library = new AssetLibrary();

await library.upload('logo', './logo.png', 'image/png');
await library.upload('background', './bg.jpg', 'image/jpeg');

const prompt = library.buildPrompt(`
  Create a video with:
  - Logo: {logo}
  - Background: {background}
  - Smooth animations
`);

await submitPrompt(projectId, chapterId, prompt);

Best Practices

1. Optimize Before Upload

Keep files under 100MB by compressing them before upload:Images:
  • Use tools like TinyPNG, ImageOptim, or Squoosh
  • Convert to WebP for smaller file sizes
  • Resize to appropriate dimensions
Videos:
  • Use HandBrake or FFmpeg to compress
  • Target reasonable bitrates (e.g., 5-10 Mbps for 1080p)
  • Consider lower resolutions if appropriate
Audio:
  • Use 128-320 kbps for MP3
  • Consider mono instead of stereo for voice
  • Trim silence from beginning and end
Choose the right format for your use case:
  • PNG: Logos, graphics with transparency
  • JPEG: Photos, complex images without transparency
  • MP4: General video content
  • MP3: Background music, voiceovers
function validateAsset(filePath: string, maxSize = 100 * 1024 * 1024) {
  const stats = fs.statSync(filePath);

  if (stats.size > maxSize) {
    throw new Error(`File ${filePath} exceeds 100MB limit`);
  }

  const ext = path.extname(filePath).toLowerCase();
  const allowedExts = ['.png', '.jpg', '.jpeg', '.gif', '.mp4', '.mov', '.mp3', '.wav'];

  if (!allowedExts.includes(ext)) {
    throw new Error(`Unsupported file type: ${ext}`);
  }

  return true;
}

2. Track Asset Usage

Keep a record of which assets are used in which projects:
interface AssetUsage {
  assetId: string;
  assetUrl: string;
  projectIds: string[];
  uploadedAt: string;
}

const assetRegistry: Map<string, AssetUsage> = new Map();

function trackAssetUsage(assetId: string, projectId: string) {
  const usage = assetRegistry.get(assetId) || {
    assetId,
    assetUrl: '',
    projectIds: [],
    uploadedAt: new Date().toISOString()
  };

  if (!usage.projectIds.includes(projectId)) {
    usage.projectIds.push(projectId);
  }

  assetRegistry.set(assetId, usage);
}

3. Implement Retry Logic

Handle upload failures gracefully:
async function uploadWithRetry(
  filePath: string,
  contentType: string,
  maxRetries = 3
) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await uploadAsset(filePath, contentType);
    } catch (error: any) {
      if (attempt === maxRetries - 1) throw error;

      console.log(`Upload failed, retrying (${attempt + 1}/${maxRetries})...`);
      await new Promise(r => setTimeout(r, 1000 * (attempt + 1)));
    }
  }
}

4. Clean Up Unused Assets

Periodically review and delete unused assets:
async function findUnusedAssets() {
  const { assets } = await fetch(
    'https://api.babou.ai/api/v1/assets',
    {
      headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
    }
  ).then(r => r.json());

  const oneMonthAgo = new Date();
  oneMonthAgo.setMonth(oneMonthAgo.getMonth() - 1);

  const unused = assets.filter(asset => {
    const createdAt = new Date(asset.created_at);
    const isOld = createdAt < oneMonthAgo;
    const isUnused = !assetRegistry.has(asset.id);

    return isOld && isUnused;
  });

  console.log(`Found ${unused.length} unused assets`);
  return unused;
}

Common Workflows

Workflow 1: Branded Content

// Upload brand assets once
const brandAssets = {
  logo: await uploadAsset('./brand/logo.png', 'image/png'),
  colors: await uploadAsset('./brand/colors.png', 'image/png'),
  font: await uploadAsset('./brand/font-sample.png', 'image/png')
};

// Reuse in multiple videos
function createBrandedPrompt(content: string) {
  return `
${content}

Branding:
- Logo: ${brandAssets.logo.url}
- Use brand colors from: ${brandAssets.colors.url}
- Typography reference: ${brandAssets.font.url}
  `;
}

// Use in different projects
await submitPrompt(
  project1Id,
  chapterId,
  createBrandedPrompt('Create product intro')
);

await submitPrompt(
  project2Id,
  chapterId,
  createBrandedPrompt('Create tutorial video')
);

Workflow 2: Template Library

// Upload template assets
const templates = {
  intro: {
    background: await uploadAsset('./templates/intro-bg.mp4', 'video/mp4'),
    music: await uploadAsset('./templates/intro-music.mp3', 'audio/mpeg')
  },
  outro: {
    background: await uploadAsset('./templates/outro-bg.mp4', 'video/mp4'),
    music: await uploadAsset('./templates/outro-music.mp3', 'audio/mpeg')
  }
};

// Apply templates
function applyTemplate(type: 'intro' | 'outro', customContent: string) {
  const template = templates[type];

  return `
${customContent}

Use these template assets:
- Background: ${template.background.url}
- Music: ${template.music.url}
  `;
}

Troubleshooting

Cause: File exceeds 100MB limitSolution:
  • Compress the file
  • Split into smaller segments
  • Use a lower resolution/quality
Cause: Asset still processing or URL expiredSolution:
  • Check asset status: GET /api/v1/assets/{assetId}
  • Wait for state: "ready"
  • Use fresh URLs (don’t cache old URLs)
Cause: Content-Type header doesn’t match fileSolution:
  • Verify file extension matches content type
  • Use correct MIME types:
    • PNG: image/png
    • JPEG: image/jpeg
    • MP4: video/mp4
    • MP3: audio/mpeg

Next Steps