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

# Upload files

> Upload images and files for use across the Whop platform

Upload files to Whop for use in courses, forums, profiles, and more. The SDK provides a simple `upload` method that handles the entire process for you.

<CodeGroup>
  ```typescript TypeScript theme={null}
  import fs from 'fs';

  const file = await client.files.upload({
    file: fs.readFileSync('./photo.jpg'),
    filename: 'photo.jpg',
  });

  console.log(file.id); // file_xxxxxxxxxxxxx
  console.log(file.url); // URL to access the file
  ```

  ```python Python theme={null}
  file = client.files.upload(
      file=open("./photo.jpg", "rb").read(),
      filename="photo.jpg",
  )

  print(file.id)  # file_xxxxxxxxxxxxx
  print(file.url)  # URL to access the file
  ```

  ```ruby Ruby theme={null}
  file = client.files.upload(
    file: File.binread("./photo.jpg"),
    filename: "photo.jpg",
  )

  puts file.id  # file_xxxxxxxxxxxxx
  puts file.url  # URL to access the file
  ```
</CodeGroup>

The `upload` method:

1. Creates a file record with a presigned URL
2. Uploads your file to storage
3. Polls until processing is complete
4. Returns the ready file with its final URL

# Public vs private files

Every file you upload has a **visibility** that controls how it can be accessed. You choose visibility at upload time — it cannot be changed after.

| Visibility         | URL type                    | Who can access             | Use for                                                        |
| ------------------ | --------------------------- | -------------------------- | -------------------------------------------------------------- |
| `public` (default) | Permanent, unsigned CDN URL | Anyone with the link       | Product images, thumbnails, branding, marketing assets         |
| `private`          | Signed URL that expires     | Only your app, via the API | User-uploaded documents, sensitive content, AI-generated files |

<Warning>
  **Choose carefully.** Public files are cached on our CDN and accessible to anyone with the URL — there is no way to revoke access. If the content is user-specific or sensitive, use `private`.
</Warning>

## Uploading a private file

Pass `visibility: "private"` when creating the file:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const file = await client.files.upload({
    file: fs.readFileSync('./user-document.pdf'),
    filename: 'user-document.pdf',
    visibility: 'private',
  });

  // file.url is a signed URL that expires — fetch it fresh via
  // client.files.retrieve(file.id) whenever you need a new one
  ```

  ```python Python theme={null}
  file = client.files.upload(
      file=open("./user-document.pdf", "rb").read(),
      filename="user-document.pdf",
      visibility="private",
  )
  ```

  ```ruby Ruby theme={null}
  file = client.files.upload(
    file: File.binread("./user-document.pdf"),
    filename: "user-document.pdf",
    visibility: "private",
  )
  ```
</CodeGroup>

## Accessing private files

Private file URLs expire. To get a fresh URL, retrieve the file by ID:

```typescript theme={null}
const file = await client.files.retrieve(fileId);
console.log(file.url); // fresh signed URL
```

# Using uploaded files

Once uploaded, use the file ID in any API call that accepts file attachments:

```typescript theme={null}
await client.courses.update({
	id: "course_xxx",
	thumbnail: { id: file.id },
});
```

# File properties

| Property        | Description                                                    |
| --------------- | -------------------------------------------------------------- |
| `id`            | Unique identifier (e.g., `file_xxxxxxxxxxxxx`)                 |
| `filename`      | Original filename                                              |
| `content_type`  | MIME type (e.g., `image/jpeg`)                                 |
| `byte_size`     | File size in bytes                                             |
| `url`           | URL to access the file (signed and expiring for private files) |
| `upload_status` | Status: `pending`, `processing`, `ready`, or `failed`          |
| `visibility`    | `public` or `private`                                          |
