Create a file record, PUT the bytes to the presigned upload URL, choose public or private files, confirm the upload status, and attach files to resources.
Upload files to Whop for use in courses, forums, profiles, disputes, and more.
Uploading is a two-step flow, and the SDK helps with the first step:
Create a file record.files.create returns the new file’s id plus a presigned upload_url and the upload_headers to send with it.
Send the bytes yourself.PUT the file’s contents to upload_url with those headers. The SDK doesn’t transfer the bytes for you.
Once the bytes land, the file processes and its upload_status becomes ready. You can attach the id to a resource right away, and use files.retrieve to poll for the final url.
use whop_sdk::prelude::*;let config = ClientConfig { token: Some(std::env::var("WHOP_API_KEY").expect("WHOP_API_KEY is not set")), ..Default::default()};let client = Whop::new(config).expect("Failed to build client");
Create the record, then PUT the bytes to the presigned URL that comes back.
import { readFile } from "node:fs/promises";// 1. Create the file record and get a presigned upload URLconst file = await client.files.create({ filename: "photo.jpg" });// 2. Send the bytes to storage yourselfconst bytes = await readFile("./photo.jpg");await fetch(file.upload_url!, { method: "PUT", headers: file.upload_headers as Record<string, string>, body: bytes,});console.log(file.id); // file_xxxxxxxxxxxxx — ready to attach
import requests# 1. Create the file record and get a presigned upload URLfile = client.files.create(filename="photo.jpg")# 2. Send the bytes to storage yourselfwith open("./photo.jpg", "rb") as contents: requests.put( file.upload_url, headers=file.upload_headers or {}, data=contents, )print(file.id) # file_xxxxxxxxxxxxx — ready to attach
require "net/http"# 1. Create the file record and get a presigned upload URLfile = client.files.create(filename: "photo.jpg")# 2. Send the bytes to storage yourselfuri = URI(file.upload_url)request = Net::HTTP::Put.new(uri)(file.upload_headers || {}).each { |key, value| request[key] = value }request.body = File.binread("./photo.jpg")Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(request) }puts file.id # file_xxxxxxxxxxxxx — ready to attach
import ( "bytes" "fmt" "net/http" "os")// 1. Create the file record and get a presigned upload URLfile, err := client.Files.Create(context.TODO(), &whopsdk.CreateFilesRequest{ Filename: "photo.jpg",})if err != nil { return err}// 2. Send the bytes to storage yourselfdata, err := os.ReadFile("./photo.jpg")if err != nil { return err}request, err := http.NewRequest(http.MethodPut, *file.UploadURL, bytes.NewReader(data))if err != nil { return err}for key, value := range file.UploadHeaders { request.Header.Set(key, fmt.Sprint(value))}if _, err := http.DefaultClient.Do(request); err != nil { return err}fmt.Println(file.ID) // file_xxxxxxxxxxxxx — ready to attach
// 1. Create the file record and get a presigned upload URLlet file = client .files .create( &CreateFilesRequest { filename: "photo.jpg".to_string(), ..Default::default() }, None, ) .await?;// 2. Send the bytes to storage yourselflet contents = std::fs::read("./photo.jpg")?;let mut request = reqwest::Client::new() .put(file.upload_url.as_deref().unwrap()) .body(contents);if let Some(headers) = &file.upload_headers { for (key, value) in headers { if let Some(value) = value.as_str() { request = request.header(key, value); } }}request.send().await?;println!("{}", file.id); // file_xxxxxxxxxxxxx — ready to attach
Set the visibility before uploading. Public files are cached on Whop’s CDN and accessible to anyone with the URL — there is no way to revoke access. If the content is user-specific or sensitive, keep the default private.
Pass a public visibility when creating the file, then upload the bytes the same way:
const file = await client.files.create({ filename: "product-cover.jpg", visibility: "public",});// PUT the bytes to file.upload_url, then file.url is a permanent CDN URL
file = client.files.create( filename="product-cover.jpg", visibility="public",)# PUT the bytes to file.upload_url, then file.url is a permanent CDN URL
file = client.files.create( filename: "product-cover.jpg", visibility: "public",)# PUT the bytes to file.upload_url, then file.url is a permanent CDN URL
file, err := client.Files.Create(context.TODO(), &whopsdk.CreateFilesRequest{ Filename: "product-cover.jpg", Visibility: whopsdk.CreateFilesRequestVisibilityPublic.Ptr(),})// PUT the bytes to file.UploadURL, then file.URL is a permanent CDN URL
let file = client .files .create( &CreateFilesRequest { filename: "product-cover.jpg".to_string(), visibility: Some(CreateFilesRequestVisibility::Public), ..Default::default() }, None, ) .await?;// PUT the bytes to file.upload_url, then file.url is a permanent CDN URL
Attaching a file to a resource moves it to the storage that matches that resource’s own visibility. An explicit visibility mainly matters for files you link to directly without attaching.
After the bytes land, the file processes asynchronously. Retrieve it until upload_status leaves pending and processing. When it’s ready, read the final url. When it’s failed, stop and surface the error.