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

# Files & Folders

> Upload, manage, and organize files and folders via the Mavera API

## Overview

The Files API allows you to upload, manage, and retrieve files/assets in your Mavera workspaces. Files can include images, videos, documents, and other media that you want to use with other API features like responses, video analysis, or brand voice creation.

<CardGroup cols={2}>
  <Card title="Direct Uploads" icon="cloud-arrow-up">
    Upload files directly to storage using presigned URLs - no file data passes through the API server
  </Card>

  <Card title="Folder Organization" icon="folder-tree">
    Create folders to organize files, with full CRUD operations via API
  </Card>

  <Card title="Favorites" icon="star">
    Mark files and folders as favorites for quick access
  </Card>

  <Card title="Search" icon="magnifying-glass">
    Search files and folders by name with case-insensitive matching
  </Card>
</CardGroup>

## Upload Flow

The file upload process uses presigned URLs for efficient, direct-to-storage uploads:

<Steps>
  <Step title="Request Upload URL">
    Call `POST /api/v1/files/upload-url` with file metadata to get a presigned upload URL
  </Step>

  <Step title="Upload File">
    Use the presigned URL to upload the file directly to storage (PUT request)
  </Step>

  <Step title="Create File Record">
    Call `POST /api/v1/files` to create the database record and start tracking storage usage
  </Step>
</Steps>

## Basic Usage

### Uploading a File

<CodeGroup>
  ```python Python theme={"dark"}
  import requests
  import hashlib

  api_key = "mvra_live_your_key_here"
  headers = {"Authorization": f"Bearer {api_key}"}
  workspace_id = "ws_abc123"

  # Read file
  with open("document.pdf", "rb") as f:
      content = f.read()

  # Step 1: Get presigned upload URL
  upload_response = requests.post(
      "https://app.mavera.io/api/v1/files/upload-url",
      headers=headers,
      json={
          "file_name": "document.pdf",
          "file_type": "application/pdf",
          "file_size": len(content),
          "workspace_id": workspace_id
      }
  ).json()

  print(f"Upload URL expires in: {upload_response['expires_in']}s")

  # Step 2: Upload to presigned URL
  requests.put(
      upload_response["upload_url"],
      data=content,
      headers={"Content-Type": "application/pdf"}
  )

  # Step 3: Create file record
  file = requests.post(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      json={
          "name": "document.pdf",
          "type": "application/pdf",
          "url": upload_response["public_url"],
          "workspace_id": workspace_id,
          "file_size": len(content)
      }
  ).json()

  print(f"File created: {file['id']}")
  print(f"URL: {file['url']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const apiKey = "mvra_live_your_key_here";
  const headers = {
    "Authorization": `Bearer ${apiKey}`,
    "Content-Type": "application/json"
  };
  const workspaceId = "ws_abc123";

  async function uploadFile(file) {
    // Step 1: Get presigned upload URL
    const uploadResponse = await fetch(
      "https://app.mavera.io/api/v1/files/upload-url",
      {
        method: "POST",
        headers,
        body: JSON.stringify({
          file_name: file.name,
          file_type: file.type,
          file_size: file.size,
          workspace_id: workspaceId
        })
      }
    ).then(r => r.json());

    // Step 2: Upload to presigned URL
    await fetch(uploadResponse.upload_url, {
      method: "PUT",
      body: file,
      headers: { "Content-Type": file.type }
    });

    // Step 3: Create file record
    const fileRecord = await fetch("https://app.mavera.io/api/v1/files", {
      method: "POST",
      headers,
      body: JSON.stringify({
        name: file.name,
        type: file.type,
        url: uploadResponse.public_url,
        workspace_id: workspaceId,
        file_size: file.size
      })
    }).then(r => r.json());

    return fileRecord;
  }

  // Usage with file input
  const input = document.querySelector('input[type="file"]');
  input.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    const result = await uploadFile(file);
    console.log("Uploaded:", result.id);
  });
  ```

  ```bash cURL theme={"dark"}
  # Step 1: Get presigned upload URL
  curl -X POST https://app.mavera.io/api/v1/files/upload-url \
    -H "Authorization: Bearer mvra_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "file_name": "image.png",
      "file_type": "image/png",
      "file_size": 524288,
      "workspace_id": "ws_abc123"
    }'

  # Response includes upload_url and public_url

  # Step 2: Upload to presigned URL (use upload_url from response)
  curl -X PUT "PRESIGNED_URL_FROM_STEP_1" \
    -H "Content-Type: image/png" \
    --data-binary @image.png

  # Step 3: Create file record (use public_url from step 1)
  curl -X POST https://app.mavera.io/api/v1/files \
    -H "Authorization: Bearer mvra_live_your_key_here" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "image.png",
      "type": "image/png",
      "url": "PUBLIC_URL_FROM_STEP_1",
      "workspace_id": "ws_abc123",
      "file_size": 524288
    }'
  ```
