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

# Media & Vault

> Upload media, access vault, and embed content

## What You Can Build

* **Media Management**: Upload, organize, and manage content libraries
* **Content Embedding**: Display OnlyFans media on your platform
* **Backup Tools**: Download and archive creator content
* **Cross-Platform Sync**: Manage media across multiple accounts

***

## Quick Example

List media from the vault:

<CodeGroup>
  ```javascript Node.js theme={null}
  const response = await fetch("https://api.ofauth.com/v2/access/vault/media", {
    headers: {
      apikey: "YOUR_API_KEY",
      "x-connection-id": "conn_abc123"
    }
  })

  const { list, hasMore } = await response.json()
  console.log(`Found ${list.length} media items`)
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      "https://api.ofauth.com/v2/access/vault/media",
      headers={
          "apikey": "YOUR_API_KEY",
          "x-connection-id": "conn_abc123"
      }
  )
  data = response.json()
  print(f"Found {len(data['list'])} media items")
  ```
</CodeGroup>

***

## Common Operations

### List Vault Media

Get media from the vault with optional filters:

```javascript theme={null}
const response = await fetch("https://api.ofauth.com/v2/access/vault/media?" + new URLSearchParams({
  limit: "24",
  offset: "0",
  sortBy: "recent",         // "recent" | "most-liked" | "highest-tips"
  sortDirection: "desc",    // "asc" | "desc"
  mediaType: "photo"        // "photo" | "video" | "audio" | "gif"
}), {
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123"
  }
})

const { list, hasMore } = await response.json()
```

### Get Vault Lists (Folders)

OnlyFans organizes vault media into lists/folders:

```javascript theme={null}
const response = await fetch("https://api.ofauth.com/v2/access/vault/lists", {
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123"
  }
})

const { list, all, hasMore } = await response.json()
// list: array of vault folders
// all: summary with total counts by type
```

### Get Media in a Specific List

```javascript theme={null}
const response = await fetch("https://api.ofauth.com/v2/access/vault/lists/LIST_ID/media", {
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123"
  }
})

const { list, hasMore } = await response.json()
```

### Create a Vault List

```javascript theme={null}
const response = await fetch("https://api.ofauth.com/v2/access/vault/lists", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    name: "My Custom Folder"
  })
})

const newList = await response.json()
```

### Add Media to a List

```javascript theme={null}
const response = await fetch("https://api.ofauth.com/v2/access/vault/lists/LIST_ID/media", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    mediaIds: [123, 456, 789]
  })
})
```

***

## Uploading Media

The `/uploads/init` response determines whether you need single-part or multi-part upload:

| Header                           | Meaning                                  |
| -------------------------------- | ---------------------------------------- |
| `x-ofauth-upload-total-parts: 1` | Single-part upload (auto-completes)      |
| `x-ofauth-upload-total-parts: N` | Multi-part upload (requires `/complete`) |

### Single-Part Upload (Small Files)

For small files where `x-ofauth-upload-total-parts` is `1`, upload completes automatically:

```javascript theme={null}
// Step 1: Initialize
const initResponse = await fetch("https://api.ofauth.com/v2/access/uploads/init", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    filename: "photo.jpg",
    filesize: 1024000,
    mimeType: "image/jpeg"
  })
})

const { mediaUploadId } = await initResponse.json()
const totalParts = initResponse.headers.get("x-ofauth-upload-total-parts")

// Step 2: Upload file (auto-completes for single-part)
const uploadResponse = await fetch(
  `https://api.ofauth.com/v2/access/uploads/${mediaUploadId}`,
  {
    method: "PUT",
    headers: {
      apikey: "YOUR_API_KEY",
      "x-connection-id": "conn_abc123",
      "Content-Type": "image/jpeg"
    },
    body: fileBuffer
  }
)

// Done! Response contains the media info directly
const { media } = await uploadResponse.json()
console.log("Uploaded media ID:", media.id)
```

### Multi-Part Upload (Large Files)

For larger files where `x-ofauth-upload-total-parts` is greater than `1`, upload in chunks and call `/complete`:

```javascript theme={null}
// Step 1: Initialize
const initResponse = await fetch("https://api.ofauth.com/v2/access/uploads/init", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    filename: "video.mp4",
    filesize: 50000000,  // 50MB
    mimeType: "video/mp4"
  })
})

