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

# Upload Asset

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

<Info>
  **Maximum file size:** 100MB per upload
</Info>

## Request

### Headers

<ParamField header="Authorization" type="string" required>
  Bearer token with your API key: `Bearer sk-bab-your-api-key`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  The MIME type of the file being uploaded (e.g., `video/mp4`, `image/png`, `audio/mpeg`)
</ParamField>

### Body

Send the raw binary file data as the request body.

<ParamField body="(raw binary)" type="binary" required>
  The raw file content
</ParamField>

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

<ResponseField name="id" type="string">
  Unique identifier for the uploaded asset
</ResponseField>

<ResponseField name="name" type="string">
  Asset filename
</ResponseField>

<ResponseField name="url" type="string | null">
  S3 URL to access the asset (null until upload completes)
</ResponseField>

<ResponseField name="content_type" type="string">
  MIME type of the uploaded file
</ResponseField>

<ResponseField name="size" type="number | null">
  File size in bytes
</ResponseField>

<ResponseField name="state" type="string">
  Upload state: `uploading`, `processing`, `ready`, or `error`
</ResponseField>

<ResponseField name="duration" type="number | null">
  Duration in seconds (for video/audio files, null for images)
</ResponseField>

<ResponseField name="width" type="number | null">
  Width in pixels (for image/video files)
</ResponseField>

<ResponseField name="height" type="number | null">
  Height in pixels (for image/video files)
</ResponseField>

<ResponseField name="created_at" type="string">
  ISO 8601 timestamp of upload
</ResponseField>

## Examples

<CodeGroup>
  ```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')
  ```
</CodeGroup>

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

<ResponseField name="FILE_TOO_LARGE" type="413">
  File exceeds 100MB limit

  ```json theme={null}
  {
    "error": "File size exceeds maximum allowed size of 100MB",
    "code": "FILE_TOO_LARGE"
  }
  ```
</ResponseField>

<ResponseField name="UPLOAD_FAILED" type="500">
  S3 upload failed

  ```json theme={null}
  {
    "error": "Failed to upload file to storage",
    "code": "UPLOAD_FAILED"
  }
  ```
</ResponseField>

<ResponseField name="VALIDATION_ERROR" type="400">
  Invalid or missing Content-Type header

  ```json theme={null}
  {
    "error": "Content-Type header is required",
    "code": "VALIDATION_ERROR"
  }
  ```
</ResponseField>

## Best Practices

<AccordionGroup>
  <Accordion title="Check file size before uploading">
    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');
    }
    ```
  </Accordion>

  <Accordion title="Store asset IDs and URLs">
    Save the returned `id` and `url` in your database to reference the asset later in your video projects.
  </Accordion>

  <Accordion title="Use appropriate content types">
    Always set the correct `Content-Type` header matching your file format. This ensures proper processing and display.
  </Accordion>

  <Accordion title="Handle upload errors gracefully">
    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)));
        }
      }
    }
    ```
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Get Asset" icon="download" href="/api-reference/assets/get">
    Retrieve asset details by ID
  </Card>

  <Card title="List Assets" icon="list" href="/api-reference/assets/list">
    View all your uploaded assets
  </Card>

  <Card title="Asset Management Guide" icon="folder" href="/guides/asset-management">
    Learn how to use assets in your videos
  </Card>
</CardGroup>