</CodeGroup>

### Listing Files

<CodeGroup>
  ```python Python theme={"dark"}
  import requests

  api_key = "mvra_live_your_key_here"
  headers = {"Authorization": f"Bearer {api_key}"}

  # List all files
  files = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers
  ).json()

  for file in files["data"]:
      print(f"{file['name']} ({file['type']}) - {file['size']} bytes")
      print(f"  Favorite: {file['is_favorite']}")

  # Filter by workspace
  files = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      params={"workspace_id": "ws_abc123"}
  ).json()

  # Filter by type (images only)
  images = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      params={"type": "image"}
  ).json()

  # Get only favorites
  favorites = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      params={"is_favorite": "true"}
  ).json()

  # Search by name
  results = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      params={"search": "report"}
  ).json()

  # Paginate through results
  cursor = None
  all_files = []

  while True:
      params = {"limit": 50}
      if cursor:
          params["cursor"] = cursor

      response = requests.get(
          "https://app.mavera.io/api/v1/files",
          headers=headers,
          params=params
      ).json()

      all_files.extend(response["data"])

      if not response["has_more"]:
          break

      cursor = response["next_cursor"]

  print(f"Total files: {len(all_files)}")
  ```

  ```javascript JavaScript theme={"dark"}
  const apiKey = "mvra_live_your_key_here";
  const headers = { "Authorization": `Bearer ${apiKey}` };

  // List all files
  const files = await fetch("https://app.mavera.io/api/v1/files", { headers })
    .then(r => r.json());

  files.data.forEach(file => {
    console.log(`${file.name} (${file.type}) - ${file.size} bytes`);
    console.log(`  Favorite: ${file.is_favorite}`);
  });

  // Filter by type
  const images = await fetch(
    "https://app.mavera.io/api/v1/files?type=image",
    { headers }
  ).then(r => r.json());

  // Get favorites only
  const favorites = await fetch(
    "https://app.mavera.io/api/v1/files?is_favorite=true",
    { headers }
  ).then(r => r.json());

  // Search by name
  const searchResults = await fetch(
    "https://app.mavera.io/api/v1/files?search=report",
    { headers }
  ).then(r => r.json());

  // Paginate
  async function getAllFiles() {
    const allFiles = [];
    let cursor = null;

    while (true) {
      const url = new URL("https://app.mavera.io/api/v1/files");
      url.searchParams.set("limit", "50");
      if (cursor) url.searchParams.set("cursor", cursor);

      const response = await fetch(url, { headers }).then(r => r.json());
      allFiles.push(...response.data);

      if (!response.has_more) break;
      cursor = response.next_cursor;
    }

    return allFiles;
  }
  ```
</CodeGroup>

### Managing Favorites

Toggle favorite status on files to mark them for quick access:

<CodeGroup>
  ```python Python theme={"dark"}
  # Toggle favorite on a file
  result = requests.post(
      "https://app.mavera.io/api/v1/files/file_abc123/favorite",
      headers=headers
  ).json()

  print(f"File is now favorite: {result['is_favorite']}")

  # List only favorites
  favorites = requests.get(
      "https://app.mavera.io/api/v1/files",
      headers=headers,
      params={"is_favorite": "true"}
  ).json()
  ```

  ```javascript JavaScript theme={"dark"}
  // Toggle favorite on a file
  const result = await fetch(
    "https://app.mavera.io/api/v1/files/file_abc123/favorite",
    { method: "POST", headers }
  ).then(r => r.json());

  console.log(`File is now favorite: ${result.is_favorite}`);
  ```
</CodeGroup>

### Getting File Details

<CodeGroup>
  ```python Python theme={"dark"}
  file = requests.get(
      "https://app.mavera.io/api/v1/files/file_abc123",
      headers=headers
  ).json()

  print(f"Name: {file['name']}")
  print(f"URL: {file['url']}")
  print(f"Type: {file['type']}")
  print(f"Size: {file['size']} bytes")
  print(f"Folder: {file['folder']['name'] if file['folder'] else 'None'}")
  ```

  ```javascript JavaScript theme={"dark"}
  const file = await fetch(
    "https://app.mavera.io/api/v1/files/file_abc123",
    { headers }
  ).then(r => r.json());

  console.log(`Name: ${file.name}`);
  console.log(`URL: ${file.url}`);
  ```