const { mediaUploadId } = await initResponse.json()
const totalParts = parseInt(initResponse.headers.get("x-ofauth-upload-total-parts"))
const partSize = parseInt(initResponse.headers.get("x-ofauth-upload-part-size"))

// Step 2: Upload each chunk
for (let partNumber = 1; partNumber <= totalParts; partNumber++) {
  const start = (partNumber - 1) * partSize
  const end = Math.min(start + partSize, fileBuffer.length)
  const chunk = fileBuffer.slice(start, end)

  await fetch(
    `https://api.ofauth.com/v2/access/uploads/${mediaUploadId}/parts/${partNumber}`,
    {
      method: "PUT",
      headers: {
        apikey: "YOUR_API_KEY",
        "x-connection-id": "conn_abc123",
        "Content-Type": "video/mp4"
      },
      body: chunk
    }
  )
}

// Step 3: Complete upload (required for multi-part only)
const completeResponse = await fetch("https://api.ofauth.com/v2/access/uploads/complete", {
  method: "POST",
  headers: {
    apikey: "YOUR_API_KEY",
    "x-connection-id": "conn_abc123",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({ mediaUploadId })
})

const { media } = await completeResponse.json()
console.log("Uploaded media ID:", media.id)
```

<Info>
  **Single-part**: No `/complete` call needed—the PUT response returns the media directly.
  **Multi-part**: Must call `/complete` after all chunks are uploaded.
</Info>

***

## Media Storage Options

OFAuth provides two complementary solutions for handling OnlyFans media:

| Feature      | Media Proxy             | Vault+                       |
| ------------ | ----------------------- | ---------------------------- |
| **Purpose**  | Display current content | Build media libraries        |
| **Access**   | Via API response URLs   | By media ID directly         |
| **Storage**  | Temporary (edge cache)  | Persistent storage           |
| **Setup**    | Automatic               | Requires enabling            |
| **Billing**  | Per KB bandwidth        | Per GB/month storage         |
| **Best for** | Immediate display       | PPV features, offline access |

<Card title="Media Storage Guide" icon="database" href="/guides/media-storage">
  Learn when to use Media Proxy vs Vault+ with detailed examples
</Card>

***

## Media Proxy

OFAuth automatically proxies OnlyFans CDN URLs, making media embeddable on your platform.

<Info>
  **Automatic**: API responses already contain proxied `media.ofauth.com` URLs. No extra configuration needed.
</Info>

### Why Media Proxy?

| OnlyFans CDN        | OFAuth Media Proxy                |
| ------------------- | --------------------------------- |
| URLs expire quickly | Extended validity                 |
| CORS blocked        | CORS enabled                      |
| No caching control  | Edge-cached globally              |
| Cannot embed        | Embeddable in `<img>` / `<video>` |

### Using Proxied URLs

```javascript theme={null}
// API responses automatically include proxied URLs in files.full.url
const media = list[0]

// Use directly in HTML
const imgUrl = media.files.full.url
// https://media.ofauth.com/v1/abc123/photo.jpg
```

```html theme={null}
<!-- Embed in your app -->
<img src="https://media.ofauth.com/v1/abc123/photo.jpg" alt="Content" />
<video src="https://media.ofauth.com/v1/xyz789/video.mp4" controls />
```

### Trigger Persistent Caching

You can trigger persistent caching of a specific media item by sending a `POST` request to its media proxy URL:

```javascript theme={null}
await fetch("https://media.ofauth.com/abc123...", { method: "POST" })
// Response: { "success": true, "mediaId": "123456", "status": "queued" }
```

### Purge Stored Media

Remove a specific media item from persistent cache with a `DELETE` request:

```javascript theme={null}
await fetch("https://media.ofauth.com/abc123...", { method: "DELETE" })
// Response: { "success": true, "mediaId": "123456", "freedBytes": 1048576 }
```

### Billing

<CardGroup cols={2}>
  <Card title="Media Proxy" icon="globe">
    Billed per KB of bandwidth transferred. Cache hits (within 7 days) are free.
  </Card>

  <Card title="Vault+" icon="database">
    Billed per GB/month of storage. Persistent storage for media you need long-term access to.
  </Card>
</CardGroup>

<Info>
  Monitor usage in your [dashboard](https://app.ofauth.com). Media Proxy is best for temporary display; Vault+ is best for building permanent media libraries.
</Info>

***

## API Endpoints

| Endpoint                                                | Method | Description             |
| ------------------------------------------------------- | ------ | ----------------------- |
| `/v2/access/vault/media`                                | GET    | List all vault media    |
| `/v2/access/vault/lists`                                | GET    | Get vault lists/folders |
| `/v2/access/vault/lists`                                | POST   | Create a vault list     |
| `/v2/access/vault/lists/{listId}`                       | PATCH  | Update a vault list     |
| `/v2/access/vault/lists/{listId}`                       | DELETE | Delete a vault list     |
| `/v2/access/vault/lists/{listId}/media`                 | GET    | Get media in a list     |
| `/v2/access/vault/lists/{listId}/media`                 | POST   | Add media to a list     |
| `/v2/access/uploads/init`                               | POST   | Initialize media upload |
| `/v2/access/uploads/{mediaUploadId}`                    | PUT    | Upload single-part file |
| `/v2/access/uploads/{mediaUploadId}/parts/{partNumber}` | PUT    | Upload chunk            |
| `/v2/access/uploads/complete`                           | POST   | Complete upload         |

<Card title="Full API Reference" icon="book" href="/api-reference/access/overview">
  See complete endpoint documentation
</Card>

***

## Query Parameters

### Vault Media Query

| Parameter       | Type   | Default  | Description                                      |
| --------------- | ------ | -------- | ------------------------------------------------ |
| `limit`         | number | 24       | Results per page (1-40)                          |
| `offset`        | number | 0        | Pagination offset                                |
| `sortBy`        | string | "recent" | Sort by: `recent`, `most-liked`, `highest-tips`  |
| `sortDirection` | string | "desc"   | Sort direction: `asc`, `desc`                    |
| `mediaType`     | string | -        | Filter by type: `photo`, `video`, `audio`, `gif` |
| `query`         | string | -        | Search query                                     |

***

## Media Data Structure

Each media item includes:

```json theme={null}
{
  "id": 123456,
  "type": "photo",
  "canView": true,
  "hasError": false,
  "isReady": true,
  "createdAt": "2024-01-15T10:30:00Z",
  "files": {
    "full": {
      "url": "https://media.ofauth.com/v1/abc123/photo.jpg",
      "width": 1920,
      "height": 1080,
      "size": 245000
    },
    "thumb": {
      "url": "https://media.ofauth.com/v1/abc123/thumb.jpg",
      "width": 200,
      "height": 200
    },
    "preview": {
      "url": "https://media.ofauth.com/v1/abc123/preview.jpg",
      "width": 480,
      "height": 480
    }
  },
  "counters": {
    "likesCount": 42,
    "tipsSumm": 25.00
  }
}
```

For videos, additional fields:

```json theme={null}
{
  "type": "video",
  "duration": 120,
  "videoSources": {
    "240": "https://media.ofauth.com/v1/xyz789/240p.mp4",
    "720": "https://media.ofauth.com/v1/xyz789/720p.mp4"
  }
}
```

***

## Vault List Structure

```json theme={null}
{
  "id": 12345,
  "type": "custom",
  "name": "My Photos",
  "hasMedia": true,
  "canUpdate": true,
  "canDelete": true,
  "medias": [
    { "type": "photo", "url": "https://..." }
  ]
}
```

List types: `custom`, `messages`, `posts`, `stories`, `streams`, `media_stickers`

***

## Tips & Best Practices

<Tip>
  **Upload First**: Always upload media to the vault before referencing it in posts or messages. You can't upload inline.
</Tip>

<Info>
  **Supported Formats**: Images (JPEG, PNG, GIF, WebP), Videos (MP4, MOV), Audio (MP3, M4A). Check OnlyFans' guidelines for size limits.
</Info>

<Warning>
  **Bandwidth Costs**: Frequently accessed media (profile pictures, popular posts) may generate significant bandwidth charges. Monitor usage in your dashboard.
</Warning>

***

## Related Guides

<CardGroup cols={2}>
  <Card title="Media Storage" icon="database" href="/guides/media-storage">
    Media Proxy vs Vault+ - when to use each
  </Card>

  <Card title="Access API" icon="key" href="/api-reference/access/overview">
    Complete API reference
  </Card>

  <Card title="Content & Posts" icon="image" href="/guides/content">
    Use media in posts and stories
  </Card>

  <Card title="Messaging & Chats" icon="messages" href="/guides/messaging">
    Send media in direct messages
  </Card>
</CardGroup>
