# Get Asset
Source: https://docs.babou.ai/api-reference/assets/get
GET /api/v1/assets/{assetId}
Retrieve details about a specific asset by its ID
## Overview
Fetch metadata and details for a specific uploaded asset using its unique identifier.
## Request
### Path Parameters
The unique identifier of the asset (UUID format)
### Headers
Bearer token with your API key: `Bearer sk-bab-your-api-key`
## Response
Unique identifier for the asset
Asset filename
S3 URL to access the asset
MIME type of the file
File size in bytes
Current state: `uploading`, `processing`, `ready`, or `error`
Duration in seconds (for video/audio files)
Width in pixels (for image/video files)
Height in pixels (for image/video files)
ISO 8601 timestamp of when the asset was uploaded
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/assets/ast_abc123xyz456789012 \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function getAsset(assetId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/assets/${assetId}`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
if (!response.ok) {
throw new Error(`Failed to fetch asset: ${response.statusText}`);
}
const asset = await response.json();
return asset;
}
const asset = await getAsset('ast_abc123xyz456789012');
console.log('Asset URL:', asset.url);
console.log('Size:', asset.size, 'bytes');
```
```python Python theme={null}
import os
import requests
def get_asset(asset_id):
response = requests.get(
f'https://api.babou.ai/api/v1/assets/{asset_id}',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
if not response.ok:
raise Exception(f'Failed to fetch asset: {response.text}')
return response.json()
asset = get_asset('ast_abc123xyz456789012')
print(f'Asset URL: {asset["url"]}')
print(f'Size: {asset["size"]} bytes')
```
## Response Example
```json theme={null}
{
"id": "ast_abc123xyz456789012",
"name": "uploaded-file",
"url": "s3://babou-assets/assets/ast_abc123xyz456789012-original",
"content_type": "image/png",
"size": 245678,
"state": "ready",
"duration": null,
"width": 1920,
"height": 1080,
"created_at": "2025-12-02T10:00:00Z"
}
```
## Error Responses
Asset not found or you don't have access
```json theme={null}
{
"error": "Asset not found",
"code": "NOT_FOUND"
}
```
Invalid or missing API key
```json theme={null}
{
"error": "Unauthorized - Invalid API key",
"code": "UNAUTHORIZED"
}
```
## Use Cases
After uploading a large file, poll this endpoint to verify the upload completed successfully:
```typescript theme={null}
async function waitForAssetReady(assetId: string, maxAttempts = 10) {
for (let i = 0; i < maxAttempts; i++) {
const asset = await getAsset(assetId);
if (asset.state === 'ready') {
return asset;
}
if (asset.state === 'failed') {
throw new Error('Asset upload failed');
}
await new Promise(r => setTimeout(r, 2000)); // Wait 2s
}
throw new Error('Asset not ready after maximum attempts');
}
```
Before referencing an asset in your video project, verify it exists and is accessible:
```typescript theme={null}
async function validateAsset(assetId: string) {
try {
const asset = await getAsset(assetId);
return asset.state === 'ready';
} catch (error) {
console.error('Asset validation failed:', error);
return false;
}
}
```
Retrieve the public URL to download or display the asset:
```python theme={null}
def get_asset_url(asset_id):
asset = get_asset(asset_id)
return asset['url']
# Use the URL to download
import requests
url = get_asset_url('ast_abc123xyz456789012')
content = requests.get(url).content
```
## Next Steps
Upload a new asset
View all your assets
# List Assets
Source: https://docs.babou.ai/api-reference/assets/list
GET /api/v1/assets
Retrieve a paginated list of all your uploaded assets
## Overview
Get a list of all assets you've uploaded to Babou. Supports pagination and filtering by content type and state.
## Request
### Headers
Bearer token with your API key: `Bearer sk-bab-your-api-key`
### Query Parameters
Number of assets to return per page (max: 100)
Number of assets to skip for pagination
Filter by MIME type (e.g., `image/png`, `video/mp4`)
Filter by state: `uploading`, `ready`, or `failed`
## Response
Array of asset objects
Unique identifier for the asset
Public URL to access the asset
MIME type of the file
File size in bytes
Current state: `uploading`, `ready`, or `failed`
ISO 8601 timestamp
Pagination metadata
Items per page
Items skipped
Total number of assets matching the filters
## Examples
```bash curl theme={null}
# List all assets
curl https://api.babou.ai/api/v1/assets \
-H "Authorization: Bearer $BABOU_API_KEY"
# List with pagination
curl "https://api.babou.ai/api/v1/assets?limit=50&offset=100" \
-H "Authorization: Bearer $BABOU_API_KEY"
# Filter by content type
curl "https://api.babou.ai/api/v1/assets?contentType=video/mp4" \
-H "Authorization: Bearer $BABOU_API_KEY"
# Filter by state
curl "https://api.babou.ai/api/v1/assets?state=ready" \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
interface ListAssetsOptions {
limit?: number;
offset?: number;
contentType?: string;
state?: 'uploading' | 'ready' | 'failed';
}
async function listAssets(options: ListAssetsOptions = {}) {
const params = new URLSearchParams();
if (options.limit) params.append('limit', options.limit.toString());
if (options.offset) params.append('offset', options.offset.toString());
if (options.contentType) params.append('contentType', options.contentType);
if (options.state) params.append('state', options.state);
const response = await fetch(
`https://api.babou.ai/api/v1/assets?${params}`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
// List all assets
const allAssets = await listAssets();
// List only videos
const videos = await listAssets({ contentType: 'video/mp4' });
// Paginate through assets
const page2 = await listAssets({ limit: 20, offset: 20 });
console.log(`Found ${allAssets.pagination.total} total assets`);
console.log(`First asset: ${allAssets.assets[0]?.url}`);
```
```python Python theme={null}
import os
import requests
from typing import Optional
def list_assets(
limit: Optional[int] = None,
offset: Optional[int] = None,
content_type: Optional[str] = None,
state: Optional[str] = None
):
params = {}
if limit: params['limit'] = limit
if offset: params['offset'] = offset
if content_type: params['contentType'] = content_type
if state: params['state'] = state
response = requests.get(
'https://api.babou.ai/api/v1/assets',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'},
params=params
)
return response.json()
# List all assets
all_assets = list_assets()
# List only videos
videos = list_assets(content_type='video/mp4')
# Paginate through assets
page_2 = list_assets(limit=20, offset=20)
print(f'Found {all_assets["pagination"]["total"]} total assets')
print(f'First asset: {all_assets["assets"][0]["url"] if all_assets["assets"] else "None"}')
```
## Response Example
```json theme={null}
{
"assets": [
{
"id": "ast_abc123xyz456789012",
"name": "logo.png",
"url": "s3://babou-assets/assets/ast_abc123xyz456789012-original",
"content_type": "image/png",
"size": 245678,
"state": "ready",
"duration": null,
"width": 1920,
"height": 1080,
"created_at": "2025-12-02T10:00:00Z"
},
{
"id": "ast_def456uvw987654321",
"name": "intro.mp4",
"url": "s3://babou-assets/assets/ast_def456uvw987654321-original",
"content_type": "video/mp4",
"size": 15234567,
"state": "ready",
"duration": 45.2,
"width": 1920,
"height": 1080,
"created_at": "2025-12-02T09:30:00Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 42
}
}
```
## Pagination
To iterate through all assets, use the `offset` parameter:
```typescript TypeScript theme={null}
async function getAllAssets() {
const allAssets = [];
let offset = 0;
const limit = 100; // Max per page
while (true) {
const response = await listAssets({ limit, offset });
allAssets.push(...response.assets);
if (allAssets.length >= response.pagination.total) {
break;
}
offset += limit;
}
return allAssets;
}
const assets = await getAllAssets();
console.log(`Retrieved all ${assets.length} assets`);
```
```python Python theme={null}
def get_all_assets():
all_assets = []
offset = 0
limit = 100 # Max per page
while True:
response = list_assets(limit=limit, offset=offset)
all_assets.extend(response['assets'])
if len(all_assets) >= response['pagination']['total']:
break
offset += limit
return all_assets
assets = get_all_assets()
print(f'Retrieved all {len(assets)} assets')
```
## Filtering Examples
```typescript theme={null}
const images = await listAssets({ contentType: 'image/png' });
// Or filter for any image type in your application
const allImages = (await listAssets()).assets.filter(
asset => asset.content_type.startsWith('image/')
);
```
```python theme={null}
videos = list_assets(content_type='video/mp4')
# Or filter for any video type
all_assets = list_assets()
all_videos = [
asset for asset in all_assets['assets']
if asset['content_type'].startswith('video/')
]
```
```typescript theme={null}
const failedUploads = await listAssets({ state: 'failed' });
if (failedUploads.assets.length > 0) {
console.warn(`Found ${failedUploads.assets.length} failed uploads`);
// Retry or clean up failed uploads
}
```
```python theme={null}
from datetime import datetime, timedelta
assets = list_assets(limit=100)
one_hour_ago = datetime.now() - timedelta(hours=1)
recent = [
asset for asset in assets['assets']
if datetime.fromisoformat(asset['created_at'].replace('Z', '+00:00')) > one_hour_ago
]
print(f'Found {len(recent)} assets uploaded in the last hour')
```
## Next Steps
Upload a new asset
Retrieve specific asset details
Learn how to use assets in your videos
# Upload Asset
Source: https://docs.babou.ai/api-reference/assets/upload
POST /api/v1/assets
Upload a video, image, or audio file to use in your video projects
## Overview
Upload assets (videos, images, audio files) to Babou's cloud storage. Uploaded assets can be referenced in your video projects and used across multiple chapters.
**Maximum file size:** 100MB per upload
## Request
### Headers
Bearer token with your API key: `Bearer sk-bab-your-api-key`
The MIME type of the file being uploaded (e.g., `video/mp4`, `image/png`, `audio/mpeg`)
### Body
Send the raw binary file data as the request body.
The raw file content
### Supported File Types
**Video:**
* `video/mp4`
* `video/quicktime` (.mov)
* `video/x-msvideo` (.avi)
* `video/webm`
**Images:**
* `image/png`
* `image/jpeg`
* `image/gif`
* `image/webp`
* `image/svg+xml`
**Audio:**
* `audio/mpeg` (.mp3)
* `audio/wav`
* `audio/ogg`
* `audio/aac`
## Response
Unique identifier for the uploaded asset
Asset filename
S3 URL to access the asset (null until upload completes)
MIME type of the uploaded file
File size in bytes
Upload state: `uploading`, `processing`, `ready`, or `error`
Duration in seconds (for video/audio files, null for images)
Width in pixels (for image/video files)
Height in pixels (for image/video files)
ISO 8601 timestamp of upload
## Examples
```bash curl theme={null}
# Upload an image
curl -X POST https://api.babou.ai/api/v1/assets \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: image/png" \
--data-binary "@/path/to/image.png"
# Upload a video
curl -X POST https://api.babou.ai/api/v1/assets \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: video/mp4" \
--data-binary "@/path/to/video.mp4"
```
```typescript TypeScript theme={null}
import fs from 'fs';
async function uploadAsset(filePath: string, contentType: string) {
const fileBuffer = fs.readFileSync(filePath);
const response = await fetch('https://api.babou.ai/api/v1/assets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': contentType
},
body: fileBuffer
});
const asset = await response.json();
console.log('Asset uploaded:', asset.url);
return asset;
}
// Upload an image
await uploadAsset('./logo.png', 'image/png');
// Upload a video
await uploadAsset('./intro.mp4', 'video/mp4');
```
```python Python theme={null}
import os
import requests
def upload_asset(file_path, content_type):
with open(file_path, 'rb') as file:
response = requests.post(
'https://api.babou.ai/api/v1/assets',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': content_type
},
data=file
)
asset = response.json()
print(f'Asset uploaded: {asset["url"]}')
return asset
# Upload an image
upload_asset('./logo.png', 'image/png')
# Upload a video
upload_asset('./intro.mp4', 'video/mp4')
```
## Response Example
```json theme={null}
{
"id": "ast_abc123xyz456789012",
"name": "uploaded-file",
"url": "s3://babou-assets/assets/ast_abc123xyz456789012-original",
"content_type": "image/png",
"size": 245678,
"state": "processing",
"duration": null,
"width": null,
"height": null,
"created_at": "2025-12-02T10:00:00Z"
}
```
## Error Responses
File exceeds 100MB limit
```json theme={null}
{
"error": "File size exceeds maximum allowed size of 100MB",
"code": "FILE_TOO_LARGE"
}
```
S3 upload failed
```json theme={null}
{
"error": "Failed to upload file to storage",
"code": "UPLOAD_FAILED"
}
```
Invalid or missing Content-Type header
```json theme={null}
{
"error": "Content-Type header is required",
"code": "VALIDATION_ERROR"
}
```
## Best Practices
Validate that your file is under 100MB before making the request to avoid errors:
```typescript theme={null}
const stats = fs.statSync(filePath);
if (stats.size > 100 * 1024 * 1024) {
throw new Error('File exceeds 100MB limit');
}
```
Save the returned `id` and `url` in your database to reference the asset later in your video projects.
Always set the correct `Content-Type` header matching your file format. This ensures proper processing and display.
Implement retry logic for failed uploads, especially for large files that may fail due to network issues.
```typescript theme={null}
async function uploadWithRetry(filePath: string, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await uploadAsset(filePath, 'video/mp4');
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
}
}
```
## Next Steps
Retrieve asset details by ID
View all your uploaded assets
Learn how to use assets in your videos
# Create Chapter
Source: https://docs.babou.ai/api-reference/chapters/create
POST /api/v1/projects/{projectId}/chapters
Add a new chapter to a video project
## Overview
Creates a new chapter within a project. Chapters are segments of your video that can have individual content and duration.
## Request
### Path Parameters
The project ID
### Headers
Bearer token with your API key
Must be `application/json`
### Body
Chapter name (1-30 characters)
Optional duration in seconds (must be positive integer)
## Response
Unique chapter identifier (format: `cht_[A-Za-z0-9]{21}`)
Parent project ID
Chapter name
Duration in seconds
ISO 8601 timestamp when the chapter was created
## Examples
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Introduction",
"duration": 30
}'
```
```typescript TypeScript theme={null}
async function createChapter(
projectId: string,
name: string,
duration?: number
) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name, duration })
}
);
return await response.json();
}
const chapter = await createChapter('prj_abc123xyz', 'Introduction', 30);
console.log('Created chapter:', chapter.id);
```
```python Python theme={null}
import os
import requests
def create_chapter(project_id, name, duration=None):
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'name': name, 'duration': duration}
)
return response.json()
chapter = create_chapter('prj_abc123xyz', 'Introduction', 30)
print(f'Created chapter: {chapter["id"]}')
```
## Response Example
```json theme={null}
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z"
}
```
## Error Responses
Invalid request parameters
```json theme={null}
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"hint": "Name must be between 1 and 30 characters"
}
```
Project not found
```json theme={null}
{
"error": "Project not found",
"code": "NOT_FOUND"
}
```
## Next Steps
Add video content to this chapter
View all chapters in the project
# Get Chapter
Source: https://docs.babou.ai/api-reference/chapters/get
GET /api/v1/projects/{projectId}/chapters/{chapterId}
Get detailed information about a specific chapter including all prompts
## Overview
Retrieve complete details about a specific chapter, including all associated prompts and content.
## Request
### Path Parameters
The project ID
The chapter ID
### Headers
Bearer token with your API key
## Response
Returns the chapter object with all nested prompts and content.
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters/cht_def456uvw \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function getChapter(projectId: string, chapterId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters/${chapterId}`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
const chapter = await getChapter('prj_abc123xyz', 'cht_def456uvw');
console.log(`Chapter: ${chapter.name}`);
```
```python Python theme={null}
import os
import requests
def get_chapter(project_id, chapter_id):
response = requests.get(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters/{chapter_id}',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
return response.json()
chapter = get_chapter('prj_abc123xyz', 'cht_def456uvw')
print(f'Chapter: {chapter["name"]}')
```
## Response Example
```json theme={null}
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z",
"status": "ready",
"prompt_count": 2,
"prompts": [
{
"id": "int_abc123xyz789",
"content": "Create an engaging introduction about AI video creation",
"result": "Video content created successfully",
"created_at": "2025-12-02T10:05:00Z"
},
{
"id": "int_def456uvw012",
"content": "Add smooth transitions between scenes",
"result": null,
"created_at": "2025-12-02T10:07:00Z"
}
]
}
```
The `result` field in each prompt contains the result message from processing, or `null` if still processing. The chapter-level `status` field indicates the overall chapter state: `ready`, `processing`, or `error`.
## Error Responses
Chapter not found
```json theme={null}
{
"error": "Chapter not found",
"code": "NOT_FOUND"
}
```
## Next Steps
Add video content to this chapter
View all chapters
# List Chapters
Source: https://docs.babou.ai/api-reference/chapters/list
GET /api/v1/projects/{projectId}/chapters
Get all chapters in a project
## Overview
Retrieve all chapters for a specific project.
## Request
### Path Parameters
The project ID
### Headers
Bearer token with your API key
## Response
The project ID
Array of chapter objects
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function listChapters(projectId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
const result = await listChapters('prj_abc123xyz');
console.log(`Project has ${result.chapters.length} chapters`);
```
```python Python theme={null}
import os
import requests
def list_chapters(project_id):
response = requests.get(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
return response.json()
result = list_chapters('prj_abc123xyz')
print(f'Project has {len(result["chapters"])} chapters')
```
## Response Example
```json theme={null}
{
"project_id": "prj_abc123xyz",
"chapters": [
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z",
"status": "ready",
"prompt_count": 2
},
{
"id": "cht_ghi789rst",
"project_id": "prj_abc123xyz",
"name": "Main Content",
"duration": 60,
"created_at": "2025-12-02T10:02:00Z",
"status": "processing",
"prompt_count": 1
}
]
}
```
The `status` field is derived from chapter interactions and can be `ready`, `processing`, or `error`. The `prompt_count` field indicates how many prompts have been submitted to the chapter.
## Next Steps
Get detailed chapter information
Add a new chapter
# Error Reference
Source: https://docs.babou.ai/api-reference/errors
Complete guide to Babou API error codes and how to handle them
## Error Response Format
All error responses from the Babou API follow a consistent JSON structure:
```json theme={null}
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"hint": "Optional guidance on how to fix the error"
}
```
A human-readable description of what went wrong
A machine-readable error code for programmatic handling
Optional additional guidance on resolving the error
## HTTP Status Codes
The Babou API uses standard HTTP status codes:
| Status Code | Meaning |
| ----------- | ------------------------------------------------------------- |
| `200` | Success - Request completed successfully |
| `400` | Bad Request - Invalid parameters or request format |
| `401` | Unauthorized - Invalid or missing API key |
| `404` | Not Found - Resource doesn't exist or you don't have access |
| `409` | Conflict - Resource state conflict (e.g., already processing) |
| `413` | Payload Too Large - File exceeds size limits |
| `429` | Too Many Requests - Rate limit exceeded |
| `500` | Internal Server Error - Something went wrong on our end |
## Common Error Codes
### Authentication Errors
**HTTP Status:** `401`
**Cause:** Invalid or missing API key
**Example:**
```json theme={null}
{
"error": "Unauthorized - Invalid API key",
"code": "UNAUTHORIZED"
}
```
**Solution:**
* Verify your API key is correct
* Ensure you're including the `Authorization` header
* Check that your API key hasn't expired
* Get a new API key from the [dashboard](https://babou.ai)
**HTTP Status:** `401`
**Cause:** API key doesn't match the expected format (`sk-bab-*`)
**Example:**
```json theme={null}
{
"error": "Invalid API key format",
"code": "INVALID_API_KEY_FORMAT"
}
```
**Solution:**
* Ensure your API key starts with `sk-bab-`
* Check for typos or truncation
* Don't add extra characters or whitespace
**HTTP Status:** `401`
**Cause:** The API key has passed its expiration date
**Example:**
```json theme={null}
{
"error": "API key has expired",
"code": "API_KEY_EXPIRED"
}
```
**Solution:**
* Generate a new API key from your dashboard
* Update your application with the new key
* Set up key rotation to prevent future expirations
### Validation Errors
**HTTP Status:** `400`
**Cause:** Request parameters don't meet validation requirements
**Example:**
```json theme={null}
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"hint": "Name must be between 1 and 30 characters"
}
```
**Common Validation Rules:**
* Project name: 1-30 characters
* Project description: max 1000 characters
* Chapter name: 1-30 characters
* Chapter duration: positive integer
* Prompt content: 1-5000 characters
**Solution:**
* Check the `hint` field for specific guidance
* Review the API documentation for parameter requirements
* Validate input on the client side before sending
### Resource Errors
**HTTP Status:** `404`
**Cause:** The requested resource doesn't exist or you don't have access
**Example:**
```json theme={null}
{
"error": "Project not found",
"code": "NOT_FOUND"
}
```
**Solution:**
* Verify the ID is correct
* Check that the resource belongs to your account
* Ensure the resource hasn't been deleted
* Use List endpoints to find valid IDs
**HTTP Status:** `409`
**Cause:** Resource state conflict - operation can't proceed due to current state
**Common Scenarios:**
* Another prompt is already being processed for a chapter
* Export is already in progress for a project
**Example:**
```json theme={null}
{
"error": "A prompt is already being processed for this chapter",
"code": "CONFLICT",
"hint": "Wait for the current prompt to complete or set force: true to override"
}
```
**Solution:**
* Wait for the current operation to complete
* Check operation status before retrying
* Use `force: true` parameter if applicable (use cautiously)
### File Upload Errors
**HTTP Status:** `413`
**Cause:** Uploaded file exceeds the 100MB size limit
**Example:**
```json theme={null}
{
"error": "File size exceeds maximum allowed size of 100MB",
"code": "FILE_TOO_LARGE"
}
```
**Solution:**
* Compress the file before uploading
* Split large videos into smaller segments
* Check file size before upload:
```typescript theme={null}
const stats = fs.statSync(filePath);
if (stats.size > 100 * 1024 * 1024) {
throw new Error('File too large');
}
```
**HTTP Status:** `500`
**Cause:** Failed to upload file to cloud storage
**Example:**
```json theme={null}
{
"error": "Failed to upload file to storage",
"code": "UPLOAD_FAILED"
}
```
**Solution:**
* Retry the upload
* Check your network connection
* Verify the file isn't corrupted
* Contact support if the issue persists
### Rate Limiting
**HTTP Status:** `429`
**Cause:** Too many requests in a short time period
**Example:**
```json theme={null}
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"hint": "Retry after 60 seconds"
}
```
**Solution:**
* Implement exponential backoff
* Space out your requests
* Cache responses when possible
* Contact support for higher rate limits
**Retry Strategy:**
```typescript theme={null}
async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
// Exponential backoff
const delay = Math.pow(2, i) * 1000;
await new Promise(r => setTimeout(r, delay));
}
throw new Error('Max retries exceeded');
}
```
### Server Errors
**HTTP Status:** `500`
**Cause:** An unexpected error occurred on the server
**Example:**
```json theme={null}
{
"error": "An internal error occurred",
"code": "INTERNAL_ERROR"
}
```
**Solution:**
* Retry the request after a short delay
* Check the [status page](https://status.babou.ai) for known issues
* Contact support if the problem persists
* Include the request ID if available for faster debugging
## Error Handling Best Practices
### 1. Always Check Response Status
```typescript TypeScript theme={null}
async function makeApiCall(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new Error(`API Error (${response.status}): ${error.error} [${error.code}]`);
}
return await response.json();
}
```
```python Python theme={null}
def make_api_call(url, **kwargs):
response = requests.request(url=url, **kwargs)
if not response.ok:
error = response.json()
raise Exception(f'API Error ({response.status_code}): {error["error"]} [{error["code"]}]')
return response.json()
```
### 2. Implement Retry Logic
```typescript theme={null}
async function retryWithBackoff(
fn: () => Promise,
maxRetries = 3,
baseDelay = 1000
): Promise {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
const isLastAttempt = attempt === maxRetries - 1;
// Don't retry on client errors (4xx except 429)
if (error.message.includes('(4') && !error.message.includes('(429')) {
throw error;
}
if (isLastAttempt) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Retry ${attempt + 1}/${maxRetries} after ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('Max retries exceeded');
}
```
### 3. Handle Specific Error Codes
```typescript theme={null}
async function submitPromptSafe(projectId: string, chapterId: string, content: string) {
try {
return await submitPrompt(projectId, chapterId, content);
} catch (error: any) {
if (error.message.includes('[CONFLICT]')) {
// Wait and retry for conflicts
console.log('Waiting for current operation to complete...');
await new Promise(r => setTimeout(r, 10000));
return await submitPrompt(projectId, chapterId, content);
}
if (error.message.includes('[UNAUTHORIZED]')) {
// Don't retry auth errors
throw new Error('Invalid API key - please check your credentials');
}
if (error.message.includes('[VALIDATION_ERROR]')) {
// Don't retry validation errors
throw new Error(`Invalid input: ${error.message}`);
}
// Retry other errors
return await retryWithBackoff(() => submitPrompt(projectId, chapterId, content));
}
}
```
### 4. Log Errors for Debugging
```python theme={null}
import logging
logger = logging.getLogger(__name__)
def api_call_with_logging(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
logger.error(f'API call failed: {func.__name__}', exc_info=True)
logger.error(f'Args: {args}, Kwargs: {kwargs}')
raise
return wrapper
@api_call_with_logging
def create_project(name, description=None):
# ... API call logic
pass
```
## Need Help?
If you're experiencing errors that aren't covered here:
View system status and known issues
Get help from our support team
# Start Export
Source: https://docs.babou.ai/api-reference/exports/start
POST /api/v1/projects/{projectId}/export
Export a video project to a downloadable video file
## Overview
Starts the export process for a complete video project. All chapters are rendered and combined into a single video file. The export typically takes 2-5 minutes depending on project complexity.
Ensure all chapter prompts have completed processing before exporting. The export will only include completed chapters.
## Request
### Path Parameters
The project ID to export
### Headers
Bearer token with your API key
## Response
Export status: `queued`, `processing`, `completed`, or `failed`
Human-readable status message
Estimated completion time (e.g., "2-5 minutes")
## Examples
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/export \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function startExport(projectId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Export failed: ${error.error}`);
}
const result = await response.json();
console.log(`Export started: ${result.status}`);
console.log(`Estimated time: ${result.estimated_time}`);
return result;
}
await startExport('prj_abc123xyz');
```
```python Python theme={null}
import os
import requests
def start_export(project_id):
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/export',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
if not response.ok:
error = response.json()
raise Exception(f'Export failed: {error["error"]}')
result = response.json()
print(f'Export started: {result["status"]}')
print(f'Estimated time: {result["estimated_time"]}')
return result
start_export('prj_abc123xyz')
```
## Response Example
```json theme={null}
{
"status": "queued",
"message": "Export started",
"estimated_time": "2-5 minutes"
}
```
## Error Responses
Export already in progress for this project
```json theme={null}
{
"error": "An export is already in progress for this project",
"code": "CONFLICT",
"hint": "Wait for the current export to complete or check its status"
}
```
Project not found
```json theme={null}
{
"error": "Project not found",
"code": "NOT_FOUND"
}
```
Project has no completed chapters to export
```json theme={null}
{
"error": "Project has no completed chapters",
"code": "VALIDATION_ERROR",
"hint": "Add chapters and submit prompts before exporting"
}
```
## Best Practices
Check that all chapters have completed processing:
```typescript theme={null}
async function ensureChaptersReady(projectId: string) {
const { chapters } = await listChapters(projectId);
if (chapters.length === 0) {
throw new Error('Project has no chapters');
}
for (const chapter of chapters) {
const fullChapter = await getChapter(projectId, chapter.id);
const latestPrompt = fullChapter.prompts?.[fullChapter.prompts.length - 1];
if (!latestPrompt || latestPrompt.status !== 'completed') {
throw new Error(`Chapter "${chapter.name}" is not ready for export`);
}
}
return true;
}
// Use before exporting
await ensureChaptersReady('prj_abc123xyz');
await startExport('prj_abc123xyz');
```
If an export is already in progress, wait for it to complete:
```typescript theme={null}
async function startExportSafe(projectId: string) {
try {
return await startExport(projectId);
} catch (error: any) {
if (error.message.includes('already in progress')) {
console.log('Export already running, checking status...');
return await getExportStatus(projectId);
}
throw error;
}
}
```
After starting an export, poll the status endpoint:
```python theme={null}
import time
def wait_for_export(project_id, max_wait=600):
"""Wait up to 10 minutes for export to complete"""
start_export(project_id)
start_time = time.time()
while time.time() - start_time < max_wait:
status = get_export_status(project_id)
if status['status'] == 'completed':
print(f'✓ Export complete: {status["download_url"]}')
return status
if status['status'] == 'failed':
raise Exception('Export failed')
print(f'Still processing... ({status["status"]})')
time.sleep(10) # Check every 10 seconds
raise Exception('Export timeout')
```
## Next Steps
Check export progress and get download URL
View chapters included in the export
# Get Export Status
Source: https://docs.babou.ai/api-reference/exports/status
GET /api/v1/projects/{projectId}/export
Check the status of a video export and get the download URL when complete
## Overview
Check the progress of an export job for a project. When the export is complete, this endpoint returns a download URL for the final video.
## Request
### Path Parameters
The project ID
### Headers
Bearer token with your API key
## Response
Current export status: `queued`, `processing`, `completed`, or `failed`
URL to download the completed video (only present when `status` is `completed`, otherwise `null`)
Error message if export failed (only present when `status` is `failed`)
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects/prj_abc123xyz/export \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function getExportStatus(projectId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
// Check status
const status = await getExportStatus('prj_abc123xyz');
console.log(`Export status: ${status.status}`);
if (status.status === 'completed') {
console.log(`Download: ${status.download_url}`);
}
```
```python Python theme={null}
import os
import requests
def get_export_status(project_id):
response = requests.get(
f'https://api.babou.ai/api/v1/projects/{project_id}/export',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
return response.json()
# Check status
status = get_export_status('prj_abc123xyz')
print(f'Export status: {status["status"]}')
if status['status'] == 'completed':
print(f'Download: {status["download_url"]}')
```
## Response Examples
### Export in Progress
```json theme={null}
{
"status": "processing",
"download_url": null
}
```
### Export Completed
```json theme={null}
{
"status": "completed",
"download_url": "https://assets.babou.ai/exports/prj_abc123xyz.mp4"
}
```
### Export Failed
```json theme={null}
{
"status": "failed",
"error": "Failed to render chapter 2"
}
```
## Polling for Completion
To wait for an export to complete, poll this endpoint periodically:
```typescript TypeScript theme={null}
async function waitForExport(
projectId: string,
pollInterval = 5000,
timeout = 600000
) {
const startTime = Date.now();
while (Date.now() - startTime < timeout) {
const status = await getExportStatus(projectId);
if (status.status === 'completed') {
return status;
}
if (status.status === 'failed') {
throw new Error(`Export failed: ${status.error}`);
}
console.log(`Export ${status.status}... (${Math.round((Date.now() - startTime) / 1000)}s elapsed)`);
await new Promise(resolve => setTimeout(resolve, pollInterval));
}
throw new Error('Export timeout after 10 minutes');
}
// Use it
const result = await waitForExport('prj_abc123xyz');
console.log('✓ Video ready!');
console.log(`Download: ${result.download_url}`);
console.log(`Took ${result.duration_seconds} seconds`);
```
```python Python theme={null}
import time
def wait_for_export(project_id, poll_interval=5, timeout=600):
"""Wait for export to complete with timeout"""
start_time = time.time()
while time.time() - start_time < timeout:
status = get_export_status(project_id)
if status['status'] == 'completed':
return status
if status['status'] == 'failed':
raise Exception(f'Export failed: {status.get("error", "Unknown error")}')
elapsed = int(time.time() - start_time)
print(f'Export {status["status"]}... ({elapsed}s elapsed)')
time.sleep(poll_interval)
raise Exception('Export timeout after 10 minutes')
# Use it
result = wait_for_export('prj_abc123xyz')
print('✓ Video ready!')
print(f'Download: {result["download_url"]}')
print(f'Took {result["duration_seconds"]} seconds')
```
## Downloading the Video
Once the export is complete, download the video file:
```bash curl theme={null}
# Get the download URL
DOWNLOAD_URL=$(curl -s https://api.babou.ai/api/v1/projects/prj_abc123xyz/export \
-H "Authorization: Bearer $BABOU_API_KEY" | jq -r '.download_url')
# Download the video
curl -o video.mp4 "$DOWNLOAD_URL"
```
```typescript TypeScript theme={null}
import fs from 'fs';
async function downloadVideo(projectId: string, outputPath: string) {
// Wait for export to complete
const status = await waitForExport(projectId);
// Download the video
const response = await fetch(status.download_url);
const buffer = await response.arrayBuffer();
// Save to file
fs.writeFileSync(outputPath, Buffer.from(buffer));
console.log(`✓ Video saved to ${outputPath}`);
}
await downloadVideo('prj_abc123xyz', './my-video.mp4');
```
```python Python theme={null}
import requests
def download_video(project_id, output_path):
# Wait for export to complete
status = wait_for_export(project_id)
# Download the video
response = requests.get(status['download_url'])
# Save to file
with open(output_path, 'wb') as f:
f.write(response.content)
print(f'✓ Video saved to {output_path}')
download_video('prj_abc123xyz', './my-video.mp4')
```
## Error Responses
No export found for this project
```json theme={null}
{
"error": "No export found for this project",
"code": "NOT_FOUND",
"hint": "Start an export first using POST /api/v1/projects/{projectId}/export"
}
```
## Best Practices
Use increasing intervals when polling to reduce API load:
```typescript theme={null}
async function waitForExportWithBackoff(projectId: string) {
let interval = 2000; // Start at 2 seconds
const maxInterval = 30000; // Cap at 30 seconds
const startTime = Date.now();
while (true) {
const status = await getExportStatus(projectId);
if (status.status === 'completed') return status;
if (status.status === 'failed') throw new Error('Export failed');
await new Promise(r => setTimeout(r, interval));
// Increase interval (exponential backoff)
interval = Math.min(interval * 1.5, maxInterval);
}
}
```
Download URLs may expire after a certain time. Download the video promptly:
```python theme={null}
def download_with_retry(url, output_path, max_retries=3):
for attempt in range(max_retries):
try:
response = requests.get(url, timeout=60)
response.raise_for_status()
with open(output_path, 'wb') as f:
f.write(response.content)
return True
except Exception as e:
if attempt == max_retries - 1:
raise
print(f'Download failed, retrying... ({attempt + 1}/{max_retries})')
time.sleep(2 ** attempt)
```
Keep users informed during the export wait:
```typescript theme={null}
async function exportWithProgress(projectId: string) {
console.log('Starting export...');
await startExport(projectId);
let lastStatus = '';
const result = await waitForExport(projectId, 5000, 600000);
console.log('✓ Export completed!');
console.log(` Duration: ${result.duration_seconds}s`);
console.log(` Download: ${result.download_url}`);
return result;
}
```
## Next Steps
Start a new export
Create another video project
# API Overview
Source: https://docs.babou.ai/api-reference/overview
Introduction to the Babou REST API
The Babou REST API lets you create, manage, and export video projects programmatically. No rendering expertise required - just straightforward REST calls.
## Base URL
All API requests should be made to:
```
https://api.babou.ai/api/v1
```
## Authentication
Every API request must include your API key in the `Authorization` header:
```bash theme={null}
Authorization: Bearer sk-bab-your-api-key-here
```
See the [Authentication guide](/authentication) for detailed information.
## API Structure
The Babou API is organized around creating and managing video projects:
Create a project container for your video
Add one or more chapters to organize your video content
Submit text prompts to create video content for each chapter
Export your complete project as a downloadable video file
## Core Concepts
### Projects
A **project** is the top-level container for a video. It holds metadata like name and description, and contains one or more chapters.
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "My Marketing Video",
"description": "Q4 2025 product launch",
"created_at": "2025-12-02T10:00:00Z"
}
```
### Chapters
A **chapter** is a segment of your video. Each chapter can have its own content, duration, and video prompt. Chapters are rendered sequentially to create the final video.
```json theme={null}
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z"
}
```
### Prompts
A **prompt** is a text description submitted to create video content for a chapter. Babou's AI processes the prompt and creates video content automatically.
```json theme={null}
{
"prompt_id": "int_abc123xyz789",
"status": "processing",
"message": "Prompt processing started",
"estimated_time": "30-90 seconds"
}
```
### Assets
**Assets** are media files (videos, images, audio) you upload to use in your projects. Upload assets separately, then reference them in your prompts.
```json theme={null}
{
"id": "ast_abc123xyz456789012",
"url": "https://babou-assets.s3.amazonaws.com/assets/...",
"content_type": "image/png",
"state": "ready"
}
```
## API Endpoints
### Assets
`POST /api/v1/assets`
`GET /api/v1/assets/{assetId}`
`GET /api/v1/assets`
### Projects
`POST /api/v1/projects`
`GET /api/v1/projects`
`GET /api/v1/projects/{projectId}`
### Chapters
`POST /api/v1/projects/{projectId}/chapters`
`GET /api/v1/projects/{projectId}/chapters`
`GET /api/v1/projects/{projectId}/chapters/{chapterId}`
### Prompts
`POST /api/v1/projects/{projectId}/chapters/{chapterId}/prompt`
### Exports
`POST /api/v1/projects/{projectId}/export`
`GET /api/v1/projects/{projectId}/export`
## Rate Limiting
The Babou API implements rate limiting to ensure fair usage and system stability. Rate limits are applied per API key.
If you exceed rate limits, you'll receive a `429 Too Many Requests` response. Implement exponential backoff in your retry logic.
## Request Format
All POST and PUT requests should send JSON in the request body with the `Content-Type: application/json` header (except for asset uploads, which send binary data).
```bash theme={null}
curl -X POST https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "My Project"}'
```
## Response Format
All API responses are JSON. Successful responses return the relevant data:
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "My Project",
"created_at": "2025-12-02T10:00:00Z"
}
```
Error responses include an `error` message and a `code`:
```json theme={null}
{
"error": "Project not found",
"code": "NOT_FOUND"
}
```
See the [Error Reference](/api-reference/errors) for complete error documentation.
## ID Formats
Babou uses predictable ID prefixes to make debugging easier:
* **Projects**: `prj_[A-Za-z0-9]{21}` (e.g., `prj_abc123xyz`)
* **Chapters**: `cht_[A-Za-z0-9]{21}` (e.g., `cht_def456uvw`)
* **Prompts/Interactions**: `int_[A-Za-z0-9]{21}` (e.g., `int_abc123xyz789`)
* **Exports**: `exp_[A-Za-z0-9]{21}` (e.g., `exp_xyz789abc123`)
* **Assets**: `ast_[A-Za-z0-9]{21}` (e.g., `ast_abc123xyz456`)
## Timestamps
All timestamps are in ISO 8601 format with UTC timezone:
```
2025-12-02T10:00:00Z
```
## Pagination
List endpoints support pagination via `limit` and `offset` parameters:
```bash theme={null}
GET /api/v1/projects?limit=20&offset=40
```
Response includes pagination metadata:
```json theme={null}
{
"projects": [...],
"pagination": {
"limit": 20,
"offset": 40,
"total": 150
}
}
```
## Next Steps
Create your first video in 5 minutes
Learn how to authenticate requests
Understand error codes and responses
Deep dive into creating videos
# Create Project
Source: https://docs.babou.ai/api-reference/projects/create
POST /api/v1/projects
Create a new project
## Overview
Creates a new project. A project is the container that holds chapters, prompts, and exports.
## Request
### Headers
Bearer token with your API key
Must be `application/json`
### Body
Project name (1-30 characters)
Optional project description (max 1000 characters)
Optional project settings (reserved for future use)
## Response
Unique project identifier (format: `prj_[A-Za-z0-9]{21}`)
Project name
Project description
Project settings
ID of the currently active export, if any
ISO 8601 timestamp when the project was created
## Examples
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier"
}'
```
```typescript TypeScript theme={null}
async function createProject(name: string, description?: string) {
const response = await fetch('https://api.babou.ai/api/v1/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name, description })
});
return await response.json();
}
const project = await createProject(
'Pricing Page Refresh',
'Launch campaign for the new Team tier'
);
console.log('Created project:', project.id);
```
```python Python theme={null}
import os
import requests
def create_project(name, description=None):
response = requests.post(
'https://api.babou.ai/api/v1/projects',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'name': name, 'description': description}
)
return response.json()
project = create_project(
'Pricing Page Refresh',
'Launch campaign for the new Team tier'
)
print(f'Created project: {project["id"]}')
```
## Response Example
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier",
"settings": null,
"active_export_id": null,
"created_at": "2025-12-02T10:00:00Z"
}
```
## Error Responses
Invalid request parameters
```json theme={null}
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"hint": "Name must be between 1 and 30 characters"
}
```
Invalid or missing API key
```json theme={null}
{
"error": "Unauthorized - Invalid API key",
"code": "UNAUTHORIZED"
}
```
## Next Steps
Add chapters to your project
View all your projects
# Get Project
Source: https://docs.babou.ai/api-reference/projects/get
GET /api/v1/projects/{projectId}
Retrieve details about a specific project
## Overview
Get detailed information about a specific video project by its ID.
## Request
### Path Parameters
The project ID (e.g., `prj_abc123xyz`)
### Headers
Bearer token with your API key
## Response
Returns the complete project object with all metadata.
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects/prj_abc123xyz \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function getProject(projectId: string) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
const project = await getProject('prj_abc123xyz');
console.log(`Project: ${project.name}`);
```
```python Python theme={null}
import os
import requests
def get_project(project_id):
response = requests.get(
f'https://api.babou.ai/api/v1/projects/{project_id}',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
return response.json()
project = get_project('prj_abc123xyz')
print(f'Project: {project["name"]}')
```
## Response Example
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "Product Launch Video",
"description": "Marketing video for Q4 2025",
"settings": null,
"active_export_id": null,
"created_at": "2025-12-02T10:00:00Z"
}
```
## Error Responses
Project not found or you don't have access
```json theme={null}
{
"error": "Project not found",
"code": "NOT_FOUND"
}
```
## Next Steps
View chapters in this project
Export project to video
# List Projects
Source: https://docs.babou.ai/api-reference/projects/list
GET /api/v1/projects
Retrieve a paginated list of all your video projects
## Overview
Get a list of all video projects for the authenticated user. Supports pagination.
## Request
### Headers
Bearer token with your API key
### Query Parameters
Number of projects to return (max: 100)
Number of projects to skip for pagination
## Response
Array of project objects
Pagination metadata with `limit`, `offset`, and `total`
## Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer $BABOU_API_KEY"
# With pagination
curl "https://api.babou.ai/api/v1/projects?limit=50&offset=100" \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function listProjects(limit = 20, offset = 0) {
const params = new URLSearchParams({
limit: limit.toString(),
offset: offset.toString()
});
const response = await fetch(
`https://api.babou.ai/api/v1/projects?${params}`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
return await response.json();
}
const result = await listProjects();
console.log(`Found ${result.pagination.total} projects`);
result.projects.forEach(p => console.log(`- ${p.name}`));
```
```python Python theme={null}
import os
import requests
def list_projects(limit=20, offset=0):
response = requests.get(
'https://api.babou.ai/api/v1/projects',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'},
params={'limit': limit, 'offset': offset}
)
return response.json()
result = list_projects()
print(f'Found {result["pagination"]["total"]} projects')
for project in result['projects']:
print(f'- {project["name"]}')
```
## Response Example
```json theme={null}
{
"projects": [
{
"id": "prj_abc123xyz",
"name": "Product Launch Video",
"description": "Marketing video for Q4 2025",
"settings": null,
"active_export_id": "exp_xyz789abc",
"created_at": "2025-12-02T10:00:00Z"
},
{
"id": "prj_def456uvw",
"name": "Tutorial Series",
"description": "Educational content",
"settings": null,
"active_export_id": null,
"created_at": "2025-12-01T15:30:00Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 42
}
}
```
## Next Steps
Create a new project
Get project details
# Submit Prompt
Source: https://docs.babou.ai/api-reference/prompts/submit
POST /api/v1/projects/{projectId}/chapters/{chapterId}/prompt
Submit a text prompt to create video content for a chapter
## Overview
Submit a prompt to a chapter, describing what to build. The system processes the prompt and creates the chapter content, typically completing in 30-90 seconds.
If a chapter already has a prompt being processed, this endpoint returns a `409 Conflict` error unless you set `force: true` to override.
## Request
### Path Parameters
The project ID
The chapter ID
### Headers
Bearer token with your API key
Must be `application/json`
### Body
The prompt text describing the video content you want to create (1-5000 characters)
Force processing even if another prompt is already being processed for this chapter
## Response
Unique identifier for the submitted prompt
Processing status: `processing`, `completed`, or `failed`
Human-readable status message
Estimated processing time (e.g., "30-90 seconds")
## Examples
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters/cht_def456uvw/prompt \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "30-second product launch ad for the new Team tier. Lead with the headline from our latest release notes, drop in the new pricing screenshot, and resolve brand colors and type from the catalog."
}'
```
```typescript TypeScript theme={null}
async function submitPrompt(
projectId: string,
chapterId: string,
content: string,
force = false
) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters/${chapterId}/prompt`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ content, force })
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Failed to submit prompt: ${error.error}`);
}
return await response.json();
}
const result = await submitPrompt(
'prj_abc123xyz',
'cht_def456uvw',
'Product launch ad for the new Team tier, pulling the headline from our release notes'
);
console.log(`Prompt submitted: ${result.status}`);
console.log(`Estimated time: ${result.estimated_time}`);
```
```python Python theme={null}
import os
import requests
def submit_prompt(project_id, chapter_id, content, force=False):
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters/{chapter_id}/prompt',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'content': content, 'force': force}
)
if not response.ok:
error = response.json()
raise Exception(f'Failed to submit prompt: {error["error"]}')
return response.json()
result = submit_prompt(
'prj_abc123xyz',
'cht_def456uvw',
'Product launch ad for the new Team tier, pulling the headline from our release notes'
)
print(f'Prompt submitted: {result["status"]}')
print(f'Estimated time: {result["estimated_time"]}')
```
## Response Example
```json theme={null}
{
"prompt_id": "int_abc123xyz789",
"status": "processing",
"message": "Prompt processing started",
"estimated_time": "30-90 seconds"
}
```
## Error Responses
Invalid prompt content
```json theme={null}
{
"error": "Validation failed",
"code": "VALIDATION_ERROR",
"hint": "Content must be between 1 and 5000 characters"
}
```
Another prompt is already being processed for this chapter
```json theme={null}
{
"error": "A prompt is already being processed for this chapter",
"code": "CONFLICT",
"hint": "Wait for the current prompt to complete or set force: true to override"
}
```
Project or chapter not found
```json theme={null}
{
"error": "Chapter not found",
"code": "NOT_FOUND"
}
```
## Best Practices
Be specific about what you want in your video. Include details about:
* Visual style and aesthetics
* Text content and messaging
* Transitions and animations
* Mood and tone
```typescript theme={null}
const prompt = `
30-second product launch ad for the new Team tier.
- Lead with the headline from our latest release notes
- Show the updated pricing screenshot at 5s
- Resolve brand colors and type from the catalog
- Close on a CTA matching the pricing page button
`;
await submitPrompt(projectId, chapterId, prompt);
```
After submitting a prompt, the video content is created asynchronously. Use the [Get Chapter](/api-reference/chapters/get) endpoint to check when processing is complete:
```typescript theme={null}
async function waitForPromptCompletion(
projectId: string,
chapterId: string,
maxAttempts = 20
) {
for (let i = 0; i < maxAttempts; i++) {
const chapter = await getChapter(projectId, chapterId);
const latestPrompt = chapter.prompts[chapter.prompts.length - 1];
if (latestPrompt.status === 'completed') {
return latestPrompt;
}
if (latestPrompt.status === 'failed') {
throw new Error('Prompt processing failed');
}
await new Promise(r => setTimeout(r, 5000)); // Wait 5s
}
throw new Error('Timeout waiting for prompt completion');
}
```
If you get a 409 error, decide whether to:
1. Wait for the current prompt to complete
2. Override with `force: true` (use cautiously)
```typescript theme={null}
async function submitPromptSafe(
projectId: string,
chapterId: string,
content: string
) {
try {
return await submitPrompt(projectId, chapterId, content);
} catch (error: any) {
if (error.message.includes('already being processed')) {
console.log('Waiting for current prompt to complete...');
await waitForPromptCompletion(projectId, chapterId);
// Retry submission
return await submitPrompt(projectId, chapterId, content);
}
throw error;
}
}
```
If you've uploaded assets, reference them in your prompts:
```typescript theme={null}
// Upload asset first
const logo = await uploadAsset('./company-logo.png', 'image/png');
// Reference in prompt
const prompt = `
Create an intro video using the uploaded company logo (${logo.url}).
Animate the logo with a fade-in effect over 3 seconds.
Add the text "Welcome" below the logo.
`;
await submitPrompt(projectId, chapterId, prompt);
```
## Next Steps
Check processing status
Export your completed video
Learn more about creating videos
# Authentication
Source: https://docs.babou.ai/authentication
Learn how to authenticate with the Babou API using API keys
## Overview
Every API request needs an API key in the `Authorization` header. Get your key from the dashboard, add it to requests as a Bearer token, start building.
**Keep your API key secure!** Never expose your API key in client-side code or public repositories.
## Getting Your API Key
1. Sign up or log in at [babou.ai](https://babou.ai)
2. Navigate to your **Dashboard**
3. Go to **Settings** → **API Keys**
4. Click **Create New API Key**
5. Copy your key immediately (it won't be shown again)
API keys follow the format: `sk-bab-[random-string]`
## Making Authenticated Requests
Include your API key in the `Authorization` header of every request:
```bash theme={null}
Authorization: Bearer sk-bab-your-api-key-here
```
### Examples
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer sk-bab-your-api-key-here" \
-H "Content-Type: application/json"
```
```typescript TypeScript theme={null}
const BABOU_API_KEY = process.env.BABOU_API_KEY;
const response = await fetch('https://api.babou.ai/api/v1/projects', {
method: 'GET',
headers: {
'Authorization': `Bearer ${BABOU_API_KEY}`,
'Content-Type': 'application/json'
}
});
const data = await response.json();
console.log(data);
```
```python Python theme={null}
import os
import requests
BABOU_API_KEY = os.environ.get('BABOU_API_KEY')
response = requests.get(
'https://api.babou.ai/api/v1/projects',
headers={
'Authorization': f'Bearer {BABOU_API_KEY}',
'Content-Type': 'application/json'
}
)
data = response.json()
print(data)
```
## API Key Management
### Security Best Practices
Never hardcode API keys in your source code. Use environment variables:
```bash theme={null}
export BABOU_API_KEY=sk-bab-your-api-key-here
```
Or use a `.env` file (and add it to `.gitignore`):
```bash theme={null}
BABOU_API_KEY=sk-bab-your-api-key-here
```
Create new API keys periodically and delete old ones from your dashboard to maintain security.
Create different API keys for development, staging, and production environments to isolate access.
Check your dashboard regularly for unexpected API usage that might indicate a compromised key.
### Key Expiration
API keys can have expiration dates. You'll receive a `401 Unauthorized` error if your key has expired:
```json theme={null}
{
"error": "API key has expired",
"code": "API_KEY_EXPIRED"
}
```
## Authentication Errors
### Common Error Responses
Invalid or missing API key
```json theme={null}
{
"error": "Unauthorized - Invalid API key",
"code": "UNAUTHORIZED"
}
```
API key doesn't match expected format (`sk-bab-*`)
```json theme={null}
{
"error": "Invalid API key format",
"code": "INVALID_API_KEY_FORMAT"
}
```
The API key has expired
```json theme={null}
{
"error": "API key has expired",
"code": "API_KEY_EXPIRED"
}
```
## Testing Your Authentication
Use this simple request to verify your API key is working:
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer sk-bab-your-api-key-here"
```
```typescript TypeScript theme={null}
async function testAuth() {
const response = await fetch('https://api.babou.ai/api/v1/projects', {
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
});
if (response.ok) {
console.log('✓ Authentication successful!');
} else {
console.error('✗ Authentication failed:', await response.json());
}
}
testAuth();
```
```python Python theme={null}
import os
import requests
def test_auth():
response = requests.get(
'https://api.babou.ai/api/v1/projects',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
if response.ok:
print('✓ Authentication successful!')
else:
print('✗ Authentication failed:', response.json())
test_auth()
```
## Next Steps
Create your first video project
Explore all available endpoints
# Asset Management
Source: https://docs.babou.ai/guides/asset-management
Upload, organize, and use media assets in your video projects
## 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
Most common format, widely supported
QuickTime format, high quality
Windows video format
Web-optimized video format
### Image Files
Transparent backgrounds supported
Compressed photos and graphics
Animated graphics
Modern web image format
### Audio Files
Most common audio format
Uncompressed, high quality
Open-source audio format
Advanced audio coding
**Maximum file size:** 100MB per upload
## Uploading Assets
### Single File Upload
```bash curl theme={null}
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"
```
```typescript TypeScript theme={null}
import fs from 'fs';
async function uploadAsset(filePath: string, contentType: string) {
const fileBuffer = fs.readFileSync(filePath);
const response = await fetch('https://api.babou.ai/api/v1/assets', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': contentType
},
body: fileBuffer
});
const asset = await response.json();
console.log(`✓ Uploaded: ${filePath}`);
console.log(` URL: ${asset.url}`);
console.log(` ID: ${asset.id}`);
return asset;
}
// Upload an image
const logo = await uploadAsset('./logo.png', 'image/png');
// Upload a video
const intro = await uploadAsset('./intro.mp4', 'video/mp4');
```
```python Python theme={null}
import os
import requests
def upload_asset(file_path, content_type):
with open(file_path, 'rb') as file:
response = requests.post(
'https://api.babou.ai/api/v1/assets',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': content_type
},
data=file
)
asset = response.json()
print(f'✓ Uploaded: {file_path}')
print(f' URL: {asset["url"]}')
print(f' ID: {asset["id"]}')
return asset
# Upload an image
logo = upload_asset('./logo.png', 'image/png')
# Upload a video
intro = upload_asset('./intro.mp4', 'video/mp4')
```
### Batch Upload
Upload multiple assets at once:
```typescript TypeScript theme={null}
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`);
```
```python Python theme={null}
def upload_assets(files):
assets = []
for file_info in files:
try:
asset = upload_asset(file_info['path'], file_info['type'])
assets.append(asset)
except Exception as e:
print(f'Failed to upload {file_info["path"]}: {e}')
return assets
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'}
]
uploaded_assets = upload_assets(files)
print(f'✓ Uploaded {len(uploaded_assets)} assets')
```
## Managing Assets
### List All Assets
View all your uploaded assets:
```typescript theme={null}
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:
```typescript theme={null}
// 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:
```typescript theme={null}
// 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:
```typescript theme={null}
class AssetLibrary {
private assets: Map = 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
```typescript theme={null}
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:
```typescript theme={null}
interface AssetUsage {
assetId: string;
assetUrl: string;
projectIds: string[];
uploadedAt: string;
}
const assetRegistry: Map = 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:
```typescript theme={null}
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:
```typescript theme={null}
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
```typescript theme={null}
// 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
```typescript theme={null}
// 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 limit
**Solution:**
* Compress the file
* Split into smaller segments
* Use a lower resolution/quality
**Cause:** Asset still processing or URL expired
**Solution:**
* 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 file
**Solution:**
* Verify file extension matches content type
* Use correct MIME types:
* PNG: `image/png`
* JPEG: `image/jpeg`
* MP4: `video/mp4`
* MP3: `audio/mpeg`
## Next Steps
API reference for asset uploads
Use assets in your videos
Handle upload errors
# Error Handling
Source: https://docs.babou.ai/guides/error-handling
Build robust applications with proper error handling strategies
## Overview
Proper error handling is essential for building reliable applications with the Babou API. This guide covers common error scenarios and best practices for handling them gracefully.
## Error Response Structure
All API errors follow a consistent format:
```json theme={null}
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"hint": "Optional guidance on fixing the error"
}
```
Always check the `code` field for programmatic error handling, not the `error` message which may change.
## Basic Error Handling
### Check Response Status
Always verify the response status before processing:
```typescript TypeScript theme={null}
async function apiCall(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new ApiError(
error.error,
error.code,
response.status,
error.hint
);
}
return await response.json();
}
class ApiError extends Error {
constructor(
message: string,
public code: string,
public status: number,
public hint?: string
) {
super(message);
this.name = 'ApiError';
}
}
// Usage
try {
const project = await apiCall('https://api.babou.ai/api/v1/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({ name: 'Test Project' })
});
console.log('✓ Created:', project.id);
} catch (error) {
if (error instanceof ApiError) {
console.error(`Error ${error.status}: ${error.message}`);
console.error(`Code: ${error.code}`);
if (error.hint) console.error(`Hint: ${error.hint}`);
}
}
```
```python Python theme={null}
class ApiError(Exception):
def __init__(self, message, code, status, hint=None):
super().__init__(message)
self.code = code
self.status = status
self.hint = hint
def api_call(url, **kwargs):
response = requests.request(url=url, **kwargs)
if not response.ok:
error = response.json()
raise ApiError(
error['error'],
error['code'],
response.status_code,
error.get('hint')
)
return response.json()
# Usage
try:
project = api_call(
'https://api.babou.ai/api/v1/projects',
method='POST',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'name': 'Test Project'}
)
print(f'✓ Created: {project["id"]}')
except ApiError as e:
print(f'Error {e.status}: {e.message}')
print(f'Code: {e.code}')
if e.hint:
print(f'Hint: {e.hint}')
```
## Handling Specific Errors
### Authentication Errors
**Cause:** Invalid or missing API key
**Strategy:**
```typescript theme={null}
try {
return await apiCall(url, options);
} catch (error) {
if (error instanceof ApiError && error.code === 'UNAUTHORIZED') {
console.error('Authentication failed');
console.error('Check your API key at: https://babou.ai/settings');
// Don't retry - auth errors won't resolve automatically
throw new Error('Invalid API key - please check your credentials');
}
throw error;
}
```
**Prevention:**
* Validate API key format before making requests
* Check API key hasn't expired
* Use environment variables, never hardcode keys
**Cause:** API key has passed expiration date
**Strategy:**
```typescript theme={null}
if (error.code === 'API_KEY_EXPIRED') {
console.error('API key has expired');
console.error('Generate a new key at: https://babou.ai/settings');
// Notify user/admin to update key
await notifyAdmin('API key expired - action required');
throw new Error('API key expired - cannot proceed');
}
```
**Prevention:**
* Set up key rotation schedule
* Monitor key expiration dates
* Use multiple keys for different environments
### Validation Errors
**Cause:** Request parameters don't meet requirements
**Strategy:**
```typescript theme={null}
if (error.code === 'VALIDATION_ERROR') {
console.error('Validation failed:', error.message);
if (error.hint) {
console.error('Hint:', error.hint);
}
// Log details for debugging
logValidationError({
endpoint: url,
data: requestBody,
error: error.message,
hint: error.hint
});
// Don't retry - fix validation issues first
throw new Error(`Validation failed: ${error.hint || error.message}`);
}
```
**Prevention:**
```typescript theme={null}
function validateProject(name: string, description?: string) {
const errors = [];
if (!name || name.length < 1 || name.length > 30) {
errors.push('Name must be 1-30 characters');
}
if (description && description.length > 1000) {
errors.push('Description must be max 1000 characters');
}
if (errors.length > 0) {
throw new Error(`Validation errors:\n${errors.join('\n')}`);
}
return true;
}
// Validate before API call
validateProject(name, description);
await createProject(name, description);
```
### Resource Errors
**Cause:** Resource doesn't exist or you don't have access
**Strategy:**
```typescript theme={null}
if (error.code === 'NOT_FOUND') {
console.warn(`Resource not found: ${resourceId}`);
// Try to find the resource
const resources = await listResources();
const exists = resources.find(r => r.id === resourceId);
if (!exists) {
throw new Error(`Resource ${resourceId} does not exist`);
} else {
throw new Error(`No access to resource ${resourceId}`);
}
}
```
**Prevention:**
```typescript theme={null}
async function safeGetProject(projectId: string) {
try {
return await getProject(projectId);
} catch (error) {
if (error.code === 'NOT_FOUND') {
// Fallback: list and find
const { projects } = await listProjects();
const project = projects.find(p => p.id === projectId);
if (project) return project;
// Suggest alternatives
console.log('Did you mean one of these?');
projects.slice(0, 5).forEach(p => {
console.log(`- ${p.name} (${p.id})`);
});
}
throw error;
}
}
```
**Cause:** Resource state conflict
**Strategy:**
```typescript theme={null}
if (error.code === 'CONFLICT') {
console.warn('Conflict:', error.message);
// Check if we should wait and retry
if (error.message.includes('already processing')) {
console.log('Waiting for current operation to complete...');
await new Promise(r => setTimeout(r, 10000)); // Wait 10s
// Retry once
try {
return await apiCall(url, options);
} catch (retryError) {
// If still failing, give up
throw new Error(`Still conflicting after retry: ${error.message}`);
}
}
throw error;
}
```
**Prevention:**
```typescript theme={null}
async function submitPromptSafe(
projectId: string,
chapterId: string,
content: string
) {
// Check current status first
const chapter = await getChapter(projectId, chapterId);
const latestPrompt = chapter.prompts?.[chapter.prompts.length - 1];
if (latestPrompt && latestPrompt.status === 'processing') {
console.log('Prompt already processing, waiting...');
// Wait for completion
await waitForPromptCompletion(projectId, chapterId);
}
// Now safe to submit
return await submitPrompt(projectId, chapterId, content);
}
```
### Rate Limiting
**Cause:** Too many requests in short period
**Strategy:**
```typescript theme={null}
async function apiCallWithRetry(url: string, options: RequestInit, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await apiCall(url, options);
} catch (error) {
if (error.code === 'RATE_LIMIT_EXCEEDED') {
if (attempt === maxRetries - 1) {
throw new Error('Rate limit exceeded after retries');
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
console.log(`Rate limited, retrying in ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
continue;
}
throw error;
}
}
}
```
**Prevention:**
```typescript theme={null}
class RateLimiter {
private queue: Array<() => Promise> = [];
private processing = false;
private requestsPerSecond = 10;
async add(fn: () => Promise): Promise {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
const result = await fn();
resolve(result);
} catch (error) {
reject(error);
}
});
this.process();
});
}
private async process() {
if (this.processing || this.queue.length === 0) return;
this.processing = true;
while (this.queue.length > 0) {
const fn = this.queue.shift()!;
await fn();
await new Promise(r => setTimeout(r, 1000 / this.requestsPerSecond));
}
this.processing = false;
}
}
// Usage
const limiter = new RateLimiter();
for (const item of items) {
await limiter.add(() => processItem(item));
}
```
## Retry Strategies
### Exponential Backoff
```typescript theme={null}
async function retryWithBackoff(
fn: () => Promise,
options = {
maxRetries: 3,
baseDelay: 1000,
maxDelay: 30000
}
): Promise {
for (let attempt = 0; attempt < options.maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
const isLastAttempt = attempt === options.maxRetries - 1;
// Don't retry on client errors (except rate limiting)
if (error instanceof ApiError) {
if (error.status >= 400 && error.status < 500 && error.status !== 429) {
throw error; // Client errors won't resolve with retry
}
}
if (isLastAttempt) {
throw error;
}
// Calculate delay with exponential backoff
const delay = Math.min(
options.baseDelay * Math.pow(2, attempt),
options.maxDelay
);
console.log(`Retry ${attempt + 1}/${options.maxRetries} after ${delay}ms`);
await new Promise(r => setTimeout(r, delay));
}
}
throw new Error('Max retries exceeded');
}
// Usage
const project = await retryWithBackoff(() =>
createProject('My Project', 'Description')
);
```
### Conditional Retry
Only retry errors that might resolve:
```typescript theme={null}
function shouldRetry(error: ApiError): boolean {
// Don't retry client errors
if (error.status >= 400 && error.status < 500) {
// Except rate limiting
return error.status === 429;
}
// Retry server errors
if (error.status >= 500) {
return true;
}
// Retry specific codes
const retryableCodes = ['INTERNAL_ERROR', 'UPLOAD_FAILED'];
return retryableCodes.includes(error.code);
}
async function retryIfNeeded(fn: () => Promise): Promise {
try {
return await fn();
} catch (error) {
if (error instanceof ApiError && shouldRetry(error)) {
console.log('Retrying after error:', error.message);
return await retryWithBackoff(fn);
}
throw error;
}
}
```
## Error Logging
### Structured Logging
```typescript theme={null}
interface ErrorLog {
timestamp: string;
endpoint: string;
method: string;
status: number;
code: string;
message: string;
hint?: string;
requestData?: any;
}
function logError(error: ApiError, context: {
endpoint: string;
method: string;
requestData?: any;
}) {
const log: ErrorLog = {
timestamp: new Date().toISOString(),
endpoint: context.endpoint,
method: context.method,
status: error.status,
code: error.code,
message: error.message,
hint: error.hint,
requestData: context.requestData
};
console.error(JSON.stringify(log));
// Send to logging service
// await sendToLogService(log);
}
```
### Error Monitoring
```typescript theme={null}
class ErrorMonitor {
private errorCounts: Map = new Map();
private threshold = 5;
track(error: ApiError) {
const key = `${error.code}_${error.status}`;
const count = (this.errorCounts.get(key) || 0) + 1;
this.errorCounts.set(key, count);
if (count >= this.threshold) {
this.alert(error, count);
}
}
private alert(error: ApiError, count: number) {
console.error(`⚠️ Alert: ${error.code} occurred ${count} times`);
// Send alert to monitoring service
// await sendAlert({ error, count });
}
reset() {
this.errorCounts.clear();
}
}
const monitor = new ErrorMonitor();
try {
await apiCall(url, options);
} catch (error) {
if (error instanceof ApiError) {
monitor.track(error);
}
throw error;
}
```
## User-Friendly Error Messages
Convert technical errors to user-friendly messages:
```typescript theme={null}
function getUserMessage(error: ApiError): string {
const messages: Record = {
'UNAUTHORIZED': 'Your session has expired. Please log in again.',
'API_KEY_EXPIRED': 'Your API access has expired. Please contact support.',
'VALIDATION_ERROR': `Please check your input: ${error.hint || error.message}`,
'NOT_FOUND': 'The requested item could not be found.',
'CONFLICT': 'This operation is already in progress. Please wait.',
'FILE_TOO_LARGE': 'The file is too large. Maximum size is 100MB.',
'RATE_LIMIT_EXCEEDED': 'Too many requests. Please try again in a moment.',
'INTERNAL_ERROR': 'Something went wrong. Please try again later.'
};
return messages[error.code] || 'An unexpected error occurred.';
}
// Usage in UI
try {
await uploadAsset(file);
showSuccess('File uploaded successfully!');
} catch (error) {
if (error instanceof ApiError) {
showError(getUserMessage(error));
} else {
showError('An unexpected error occurred.');
}
}
```
## Complete Example
Putting it all together:
```typescript theme={null}
class BabouClient {
private baseUrl = 'https://api.babou.ai/api/v1';
private apiKey: string;
private rateLimiter = new RateLimiter();
private errorMonitor = new ErrorMonitor();
constructor(apiKey: string) {
this.apiKey = apiKey;
}
private async request(
endpoint: string,
options: RequestInit = {}
): Promise {
const url = `${this.baseUrl}${endpoint}`;
return this.rateLimiter.add(() =>
retryWithBackoff(async () => {
try {
const response = await fetch(url, {
...options,
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
const error = await response.json();
const apiError = new ApiError(
error.error,
error.code,
response.status,
error.hint
);
this.errorMonitor.track(apiError);
logError(apiError, {
endpoint,
method: options.method || 'GET',
requestData: options.body
});
throw apiError;
}
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
throw error;
}
throw new Error(`Network error: ${error.message}`);
}
})
);
}
async createProject(name: string, description?: string) {
// Validate first
validateProject(name, description);
return this.request('/projects', {
method: 'POST',
body: JSON.stringify({ name, description })
});
}
// ... other methods
}
// Usage
const client = new BabouClient(process.env.BABOU_API_KEY!);
try {
const project = await client.createProject('My Video');
console.log('✓ Success:', project.id);
} catch (error) {
console.error('✗ Failed:', getUserMessage(error));
}
```
## Next Steps
Complete list of error codes
Implement robust video workflows
Handle asset upload errors
# Video Creation Workflow
Source: https://docs.babou.ai/guides/video-creation-workflow
Complete guide to creating professional videos with Babou
## Overview
From idea to export in six steps. This guide covers the complete video creation process - plan, build, review, ship. Works the same whether you're using the REST API or MCP server.
## The Video Creation Process
Define the structure, duration, and content for your video
Set up a project container to organize your work
Break your video into logical segments
Submit prompts to create video content for each chapter
Iterate on content until you're satisfied
Render the final video for download
## Step 1: Plan Your Video
Before creating anything, plan your video structure:
### Define Your Goals
* **Purpose**: What is this video for? (marketing, tutorial, social media, etc.)
* **Audience**: Who will watch it?
* **Message**: What's the key takeaway?
* **Duration**: How long should it be?
### Create a Storyboard
Break your video into logical segments (chapters):
**Example: Pricing Page Refresh launch ad (30 seconds)**
1. **Hook** (5s) - Headline pulled from the latest release notes
2. **Tier Breakdown** (12s) - Walkthrough of the new pricing screenshot
3. **Why Now** (8s) - The reason this tier matters for product teams
4. **CTA** (5s) - Pricing page button, copy and color matched
### What you bring in
The inputs are real product surfaces, not stock imagery:
* The release note for the tier
* The new pricing page screenshot, or its URL
* Brand assets (logo, type, palette), already in your catalog if you've connected one
* Anything specific the chapter needs (a product screenshot, a Figma frame, a customer logo)
Upload custom assets using the [Assets API](/api-reference/assets/upload). If you've connected your catalog, brand colors and type resolve automatically. no upload needed.
## Step 2: Create a Project
Every video starts with a project:
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Pricing Page Refresh",
"description": "30-second launch ad for the new Team tier"
}'
```
```typescript TypeScript theme={null}
const project = await fetch('https://api.babou.ai/api/v1/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Pricing Page Refresh',
description: '30-second launch ad for the new Team tier'
})
}).then(r => r.json());
console.log('Project ID:', project.id);
```
Use descriptive names and detailed descriptions to keep projects organized, especially when managing multiple videos.
## Step 3: Add Chapters
Create chapters for each segment of your video:
```typescript TypeScript theme={null}
const chapters = [
{ name: 'Hook', duration: 5 },
{ name: 'Tier Breakdown', duration: 12 },
{ name: 'Why Now', duration: 8 },
{ name: 'CTA', duration: 5 }
];
for (const chapter of chapters) {
const result = await fetch(
`https://api.babou.ai/api/v1/projects/${project.id}/chapters`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(chapter)
}
).then(r => r.json());
console.log(`✓ Created: ${result.name} (${result.duration}s)`);
}
```
```python Python theme={null}
chapters = [
{'name': 'Hook', 'duration': 5},
{'name': 'Tier Breakdown', 'duration': 12},
{'name': 'Why Now', 'duration': 8},
{'name': 'CTA', 'duration': 5}
]
for chapter in chapters:
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project["id"]}/chapters',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json=chapter
)
result = response.json()
print(f'✓ Created: {result["name"]} ({result["duration"]}s)')
```
## Step 4: Create Content
Submit prompts to create video content for each chapter:
### Writing Effective Prompts
**Bad:**
```
Create a hook for the launch
```
**Good:**
```
5-second hook for our pricing page launch:
- Lead with the headline from release note v2.4
("Team plan: built for product teams that ship daily")
- Background: subtle motion of the new pricing page
- End on the Team tier card, ready to push into the next chapter
```
```
12-second tier breakdown:
- 0-3s: Pricing page hero shot, panning to the Team column
- 3-7s: Three core inclusions appear one at a time
- 7-10s: Price + billing toggle highlight
- 10-12s: Transition to "Why Now"
```
```
Use the catalog defaults:
- Brand color, type, and motion language already on file
- Pull the accent color from the pricing CTA button so the
ad matches the page it's promoting
```
If you haven't connected a catalog yet, you can pass brand details inline. Once it's connected, this section is one line.
```
Use the new pricing screenshot (uploaded as ast_pricingV2)
and the headline from release note v2.4. Match the style from
babou.ai/pricing.
```
### Submit Prompts
```typescript TypeScript theme={null}
const prompts = {
'Hook': `
5-second hook for the Team tier launch.
- Lead with the headline from release note v2.4
- Subtle motion of the new pricing page in the background
- Resolve brand color and type from the catalog
- End on the Team tier card, ready to push into the breakdown
`,
'Tier Breakdown': `
12-second walkthrough of the new Team tier.
- 0-3s: pan across the new pricing page, focus on the Team column
- 3-7s: three core inclusions appear one at a time
- 7-10s: price plus billing toggle highlight
- 10-12s: transition into "Why Now"
`,
'Why Now': `
8 seconds on why this matters for product teams.
- One-line value prop, pulled from the launch summary
- Light motion graphic from the catalog (Block: stat-callout)
- Confident, on-brand pacing
`
// ...CTA chapter follows the same shape
};
// Get chapters
const chaptersResponse = await fetch(
`https://api.babou.ai/api/v1/projects/${project.id}/chapters`,
{
headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
}
).then(r => r.json());
// Submit prompts
for (const chapter of chaptersResponse.chapters) {
if (prompts[chapter.name]) {
await fetch(
`https://api.babou.ai/api/v1/projects/${project.id}/chapters/${chapter.id}/prompt`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: prompts[chapter.name]
})
}
);
console.log(`✓ Submitted prompt for: ${chapter.name}`);
// Wait 60 seconds for processing
await new Promise(r => setTimeout(r, 60000));
}
}
```
## Step 5: Review & Refine
### Check Chapter Status
Monitor the processing status:
```typescript theme={null}
async function checkChapterStatus(projectId: string, chapterId: string) {
const chapter = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters/${chapterId}`,
{
headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
}
).then(r => r.json());
const latestPrompt = chapter.prompts[chapter.prompts.length - 1];
return {
chapterName: chapter.name,
status: latestPrompt?.status || 'no_content',
promptContent: latestPrompt?.content
};
}
```
### Iterate on Content
If you want to refine a chapter, submit a new prompt:
```typescript theme={null}
// Update a chapter with new prompt
await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters/${chapterId}/prompt`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Updated prompt with faster pacing and brighter colors',
force: true // Override previous prompt
})
}
);
```
## Step 6: Export
Once all chapters are complete, export the final video:
```typescript theme={null}
// Start export
await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
}
);
// Poll for completion
async function waitForExport(projectId: string) {
while (true) {
const status = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
headers: { 'Authorization': `Bearer ${process.env.BABOU_API_KEY}` }
}
).then(r => r.json());
if (status.status === 'completed') {
console.log('✓ Export complete!');
console.log(`Download: ${status.download_url}`);
return status;
}
if (status.status === 'failed') {
throw new Error('Export failed');
}
console.log(`Status: ${status.status}...`);
await new Promise(r => setTimeout(r, 10000)); // Wait 10s
}
}
await waitForExport(projectId);
```
## Best Practices
### 1. Start Simple
Begin with a simple structure and iterate:
```
1. Create project
2. Add 1-2 chapters
3. Test with basic prompts
4. Refine and expand
```
### 2. Use Templates
Create reusable templates for common video types:
```typescript theme={null}
const templates = {
launchAd: ['Hook', 'Tier Breakdown', 'Why Now', 'CTA'],
featureDemo: ['Open', 'Walkthrough', 'Wins', 'CTA'],
socialCut: ['Hook', 'Punchline', 'CTA']
};
function createFromTemplate(template: string[]) {
// Create chapters from template
}
```
### 3. Batch Process
For multiple similar videos, batch process:
```python theme={null}
templates = [
{'name': 'Social Post 1', 'topic': 'Feature A'},
{'name': 'Social Post 2', 'topic': 'Feature B'},
{'name': 'Social Post 3', 'topic': 'Feature C'}
]
for template in templates:
# Create project
# Add chapters
# Submit prompts
# Export
```
### 4. Version Control
Keep track of iterations:
```typescript theme={null}
const projectName = `Product Video v${version}`;
// or
const projectName = `Product Video - ${new Date().toISOString().split('T')[0]}`;
```
## Common Patterns
### Pattern 1: Rapid Prototyping
```typescript theme={null}
// Quick test of video concept
const project = await createProject('Quick Test v1');
const chapter = await addChapter(project.id, 'Test', 15);
await submitPrompt(project.id, chapter.id, 'Simple test content');
```
### Pattern 2: Progressive Enhancement
```typescript theme={null}
// Start basic, add detail
const project = await createProject('Product Video');
// V1: Basic structure
await addChapter(project.id, 'Intro', 10);
await submitPrompt(..., 'Simple intro');
// V2: Add more chapters
await addChapter(project.id, 'Features', 30);
await submitPrompt(..., 'Feature showcase');
// V3: Refine content
await submitPrompt(..., 'Enhanced intro with animations', { force: true });
```
### Pattern 3: Conditional Content
```typescript theme={null}
// Different content based on audience
const audience = 'technical'; // or 'general'
const prompt = audience === 'technical'
? 'Detailed technical architecture overview with code examples'
: 'High-level benefits overview with simple visuals';
await submitPrompt(projectId, chapterId, prompt);
```
## Next Steps
Learn how to upload and use custom assets
Handle errors gracefully in your workflows
Complete API documentation
Use MCP for conversational workflows
# Ship features. Babou turns them into marketing.
Source: https://docs.babou.ai/introduction
The developer and agent surface for Babou. Wire your product, brand, and design system in. Get launch-ready video out.
Babou is a product marketing engine. You drop in product surfaces (deploys, release notes, screenshots, Figma frames, URLs, brand kits), and Babou builds the launch ads, demos, social cuts, and campaign variants your team needs. these docs are how you wire it in.
Two ways to drive Babou:
Programmatic control from your own application code
Agentic control from Claude, Cursor, and other MCP-aware tools
## The product, in three layers
**Engine** runs the pipeline:
Six agents, one pipeline. Each owns a step, from watching your sources to cutting the final variants.
Every source, always in sync. Babou watches your product so you don't have to.
Bring your design system. Babou learns your visual language from your AEPs, brand kits, decks, and videos.
**Creative** gives you reusable building blocks:
Production-ready starting points grouped by campaign type.
Drop-in animated components. Brand-aware, combinable.
Input-specific tools. Drop in a screenshot, a Figma frame, or a URL and get a finished video back.
The **Editor** is the hands-on UI for direct work, alongside the programmatic surface these docs cover.
## What teams build with Babou
The same engine produces every format. Most projects fall into one of these:
Ship a feature, get a launch video. Pulls copy from your release notes and builds the ad automatically.
Walk users through what you built. Captures product UI and narrates the flow.
Vertical, square, widescreen. One render becomes every format your social channels need.
New UI? New flow? Babou rebuilds the walkthrough when your product changes.
Turn changelogs into short tutorials. Scripts the steps, records the screens, renders the update.
One source, many variants. Babou produces the full set from a single asset.
## Why teams use Babou
Ship the story as fast as the feature.
Refresh content without manual updates.
One product update produces campaign variants across every format automatically.
Every output inherits your brand from the catalog. No briefing required.
## Get going
Pick up your API key
Build a launch ad in five minutes
Every endpoint and shape
## Help
[support@babou.ai](mailto:support@babou.ai)
# MCP Examples
Source: https://docs.babou.ai/mcp/examples
Real flows you can drive through the Babou MCP server
These examples follow the same anchor scenario the rest of the docs use: a SaaS team shipping a new pricing tier ("Pricing Page Refresh"). Each one shows a different way agents drive Babou: from a release note, from a Figma frame, and across the full canonical output set in one shot.
## Example 1: Launch ad from a release note
The simplest flow. The agent reads what shipped and builds the matching ad.
```
User: We just shipped the Team tier. Build the launch ad.
Claude: I'll pull the latest release notes and build a 30-second
launch ad. Setting up the project now.
[Claude uses CreateProject]
✓ Created project: "Pricing Page Refresh"
[Claude uses AddChapter]
✓ Added chapter: "Launch Ad" (30s)
[Claude uses AddPrompt]
prompt: "30-second product launch ad for the new Team tier.
Lead with the headline from release note v2.4. Drop in the new
pricing screenshot. Resolve brand colors and type from the catalog.
Close on a CTA matching the pricing page button."
[Waits up to 30 seconds for completion]
✓ Chapter content built
[Claude uses ExportProject]
✓ Export started
The launch ad is ready: https://assets.babou.ai/exports/prj_abc123xyz.mp4
```
## Example 2: Feature demo from a Figma frame
The agent takes a Figma frame as the input surface, not a typed prompt. Useful when the design lands before the engineering does.
```
User: Take this Figma frame for the new Team-plan dashboard
and build a 60-second feature demo.
[Frame: figma.com/file/.../?node-id=12-345]
Claude: Reading the frame and setting up the demo.
[Claude uses CreateProject]
✓ Created project: "Team Plan Dashboard Demo"
[Claude uses AddChapter] x3
✓ Added: "Open" (10s), "Walkthrough" (40s), "CTA" (10s)
[Claude uses AddPrompt for each]
Open:
"10-second open. Pull the headline from the frame.
Animate in the dashboard hero shot. Brand from catalog."
Walkthrough:
"40 seconds walking through the three new dashboard sections
in the Figma frame. One section per beat. Match the type
rhythm in the catalog."
CTA:
"10-second close on the primary CTA from the frame,
matched to the pricing page button color."
✓ All chapters built
[Claude uses ExportProject]
✓ Export started, 2-5 minutes
Demo will land at the export URL when complete.
```
## Example 3: One launch, every format
The full canonical output set, kicked off in one conversation. This is what "every release becomes a campaign" looks like end to end.
```
User: For the Team tier launch, build the full set: launch ad,
feature demo, and three social cuts (16:9, 9:16, 1:1).
Claude: One project per format. Pulling everything from the
release note and the catalog.
[Claude uses CreateProject] x5
✓ "Pricing Page Refresh: Launch Ad"
✓ "Pricing Page Refresh: Feature Demo"
✓ "Pricing Page Refresh: Social 16:9"
✓ "Pricing Page Refresh: Social 9:16"
✓ "Pricing Page Refresh: Social 1:1"
[For each project: AddChapter + AddPrompt]
✓ Launch Ad (30s): building
✓ Feature Demo (60s): building
✓ Social 16:9 (15s): building
✓ Social 9:16 (15s): building
✓ Social 1:1 (15s): building
[Waits, then ExportProject for each]
✓ All five exported.
Drop folder ready. Brand and message land the same in every
format because they all resolve from the same catalog.
```
## Common patterns
### Check existing projects before adding chapters
```
User: Add a "Why Now" chapter to my pricing launch project.
Claude: Let me find it first.
[Uses ListProjects]
I see two pricing-related projects:
1. "Pricing Page Refresh" (created today)
2. "Pricing Page Refresh: Social 9:16" (created today)
Which one?
User: The first.
[Uses AddChapter]
✓ Added "Why Now" (8s)
```
### Iterate without starting over
```
User: The launch ad's hook is too soft. Lead harder on the price drop.
Claude: I'll re-prompt the Hook chapter with force: true so it
cancels the current cut and rebuilds.
[Uses AddPrompt with force: true]
✓ Hook rebuilt: leads on the price, then the headline
```
### Handle a long-running prompt
```
User: Build the feature demo.
[Claude uses AddPrompt]
✓ Submitted. Polled for 30 seconds.
✓ Status: still processing. Tool returned a timeout response
with chapter_id and a hint to poll again.
Claude: The demo is still rendering. I'll check back in a moment.
[Waits, then re-checks chapter status]
✓ Done. Moving on to export.
```
## Troubleshooting
### Export with no chapter content
```
User: Export the launch project.
Claude: The project has chapters but none of them have content yet.
[Uses ListChapters]
You have three chapters: Hook, Tier Breakdown, CTA.
None have completed prompts.
Want me to fill them in based on the release notes?
```
### Status check on a running export
```
User: Is the launch ad export done?
[Claude uses ExportProject, same call returns the current status]
Still exporting. Started 90 seconds ago. Estimate: 1-2 minutes.
[Polls again]
✓ Done. Download link is ready.
```
## Next steps
Drop Babou into your MCP-aware client
Every tool, with parameters and examples
Drive Babou from your own application code
Deep dive on the project / chapter / prompt flow
# MCP Server Overview
Source: https://docs.babou.ai/mcp/overview
The agentic interface to the Babou engine
## What MCP gives you
The Babou MCP server is the agentic interface to the same engine the [REST API](/api-reference/overview) exposes. Same product. Different consumer. Instead of your application code making calls, an AI assistant (Claude, Cursor, anything MCP-aware) makes them on the user's behalf.
**MCP or REST?** Use MCP when an AI assistant is the driver: agents picking templates, assembling chapters, watching for product changes and reacting. Use the REST API for direct programmatic control from your own application.
## What changes with MCP
The agent picks the project shape, opens chapters, fills them with prompts, exports the result. You describe outcomes, not API calls.
Point an agent at a release note, a Figma frame, or a deployed page, and it builds the matching marketing video.
Agents can pick a Template, drop in Blocks, or invoke an App for the input it has, instead of building from scratch every time.
Tools that are slow (prompts, exports) wait for completion or return a clean status the agent can poll.
## Available tools
The Babou MCP server exposes six tools today:
### Projects
Create a new project with a name and description.
List all your projects.
### Chapters
Add a chapter to a project with a name and optional duration.
List all chapters in a project, with their current status.
### Prompts
Submit a prompt to a chapter, describing what to build. Waits up to 30 seconds for completion.
### Export
Export a completed project to a downloadable video file.
## How it lands
Drop the Babou MCP server into your AI assistant's config. See the [setup guide](/mcp/setup).
Connect with your Babou API key or OAuth.
A release note, a Figma frame, a deployed page, a screenshot. The agent reads it.
The agent uses the tools above to assemble the project, fill chapters, and export.
Download the rendered video, or iterate.
## MCP vs REST
| | MCP server | REST API |
| ------------------------ | ------------------------------------------------------- | --------------------------------------------------- |
| **Best for** | Agents driving the work | Application code driving the work |
| **Caller** | Claude, Cursor, MCP-aware tools | Your services |
| **Style** | Outcomes ("build the launch ad from this release note") | Endpoints (`POST /projects`, `POST /chapters`, ...) |
| **Auth** | OAuth or API key | API key |
| **Async** | Tools wait for completion or return pollable status | Manual polling |
| **When to reach for it** | Exploration, iteration, hands-off pipelines | Production integrations, scheduled jobs |
The MCP server is a thin wrapper over the REST API. Anything an agent can do via MCP, your code can do via REST, and vice versa.
## Getting started
Install and configure the MCP server
Every tool, with parameters and examples
Real flows: launch ad, Figma demo, batch variants
Drive Babou from your own application code
## Supported clients
The Babou MCP server works with any MCP-compatible client:
* **Claude Desktop** (Anthropic)
* **Claude for VS Code**
* Cursor
* Any custom MCP client
Claude Desktop is the simplest place to start. Follow the [setup guide](/mcp/setup).
# MCP Server Setup
Source: https://docs.babou.ai/mcp/setup
Install and configure the Babou MCP server for use with AI assistants
## Prerequisites
Before setting up the Babou MCP server, ensure you have:
* A Babou account and API key ([get one here](https://babou.ai))
* An MCP-compatible AI assistant (Claude Desktop recommended)
## Setup with Claude Desktop
The easiest way to use Babou's MCP server is through Claude Desktop.
1. Log in to [babou.ai](https://babou.ai)
2. Go to **Settings** → **API Keys**
3. Click **Create New API Key**
4. Copy the key (starts with `sk-bab-`)
Add the Babou MCP server to your Claude Desktop configuration:
**Location:**
* **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
* **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
**Configuration:**
```json theme={null}
{
"mcpServers": {
"babou": {
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-your-api-key-here"
}
}
}
```
Replace `sk-bab-your-api-key-here` with your actual API key. Keep this file secure!
Close and reopen Claude Desktop to load the new configuration.
In Claude Desktop, ask:
```
Can you check what Babou tools are available?
```
Claude should respond with a list of available tools like `CreateProject`, `AddChapter`, etc.
Try a real flow:
```
Create a project called "Pricing Page Refresh" and build a 30-second
product launch ad for our new Team tier. Lead with the headline from
our latest release notes, drop in the new pricing screenshot, and
resolve brand colors and type from the catalog.
```
Claude will use the Babou MCP tools to create the project, add a chapter, submit the prompt, and return the result.
## Authentication Options
### Option 1: API Key (Recommended)
The simplest method - include your API key directly in the configuration:
```json theme={null}
{
"mcpServers": {
"babou": {
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-your-api-key-here"
}
}
}
```
### Option 2: OAuth (Advanced)
For enhanced security, use OAuth authentication:
1. Contact support to set up OAuth for your account
2. Configure with OAuth credentials:
```json theme={null}
{
"mcpServers": {
"babou": {
"url": "https://api.babou.ai/mcp",
"oauth": {
"clientId": "your-client-id",
"clientSecret": "your-client-secret"
}
}
}
}
```
## Configuration Options
### Basic Configuration
Minimal configuration with just an API key:
```json theme={null}
{
"mcpServers": {
"babou": {
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-your-api-key-here"
}
}
}
```
### Custom Name
Change the server name in Claude:
```json theme={null}
{
"mcpServers": {
"babou-video-creator": { // Custom name
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-your-api-key-here"
}
}
}
```
### Multiple Environments
Set up different keys for development and production:
```json theme={null}
{
"mcpServers": {
"babou-dev": {
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-dev-key-here"
},
"babou-prod": {
"url": "https://api.babou.ai/mcp",
"apiKey": "sk-bab-prod-key-here"
}
}
}
```
## Troubleshooting
**Possible causes:**
* Configuration file has syntax errors (validate JSON)
* Claude Desktop wasn't restarted after config changes
* Wrong file path for the configuration file
* API key is invalid or expired
**Solutions:**
1. Validate your JSON configuration (use a JSON validator)
2. Restart Claude Desktop completely (quit and reopen)
3. Verify the config file location for your OS
4. Generate a new API key and update the config
**Error:** `Unauthorized - Invalid API key`
**Solutions:**
* Check your API key is correct (no typos or extra spaces)
* Ensure the key starts with `sk-bab-`
* Verify the key hasn't expired in your dashboard
* Generate a new API key if needed
**Possible causes:**
* Network connectivity issues
* Large video processing taking time
* Rate limiting
**Solutions:**
* Check your internet connection
* Be patient - video creation takes 30-90 seconds
* Avoid making too many rapid requests
**macOS:**
```bash theme={null}
# Create directory if it doesn't exist
mkdir -p ~/Library/Application\ Support/Claude
# Edit configuration
open -e ~/Library/Application\ Support/Claude/claude_desktop_config.json
```
**Windows:**
```powershell theme={null}
# Create directory if it doesn't exist
New-Item -ItemType Directory -Force -Path "$env:APPDATA\Claude"
# Edit configuration
notepad "$env:APPDATA\Claude\claude_desktop_config.json"
```
## Security Best Practices
Your API key provides full access to your Babou account. Keep it secure!
1. **Never share your API key**
* Don't commit it to version control
* Don't share screenshots with the key visible
* Don't post it in support tickets (we'll never ask for it)
2. **Use separate keys for different purposes**
* Development key for testing
* Production key for real usage
* Team keys for shared access
3. **Rotate keys regularly**
* Create new keys periodically
* Delete old keys after rotation
* Update configurations promptly
4. **Monitor key usage**
* Check your dashboard for unexpected activity
* Review API usage logs
* Set up alerts for unusual patterns
## Testing Your Setup
Once configured, test the MCP server with these commands in Claude:
```
1. "List my Babou video projects"
(Should return your existing projects or an empty list)
2. "Create a test project called 'MCP Test'"
(Should create a new project and return its ID)
3. "What tools do you have for Babou?"
(Should list all 6 available tools)
```
## Next Steps
Learn about all available tools
See complete workflow examples
Understand how MCP works
Use the REST API directly
# AddChapter
Source: https://docs.babou.ai/mcp/tools/add-chapter
Add a chapter to a project via MCP
## Overview
Adds a new chapter to an existing video project. Chapters are segments of your video with individual content and duration.
## Parameters
The project ID (e.g., `prj_abc123xyz`)
Chapter name (1-30 characters)
Optional duration in seconds (must be positive integer)
## Returns
Returns the created chapter with its unique ID.
## Example Usage
### In Claude
```
Add a 30-second intro chapter to project prj_abc123xyz
```
```
Create chapters for my video: Intro (15s), Main Content (60s), Outro (10s)
```
### Direct Tool Call
```json theme={null}
{
"name": "AddChapter",
"arguments": {
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30
}
}
```
### Response
```json theme={null}
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z"
}
```
## Common Use Cases
```
Add a chapter called "Tutorial" to my project
```
```
Create these chapters: Introduction, Features, Pricing, Call to Action
```
```
Add a 45-second chapter called "Product Demo"
```
## Next Steps
After adding chapters:
* Use [AddPrompt](/mcp/tools/add-prompt) to fill chapters with content
* Use [ListChapters](/mcp/tools/list-chapters) to view all chapters in the project
## REST API Equivalent
This tool calls: `POST /api/v1/projects/{projectId}/chapters`
See the [Create Chapter API documentation](/api-reference/chapters/create) for more details.
# AddPrompt
Source: https://docs.babou.ai/mcp/tools/add-prompt
Add chapter content via a prompt
## Overview
Submits a prompt to a chapter, describing what to build. This tool **waits for completion** (unlike the REST API which returns immediately), making it suited for conversational workflows.
This tool polls for up to 30 seconds. If the chapter is still processing after that, you'll get a timeout response and can poll again.
## Parameters
The project ID (e.g., `prj_abc123xyz`)
The chapter name. The chapter is created if it doesn't already exist.
Prompt content describing what to build (1-5000 characters)
If `true`, cancel any in-flight prompt for this chapter and restart (default: `false`)
## Returns
Returns the chapter status after polling for completion.
## Example Usage
### In Claude
```
For the "Pricing Page Refresh" project, add a chapter called "Launch Ad" and build a 30-second product launch ad from our latest release notes and the new pricing screenshot.
```
```
Add a "Feature Demo" chapter that walks through the new Team plan dashboard, using the screenshots in our brand kit.
```
### Direct Tool Call
```json theme={null}
{
"name": "AddPrompt",
"arguments": {
"project_id": "prj_abc123xyz",
"chapter_title": "Launch Ad",
"prompt": "30-second product launch ad for the new Team plan. Pull the headline from our release notes, drop in the updated pricing screenshot, and resolve brand colors and type from the catalog.",
"force": false
}
}
```
### Response (Success)
```json theme={null}
{
"status": "completed",
"message": "Prompt completed in 47 seconds",
"chapter": {
"id": "cht_def456uvw",
"title": "Launch Ad",
"status": "completed"
},
"elapsed_seconds": 47
}
```
### Response (Timeout)
```json theme={null}
{
"status": "processing",
"message": "Prompt is still processing",
"elapsed_seconds": 30,
"chapter_id": "cht_def456uvw",
"chapter_title": "Launch Ad",
"hint": "Check status again in a few moments, or use force: true to restart if stuck"
}
```
## Writing Effective Prompts
```
Build the launch chapter using the headline from our latest release notes
and the new pricing screenshot. Resolve brand from the catalog.
```
```
15-second social cut:
- 0-5s: hero shot of the new dashboard
- 5-10s: the headline from our release notes
- 10-15s: CTA pulled from the pricing page
```
```
Product launch ad. 30 seconds. 16:9. Modern, on-brand,
confident pacing. Match the typography and accent color
in our catalog.
```
```
Use the pricing screenshot I uploaded earlier and the new
"Team plan" headline from the latest release note.
```
## Common Use Cases
```
For the "Feature Demo" chapter, walk through the new dashboard using the latest screenshots
```
```
Update the launch ad to lead with the price drop instead of the new feature
```
```
Build content for all five chapters of the launch project
```
## Behavior Notes
**Auto-creates chapters:** If a chapter with the specified title doesn't exist, it's created automatically.
**Blocking operation:** This tool waits for completion (up to 30 seconds). For longer processing times, use the REST API's non-blocking approach.
**Force parameter:** `force: true` cancels an in-flight prompt and restarts. Use sparingly. it interrupts work in progress.
## REST API Equivalent
This tool calls:
1. `POST /api/v1/projects/{projectId}/chapters` (if the chapter doesn't exist)
2. `POST /api/v1/projects/{projectId}/chapters/{chapterId}/prompt`
3. Polls `GET /api/v1/projects/{projectId}/chapters/{chapterId}` for completion
See the [Submit Prompt API documentation](/api-reference/prompts/submit) for more details.
# CreateProject
Source: https://docs.babou.ai/mcp/tools/create-project
Create a new project via MCP
## Overview
Creates a new project. A project is the container for chapters, prompts, and exports.
## Parameters
Project name (1-30 characters)
Optional project description (max 1000 characters)
## Returns
Returns the created project with its unique ID.
## Example Usage
### In Claude
```
Create a new project called "Pricing Page Refresh" for the launch campaign around our new tier
```
Claude will use the `CreateProject` tool and respond with the project details.
### Direct Tool Call
```json theme={null}
{
"name": "CreateProject",
"arguments": {
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier"
}
}
```
### Response
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier",
"created_at": "2026-05-02T10:00:00Z"
}
```
## Common Use Cases
```
Create a project for next week's pricing page launch
```
```
Create three projects: one for the launch ad, one for the feature demo, one for the social cuts
```
```
Create a project called "Q4 Onboarding Refresh" with description "New onboarding flow for the Team plan"
```
## Next Steps
After creating a project:
1. Use [AddChapter](/mcp/tools/add-chapter) to add chapters
2. Use [AddPrompt](/mcp/tools/add-prompt) to fill chapters with content
3. Use [ExportProject](/mcp/tools/export-project) to render the final video
## REST API Equivalent
This tool calls: `POST /api/v1/projects`
See the [Create Project API documentation](/api-reference/projects/create) for more details.
# ExportProject
Source: https://docs.babou.ai/mcp/tools/export-project
Export a video project to a downloadable file via MCP
## Overview
Exports a complete video project to a downloadable MP4 file. All chapters are rendered and combined into a single video.
**Processing time:** Exports typically take 2-5 minutes depending on project complexity.
## Parameters
The project ID to export (e.g., `prj_abc123xyz`)
## Returns
Returns the export job status. Check back periodically to get the download URL.
## Example Usage
### In Claude
```
Export my Product Demo project
```
```
I'm done with the edits - export the project to video
```
```
Export project prj_abc123xyz and let me know when it's ready
```
### Direct Tool Call
```json theme={null}
{
"name": "ExportProject",
"arguments": {
"project_id": "prj_abc123xyz"
}
}
```
### Response (Started)
```json theme={null}
{
"status": "queued",
"message": "Export started",
"estimated_time": "2-5 minutes"
}
```
### Response (After Completion)
```json theme={null}
{
"status": "completed",
"download_url": "https://assets.babou.ai/exports/prj_abc123xyz.mp4",
"started_at": "2025-12-02T10:05:00Z",
"completed_at": "2025-12-02T10:08:00Z",
"duration_seconds": 180
}
```
## Common Use Cases
```
My video is complete - export it so I can download it
```
```
Is my export ready yet?
```
Claude will check the status and let you know when it's done.
```
Export my project and give me the download link
```
Claude will start the export and provide the URL once ready.
## Before Exporting
Make sure:
* ✓ All chapters have been created
* ✓ All prompts have completed processing
* ✓ You've reviewed the content
Only completed chapters will be included in the export. Chapters without content or with failed prompts will be skipped.
## Workflow Example
### In Claude
```
User: Create a product video with intro, features, and outro chapters
Claude: [Creates project, adds chapters, submits prompts]
User: Export the project
Claude: [Starts export, waits for completion]
Claude: Your video is ready! Download it here: [URL]
```
## Export States
The export can be in one of these states:
| State | Description |
| ------------ | ---------------------------------------------------- |
| `queued` | Export is queued and waiting to start |
| `processing` | Export is actively being rendered |
| `completed` | Export finished successfully, download URL available |
| `failed` | Export failed, error message provided |
## Download the Video
Once the export status is `completed`, you can download the video from the `download_url`:
```
The video is ready at: https://assets.babou.ai/exports/prj_abc123xyz.mp4
```
Download URLs are temporary. Download your video promptly after the export completes.
## Troubleshooting
Complex projects with multiple chapters and effects take longer to render. Wait patiently or check the status after a few minutes.
Common causes:
* No completed chapters in the project
* Corrupted prompts or assets
* Server issues
Try checking chapter status and retry the export.
The download URL is only available when status is `completed`. Keep checking the export status until it finishes.
## REST API Equivalent
This tool calls:
1. `POST /api/v1/projects/{projectId}/export` (start export)
2. `GET /api/v1/projects/{projectId}/export` (check status)
See the [Export API documentation](/api-reference/exports/start) for more details.
# ListChapters
Source: https://docs.babou.ai/mcp/tools/list-chapters
Get all chapters in a project via MCP
## Overview
Retrieves all chapters for a specific video project.
## Parameters
The project ID (e.g., `prj_abc123xyz`)
## Returns
Returns an array of chapters for the specified project.
## Example Usage
### In Claude
```
Show me the chapters in project prj_abc123xyz
```
```
What chapters are in my Product Demo project?
```
### Direct Tool Call
```json theme={null}
{
"name": "ListChapters",
"arguments": {
"project_id": "prj_abc123xyz"
}
}
```
### Response
```json theme={null}
{
"project_id": "prj_abc123xyz",
"chapters": [
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Introduction",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z"
},
{
"id": "cht_ghi789rst",
"project_id": "prj_abc123xyz",
"name": "Main Content",
"duration": 60,
"created_at": "2025-12-02T10:02:00Z"
}
]
}
```
## Common Use Cases
```
What's the structure of my video?
```
```
How many chapters does my project have?
```
```
Does my project have an intro chapter?
```
## REST API Equivalent
This tool calls: `GET /api/v1/projects/{projectId}/chapters`
See the [List Chapters API documentation](/api-reference/chapters/list) for more details.
# ListProjects
Source: https://docs.babou.ai/mcp/tools/list-projects
Get all projects via MCP
## Overview
Retrieves a list of all projects for the authenticated user.
## Parameters
Maximum number of projects to return (default: 20)
Number of projects to skip for pagination (default: 0)
## Returns
Returns an array of your projects with their details.
## Example Usage
### In Claude
```
Show me all my projects
```
```
What projects do I have?
```
```
List my Babou projects
```
### Direct Tool Call
```json theme={null}
{
"name": "ListProjects",
"arguments": {}
}
```
### Response
```json theme={null}
{
"projects": [
{
"id": "prj_abc123xyz",
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier",
"created_at": "2026-05-02T10:00:00Z"
},
{
"id": "prj_def456uvw",
"name": "Onboarding Walkthrough: Team plan",
"description": "Refreshed walkthrough after the dashboard rework",
"created_at": "2026-04-28T15:00:00Z"
}
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 2
}
}
```
## Common Use Cases
```
What projects do I have?
```
```
Do I have a project for the pricing page launch?
```
```
Show me my most recent projects
```
## REST API Equivalent
This tool calls: `GET /api/v1/projects`
See the [List Projects API documentation](/api-reference/projects/list) for more details.
# Quickstart
Source: https://docs.babou.ai/quickstart
Build a launch ad with Babou in under 5 minutes
## Build your first launch ad
Five minutes. Four API calls. One launch-ready video. We'll build a 30-second product launch ad for a new pricing tier.
If you haven't already, [get your API key](/authentication) from the Babou dashboard and set it as an environment variable:
```bash theme={null}
export BABOU_API_KEY=sk-bab-your-api-key-here
```
Every video starts with a project. Create one with a name and description:
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier"
}'
```
```typescript TypeScript theme={null}
const response = await fetch('https://api.babou.ai/api/v1/projects', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Pricing Page Refresh',
description: 'Launch campaign for the new Team tier'
})
});
const project = await response.json();
console.log('Project ID:', project.id);
```
```python Python theme={null}
import os
import requests
response = requests.post(
'https://api.babou.ai/api/v1/projects',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'name': 'Pricing Page Refresh',
'description': 'Launch campaign for the new Team tier'
}
)
project = response.json()
print('Project ID:', project['id'])
```
**Response:**
```json theme={null}
{
"id": "prj_abc123xyz",
"name": "Pricing Page Refresh",
"description": "Launch campaign for the new Team tier",
"created_at": "2025-12-02T10:00:00Z"
}
```
Save the `project.id` - you'll need it for the next steps!
Videos are organized into chapters. Let's add one:
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Launch Ad",
"duration": 30
}'
```
```typescript TypeScript theme={null}
const projectId = 'prj_abc123xyz'; // From previous step
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Launch Ad',
duration: 30
})
}
);
const chapter = await response.json();
console.log('Chapter ID:', chapter.id);
```
```python Python theme={null}
project_id = 'prj_abc123xyz' # From previous step
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'name': 'Launch Ad',
'duration': 30
}
)
chapter = response.json()
print('Chapter ID:', chapter['id'])
```
**Response:**
```json theme={null}
{
"id": "cht_def456uvw",
"project_id": "prj_abc123xyz",
"name": "Launch Ad",
"duration": 30,
"created_at": "2025-12-02T10:01:00Z"
}
```
Now for the magic! Submit a text prompt to create video content:
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/chapters/cht_def456uvw/prompt \
-H "Authorization: Bearer $BABOU_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"content": "30-second product launch ad for the new Team tier. Lead with the headline from our latest release notes, drop in the new pricing screenshot, and resolve brand colors and type from the catalog."
}'
```
```typescript TypeScript theme={null}
const projectId = 'prj_abc123xyz';
const chapterId = 'cht_def456uvw';
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/chapters/${chapterId}/prompt`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: '30-second product launch ad for the new Team tier. Lead with the headline from our latest release notes, drop in the new pricing screenshot, and resolve brand colors and type from the catalog.'
})
}
);
const result = await response.json();
console.log('Status:', result.status);
console.log('Estimated time:', result.estimated_time);
```
```python Python theme={null}
project_id = 'prj_abc123xyz'
chapter_id = 'cht_def456uvw'
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/chapters/{chapter_id}/prompt',
headers={
'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}',
'Content-Type': 'application/json'
},
json={
'content': '30-second product launch ad for the new Team tier. Lead with the headline from our latest release notes, drop in the new pricing screenshot, and resolve brand colors and type from the catalog.'
}
)
result = response.json()
print('Status:', result['status'])
print('Estimated time:', result['estimated_time'])
```
**Response:**
```json theme={null}
{
"prompt_id": "int_abc123xyz789",
"status": "processing",
"message": "Prompt processing started",
"estimated_time": "30-90 seconds"
}
```
The video creation process typically takes 30-90 seconds. The chapter will be updated with the video content automatically.
Once processing is complete, export the final video:
```bash curl theme={null}
curl -X POST https://api.babou.ai/api/v1/projects/prj_abc123xyz/export \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
const projectId = 'prj_abc123xyz';
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
const exportJob = await response.json();
console.log('Export status:', exportJob.status);
console.log('Estimated time:', exportJob.estimated_time);
```
```python Python theme={null}
project_id = 'prj_abc123xyz'
response = requests.post(
f'https://api.babou.ai/api/v1/projects/{project_id}/export',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
export_job = response.json()
print('Export status:', export_job['status'])
print('Estimated time:', export_job['estimated_time'])
```
**Response:**
```json theme={null}
{
"status": "queued",
"message": "Export started",
"estimated_time": "2-5 minutes"
}
```
Poll the export endpoint to check when your video is ready:
```bash curl theme={null}
curl https://api.babou.ai/api/v1/projects/prj_abc123xyz/export \
-H "Authorization: Bearer $BABOU_API_KEY"
```
```typescript TypeScript theme={null}
async function waitForExport(projectId: string) {
while (true) {
const response = await fetch(
`https://api.babou.ai/api/v1/projects/${projectId}/export`,
{
headers: {
'Authorization': `Bearer ${process.env.BABOU_API_KEY}`
}
}
);
const status = await response.json();
if (status.status === 'completed') {
console.log('✓ Video ready!');
console.log('Download URL:', status.download_url);
return status;
}
if (status.status === 'failed') {
console.error('✗ Export failed');
return status;
}
console.log('Still processing...');
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait 5s
}
}
await waitForExport('prj_abc123xyz');
```
```python Python theme={null}
import time
def wait_for_export(project_id):
while True:
response = requests.get(
f'https://api.babou.ai/api/v1/projects/{project_id}/export',
headers={'Authorization': f'Bearer {os.environ["BABOU_API_KEY"]}'}
)
status = response.json()
if status['status'] == 'completed':
print('✓ Video ready!')
print('Download URL:', status['download_url'])
return status
if status['status'] == 'failed':
print('✗ Export failed')
return status
print('Still processing...')
time.sleep(5) # Wait 5 seconds
wait_for_export('prj_abc123xyz')
```
**Response (when completed):**
```json theme={null}
{
"status": "completed",
"download_url": "https://assets.babou.ai/exports/prj_abc123xyz.mp4",
"started_at": "2025-12-02T10:05:00Z",
"completed_at": "2025-12-02T10:08:00Z",
"duration_seconds": 180
}
```
Your video is ready! Download it from the `download_url`.
## Complete Example
Here's a complete script that creates a video from start to finish:
```typescript TypeScript theme={null}
const BABOU_API_KEY = process.env.BABOU_API_KEY;
const BASE_URL = 'https://api.babou.ai/api/v1';
async function createVideo() {
// 1. Create project
const projectRes = await fetch(`${BASE_URL}/projects`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Pricing Page Refresh',
description: 'Launch campaign for the new Team tier'
})
});
const project = await projectRes.json();
console.log('✓ Project created:', project.id);
// 2. Add chapter
const chapterRes = await fetch(
`${BASE_URL}/projects/${project.id}/chapters`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name: 'Launch Ad',
duration: 30
})
}
);
const chapter = await chapterRes.json();
console.log('✓ Chapter created:', chapter.id);
// 3. Submit prompt
const promptRes = await fetch(
`${BASE_URL}/projects/${project.id}/chapters/${chapter.id}/prompt`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${BABOU_API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
content: 'Product launch ad for the new Team tier, pulling the headline from our release notes'
})
}
);
console.log('✓ Prompt submitted, processing...');
// Wait for processing
await new Promise(resolve => setTimeout(resolve, 60000));
// 4. Export video
await fetch(`${BASE_URL}/projects/${project.id}/export`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${BABOU_API_KEY}` }
});
console.log('✓ Export started...');
// 5. Wait for completion
while (true) {
const statusRes = await fetch(
`${BASE_URL}/projects/${project.id}/export`,
{
headers: { 'Authorization': `Bearer ${BABOU_API_KEY}` }
}
);
const status = await statusRes.json();
if (status.status === 'completed') {
console.log('✓ Video ready!');
console.log('Download:', status.download_url);
break;
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
}
createVideo();
```
```python Python theme={null}
import os
import time
import requests
BABOU_API_KEY = os.environ['BABOU_API_KEY']
BASE_URL = 'https://api.babou.ai/api/v1'
HEADERS = {
'Authorization': f'Bearer {BABOU_API_KEY}',
'Content-Type': 'application/json'
}
def create_video():
# 1. Create project
project = requests.post(
f'{BASE_URL}/projects',
headers=HEADERS,
json={
'name': 'Pricing Page Refresh',
'description': 'Launch campaign for the new Team tier'
}
).json()
print(f'✓ Project created: {project["id"]}')
# 2. Add chapter
chapter = requests.post(
f'{BASE_URL}/projects/{project["id"]}/chapters',
headers=HEADERS,
json={
'name': 'Launch Ad',
'duration': 30
}
).json()
print(f'✓ Chapter created: {chapter["id"]}')
# 3. Submit prompt
requests.post(
f'{BASE_URL}/projects/{project["id"]}/chapters/{chapter["id"]}/prompt',
headers=HEADERS,
json={
'content': 'Product launch ad for the new Team tier, pulling the headline from our release notes'
}
)
print('✓ Prompt submitted, processing...')
# Wait for processing
time.sleep(60)
# 4. Export video
requests.post(
f'{BASE_URL}/projects/{project["id"]}/export',
headers={'Authorization': f'Bearer {BABOU_API_KEY}'}
)
print('✓ Export started...')
# 5. Wait for completion
while True:
status = requests.get(
f'{BASE_URL}/projects/{project["id"]}/export',
headers={'Authorization': f'Bearer {BABOU_API_KEY}'}
).json()
if status['status'] == 'completed':
print('✓ Video ready!')
print(f'Download: {status["download_url"]}')
break
time.sleep(5)
create_video()
```
## Next Steps
Learn how to upload and use custom images, videos, and audio
Deep dive into creating complex multi-chapter videos
Explore all available endpoints and options
Integrate with AI agents like Claude