</CodeGroup>

### Deleting Files

<CodeGroup>
  ```python Python theme={"dark"}
  response = requests.delete(
      "https://app.mavera.io/api/v1/files/file_abc123",
      headers=headers
  ).json()

  if response["deleted"]:
      print(f"File {response['id']} deleted successfully")
  ```

  ```javascript JavaScript theme={"dark"}
  const result = await fetch(
    "https://app.mavera.io/api/v1/files/file_abc123",
    { method: "DELETE", headers }
  ).then(r => r.json());

  if (result.deleted) {
    console.log(`File ${result.id} deleted`);
  }
  ```
</CodeGroup>

## Folders

Create and manage folders to organize your files.

### Creating Folders

<CodeGroup>
  ```python Python theme={"dark"}
  # Create a folder
  folder = requests.post(
      "https://app.mavera.io/api/v1/folders",
      headers=headers,
      json={
          "name": "Marketing Assets",
          "workspace_id": "ws_abc123"
      }
  ).json()

  print(f"Folder created: {folder['id']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const folder = await fetch("https://app.mavera.io/api/v1/folders", {
    method: "POST",
    headers,
    body: JSON.stringify({
      name: "Marketing Assets",
      workspace_id: "ws_abc123"
    })
  }).then(r => r.json());

  console.log(`Folder created: ${folder.id}`);
  ```
</CodeGroup>

### Listing Folders

<CodeGroup>
  ```python Python theme={"dark"}
  # List folders in a workspace
  folders = requests.get(
      "https://app.mavera.io/api/v1/folders",
      headers=headers,
      params={"workspace_id": "ws_abc123"}
  ).json()

  for folder in folders["data"]:
      print(f"{folder['name']} - {folder['file_count']} files")

  # Search folders
  results = requests.get(
      "https://app.mavera.io/api/v1/folders",
      headers=headers,
      params={
          "workspace_id": "ws_abc123",
          "search": "marketing"
      }
  ).json()

  # Get favorite folders only
  favorites = requests.get(
      "https://app.mavera.io/api/v1/folders",
      headers=headers,
      params={
          "workspace_id": "ws_abc123",
          "is_favorite": "true"
      }
  ).json()
  ```

  ```javascript JavaScript theme={"dark"}
  // List folders
  const folders = await fetch(
    "https://app.mavera.io/api/v1/folders?workspace_id=ws_abc123",
    { headers }
  ).then(r => r.json());

  folders.data.forEach(folder => {
    console.log(`${folder.name} - ${folder.file_count} files`);
  });
  ```
</CodeGroup>

### Getting Folder Contents

<CodeGroup>
  ```python Python theme={"dark"}
  # Get folder with files
  folder = requests.get(
      "https://app.mavera.io/api/v1/folders/folder_xyz",
      headers=headers,
      params={"include_files": "true"}
  ).json()

  print(f"Folder: {folder['name']}")
  print(f"Total files: {folder['file_count']}")

  for file in folder["files"]["data"]:
      print(f"  - {file['name']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const folder = await fetch(
    "https://app.mavera.io/api/v1/folders/folder_xyz?include_files=true",
    { headers }
  ).then(r => r.json());

  console.log(`Folder: ${folder.name}`);
  folder.files.data.forEach(file => {
    console.log(`  - ${file.name}`);
  });
  ```
</CodeGroup>

### Deleting Folders

<Warning>
  Deleting a folder also deletes all files inside it and reclaims the storage quota.
</Warning>

<CodeGroup>
  ```python Python theme={"dark"}
  result = requests.delete(
      "https://app.mavera.io/api/v1/folders/folder_xyz",
      headers=headers
  ).json()

  print(f"Folder deleted: {result['deleted']}")
  print(f"Files removed: {result['files_deleted']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const result = await fetch(
    "https://app.mavera.io/api/v1/folders/folder_xyz",
    { method: "DELETE", headers }
  ).then(r => r.json());

  console.log(`Deleted ${result.files_deleted} files`);
  ```
</CodeGroup>

### Folder Favorites

<CodeGroup>
  ```python Python theme={"dark"}
  # Toggle folder favorite
  result = requests.post(
      "https://app.mavera.io/api/v1/folders/folder_xyz/favorite",
      headers=headers
  ).json()

  print(f"Folder is now favorite: {result['is_favorite']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const result = await fetch(
    "https://app.mavera.io/api/v1/folders/folder_xyz/favorite",
    { method: "POST", headers }
  ).then(r => r.json());

  console.log(`Folder favorite: ${result.is_favorite}`);
  ```
</CodeGroup>

## File Size Limits

| File Type               | Maximum Size |
| ----------------------- | ------------ |
| Video files (`video/*`) | 2 GB         |
| All other files         | 10 MB        |

## Storage Quota

File uploads count against your subscription's storage quota. You can check your current usage via the dashboard or subscription API.

When you delete a file, the storage is reclaimed and your quota is updated accordingly.

## Using Files with Other APIs

Once uploaded, files can be referenced in other API calls:

### With the Responses API

```python theme={"dark"}
# Use file URL in response input attachments
response = requests.post(
    "https://app.mavera.io/api/v1/responses",
    headers=headers,
    json={
        "model": "mavera-v1",
        "input": [
            {
                "role": "user",
                "content": "Describe this image",
                "attachments": [
                    {
                        "type": "image",
                        "url": file["url"]
                    }
                ]
            }
        ]
    }
)
```

### With Brand Voice

```python theme={"dark"}
# Use uploaded documents for brand voice analysis
response = requests.post(
    "https://app.mavera.io/api/v1/brand-voices",
    headers=headers,
    json={
        "label": "My Brand Voice",
        "usage_context": "Marketing content",
        "documents": [
            {
                "name": file["name"],
                "url": file["url"],
                "file_type": file["type"],
                "file_size": file["size"]
            }
        ]
    }
)
```

## Best Practices

<AccordionGroup>
  <Accordion title="Use checksums for integrity verification">
    Include a SHA256 checksum when requesting upload URLs to ensure file integrity:

    ```python theme={"dark"}
    import hashlib
    checksum = hashlib.sha256(content).hexdigest()

    upload_response = requests.post(
        "https://app.mavera.io/api/v1/files/upload-url",
        json={
            "file_name": "document.pdf",
            "file_type": "application/pdf",
            "file_size": len(content),
            "checksum": checksum,  # SHA256 checksum
            "workspace_id": workspace_id
        }
    )
    ```
  </Accordion>

  <Accordion title="Handle upload errors gracefully">
    The presigned URL expires after 1 hour. If upload fails, request a new URL:

    ```python theme={"dark"}
    def upload_with_retry(file_path, workspace_id, max_retries=3):
        for attempt in range(max_retries):
            try:
                # Get fresh upload URL
                upload_response = get_upload_url(file_path, workspace_id)
                # Upload to presigned URL
                upload_to_storage(upload_response["upload_url"], file_path)
                # Create record
                return create_file_record(upload_response, file_path)
            except Exception as e:
                if attempt == max_retries - 1:
                    raise
                time.sleep(2 ** attempt)  # Exponential backoff
    ```
  </Accordion>

  <Accordion title="Organize files with folders">
    Use folder\_id to organize files logically:

    ```python theme={"dark"}
    # Upload to a specific folder
    upload_response = requests.post(
        "https://app.mavera.io/api/v1/files/upload-url",
        json={
            "file_name": "report.pdf",
            "file_type": "application/pdf",
            "file_size": file_size,
            "workspace_id": workspace_id,
            "folder_id": "folder_xyz"  # Optional folder
        }
    )
    ```
  </Accordion>

  <Accordion title="Check storage quota before uploading">
    For large files, verify you have sufficient storage quota before uploading:

    ```python theme={"dark"}
    # The upload-url endpoint will return an error if quota would be exceeded
    response = requests.post(
        "https://app.mavera.io/api/v1/files/upload-url",
        json={...}
    )
    if "error" in response.json():
        error = response.json()["error"]
        if "storage" in error["message"].lower():
            print("Storage quota exceeded!")
    ```
  </Accordion>
</AccordionGroup>

<CardGroup cols={2}>
  <Card title="Files API Reference" icon="file" href="/api-reference/files/list-files">
    See the full API specification for Files endpoints
  </Card>

  <Card title="Folders API Reference" icon="folder" href="/api-reference/folders/list-folders">
    See the full API specification for Folders endpoints
  </Card>
</CardGroup>
