Whop Platform Expert
Purpose
Provide comprehensive, accurate guidance for building on the Whop platform based on 212+ official Whop documentation files. Cover all aspects of memberships, payments, digital products, app development, and community features.
Documentation Coverage
Full access to official Whop documentation (when available):
- •Location:
docs/whop/ - •Files: 212 markdown files
- •Coverage: Complete API reference, guides, integrations, and best practices
Note: Documentation must be pulled separately:
pipx install docpull docpull https://docs.whop.com -o .claude/skills/whop/docs
Major Areas:
- •Products & Plans (membership management)
- •Payments, Refunds & Disputes
- •Memberships & Access Control
- •Courses & Educational Content
- •Experiences & Communities
- •Forums & Chat Channels
- •Apps & Integrations
- •Webhooks & Events
- •Affiliates & Revenue Sharing
When to Use
Invoke when user mentions:
- •Platform: Whop, membership platform, digital products, monetization
- •Memberships: subscriptions, access passes, licenses, member management
- •Products: digital products, courses, communities, experiences
- •Courses: educational content, lessons, chapters, students
- •Communities: forums, chat, Discord integration
- •Payments: checkout, billing, refunds, disputes, transfers
- •Apps: Whop apps, B2B integrations, OAuth, API integrations
- •Webhooks: payment events, membership events, notifications
How to Use Documentation
When answering questions:
- •
Search for specific topics:
bash# Use Grep to find relevant docs grep -r "memberships" .claude/skills/api/whop/docs/ --include="*.md"
- •
Read specific API references:
bash# API docs are organized by resource cat .claude/skills/api/whop/docs/docs_whop_com/api-reference_memberships_list-memberships.md
- •
Find integration guides:
bash# Developer guides ls .claude/skills/api/whop/docs/docs_whop_com/developer*
Core Authentication
API Keys
Two types of API keys:
- •
Company API Keys - Access your own company data
- •Found in: Dashboard → Settings → API Keys
- •Use for: Your own payments, memberships, products
- •
App API Keys - Access data across multiple companies
- •Found in: Dashboard → Developer → Your App → API Keys
- •Use for: Multi-tenant apps, B2B integrations
Authentication:
// Server-side only
const headers = {
'Authorization': `Bearer ${process.env.WHOP_API_KEY}`,
'Content-Type': 'application/json',
};
const response = await fetch('https://api.whop.com/api/v5/me', {
headers,
});
Security:
- •NEVER expose API keys client-side
- •Use environment variables
- •Rotate keys if exposed
- •Use scoped permissions when possible
Quick Start Integration
1. Create a Product
const product = await fetch('https://api.whop.com/api/v5/products', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
title: 'Premium Membership',
description: 'Access to all content',
visibility: 'visible',
}),
});
2. Create a Plan
const plan = await fetch('https://api.whop.com/api/v5/plans', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_id: 'prod_xxx',
billing_period: 1,
billing_period_unit: 'month',
price: 2999, // $29.99 in cents
currency: 'usd',
}),
});
3. List Memberships
const memberships = await fetch(
'https://api.whop.com/api/v5/memberships?valid=true',
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
}
);
Membership Management
Check User Access
// Using the Me endpoints (user's access token)
async function checkUserAccess(userAccessToken: string) {
const response = await fetch('https://api.whop.com/api/v5/me/memberships', {
headers: {
'Authorization': `Bearer ${userAccessToken}`,
},
});
const memberships = await response.json();
// Check if user has active membership
const hasAccess = memberships.data.some(
(m: any) => m.status === 'active' && m.valid
);
return hasAccess;
}
Validate License Key
async function validateLicense(licenseKey: string) {
const response = await fetch(
`https://api.whop.com/api/v5/memberships?license_key=${licenseKey}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
}
);
const data = await response.json();
return data.data.length > 0 && data.data[0].valid;
}
Cancel Membership
async function cancelMembership(membershipId: string) {
const response = await fetch(
`https://api.whop.com/api/v5/memberships/${membershipId}/cancel`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
}
);
return response.json();
}
Payment Integration
Create Checkout Session
async function createCheckout(planId: string, userId: string) {
const response = await fetch(
'https://api.whop.com/api/v5/checkout-configurations',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
plan_id: planId,
success_url: 'https://yourapp.com/success',
cancel_url: 'https://yourapp.com/cancel',
metadata: {
user_id: userId,
},
}),
}
);
const checkout = await response.json();
return checkout.url; // Redirect user to this URL
}
Retrieve Payment
async function getPayment(paymentId: string) {
const response = await fetch(
`https://api.whop.com/api/v5/payments/${paymentId}`,
{
headers: {
'Authorization': `Bearer ${API_KEY}`,
},
}
);
return response.json();
}
Refund Payment
async function refundPayment(paymentId: string, amount?: number) {
const response = await fetch(
`https://api.whop.com/api/v5/payments/${paymentId}/refund`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount, // Optional: partial refund in cents
}),
}
);
return response.json();
}
Course Management
Create Course
async function createCourse(productId: string) {
const response = await fetch('https://api.whop.com/api/v5/courses', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_id: productId,
title: 'Complete Web Development Course',
description: 'Learn fullstack development from scratch',
visibility: 'visible',
}),
});
return response.json();
}
Create Chapter
async function createChapter(courseId: string) {
const response = await fetch(
'https://api.whop.com/api/v5/course-chapters',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
course_id: courseId,
title: 'Introduction to JavaScript',
order: 1,
}),
}
);
return response.json();
}
Create Lesson
async function createLesson(chapterId: string) {
const response = await fetch(
'https://api.whop.com/api/v5/course-lessons',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
chapter_id: chapterId,
title: 'Variables and Data Types',
content: 'Lesson content in markdown...',
type: 'video', // or 'text', 'quiz', 'assignment'
video_url: 'https://youtube.com/watch?v=...',
order: 1,
}),
}
);
return response.json();
}
Track Lesson Completion
async function markLessonComplete(lessonId: string, userAccessToken: string) {
const response = await fetch(
`https://api.whop.com/api/v5/course-lessons/${lessonId}/mark-as-completed`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${userAccessToken}`,
},
}
);
return response.json();
}
Community Features
Create Forum Post
async function createForumPost(
forumId: string,
title: string,
content: string,
userAccessToken: string
) {
const response = await fetch(
'https://api.whop.com/api/v5/forum-posts',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${userAccessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
forum_id: forumId,
title,
content,
}),
}
);
return response.json();
}
Create Chat Message
async function sendChatMessage(
channelId: string,
content: string,
userAccessToken: string
) {
const response = await fetch(
'https://api.whop.com/api/v5/messages',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${userAccessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel_id: channelId,
content,
}),
}
);
return response.json();
}
Webhook Implementation
Setup Webhook Endpoint
import { headers } from 'next/headers';
import crypto from 'crypto';
export async function POST(req: Request) {
const body = await req.text();
const signature = headers().get('x-whop-signature');
// Verify webhook signature
const webhookSecret = process.env.WHOP_WEBHOOK_SECRET!;
const hash = crypto
.createHmac('sha256', webhookSecret)
.update(body)
.digest('hex');
if (hash !== signature) {
return new Response('Invalid signature', { status: 401 });
}
const event = JSON.parse(body);
// Handle webhook events
switch (event.action) {
case 'payment.succeeded':
await handlePaymentSuccess(event.data);
break;
case 'payment.failed':
await handlePaymentFailure(event.data);
break;
case 'membership.activated':
await handleMembershipActivated(event.data);
break;
case 'membership.deactivated':
await handleMembershipDeactivated(event.data);
break;
case 'dispute.created':
await handleDispute(event.data);
break;
default:
console.log(`Unhandled event: ${event.action}`);
}
return Response.json({ received: true });
}
Critical webhook events:
- •
payment.succeeded- Payment completed - •
payment.failed- Payment failed - •
payment.pending- Payment pending - •
membership.activated- Membership started - •
membership.deactivated- Membership ended/cancelled - •
invoice.paid- Recurring payment succeeded - •
invoice.past_due- Payment failed, retry in progress - •
dispute.created- Customer disputed payment
App Development
OAuth Integration
1. Redirect user to Whop OAuth:
const authUrl = new URL('https://whop.com/oauth');
authUrl.searchParams.set('client_id', process.env.WHOP_CLIENT_ID!);
authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/callback');
authUrl.searchParams.set('scope', 'memberships:read payments:read');
// Redirect user
window.location.href = authUrl.toString();
2. Handle callback and exchange code:
export async function GET(req: Request) {
const url = new URL(req.url);
const code = url.searchParams.get('code');
// Exchange code for access token
const response = await fetch('https://api.whop.com/api/v5/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: process.env.WHOP_CLIENT_ID!,
client_secret: process.env.WHOP_CLIENT_SECRET!,
code,
grant_type: 'authorization_code',
redirect_uri: 'https://yourapp.com/callback',
}),
});
const { access_token, refresh_token } = await response.json();
// Store tokens securely
await storeTokens(userId, access_token, refresh_token);
return Response.redirect('/dashboard');
}
Create Notification
async function sendNotification(userId: string, message: string) {
const response = await fetch(
'https://api.whop.com/api/v5/notifications',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
user_id: userId,
title: 'New Update',
message,
type: 'info', // or 'success', 'warning', 'error'
}),
}
);
return response.json();
}
Affiliate System
Create Promo Code
async function createPromoCode(
productId: string,
code: string,
discount: number
) {
const response = await fetch(
'https://api.whop.com/api/v5/promo-codes',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
product_id: productId,
code,
discount_type: 'percentage',
discount_value: discount, // e.g., 20 for 20% off
max_uses: 100,
}),
}
);
return response.json();
}
Next.js Integration Example
Middleware for Access Control
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
const accessToken = request.cookies.get('whop_user_token')?.value;
if (!accessToken) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Check if user has valid membership
const response = await fetch('https://api.whop.com/api/v5/me/memberships', {
headers: {
'Authorization': `Bearer ${accessToken}`,
},
});
const memberships = await response.json();
const hasAccess = memberships.data.some(
(m: any) => m.status === 'active' && m.valid
);
if (!hasAccess) {
return NextResponse.redirect(new URL('/subscribe', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/premium/:path*'],
};
Server Action for Checkout
// app/actions/whop.ts
'use server';
import { auth } from '@/lib/auth';
export async function createCheckoutSession(planId: string) {
const session = await auth();
if (!session?.user?.id) {
throw new Error('Unauthorized');
}
const response = await fetch(
'https://api.whop.com/api/v5/checkout-configurations',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.WHOP_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
plan_id: planId,
success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=true`,
cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing?canceled=true`,
metadata: {
user_id: session.user.id,
},
}),
}
);
const data = await response.json();
return { url: data.url };
}
Testing
Test Mode
Whop provides a test/sandbox environment:
- •Use test API keys from Dashboard
- •No real charges
- •Simulate memberships and payments
Test Webhooks Locally
# Use ngrok or similar for local testing ngrok http 3000 # Configure webhook URL in Whop Dashboard: # https://your-ngrok-url.ngrok.io/api/webhooks
Security Best Practices
- •
API Keys:
- •Never expose API keys client-side
- •Use environment variables
- •Rotate keys periodically
- •Use scoped keys when possible
- •
Webhooks:
- •ALWAYS verify webhook signatures
- •Use HTTPS endpoints only
- •Return 200 immediately, process async
- •Handle retries gracefully
- •
Access Tokens:
- •Store user tokens encrypted
- •Refresh tokens when expired
- •Never log sensitive data
- •
Validation:
- •Validate membership status server-side
- •Don't trust client-sent data
- •Check license keys before granting access
Common Patterns
Membership Gating Pattern
// lib/whop/check-access.ts
export async function checkMembershipAccess(
userId: string,
productId: string
): Promise<boolean> {
const response = await fetch(
`https://api.whop.com/api/v5/memberships?user_id=${userId}&product_id=${productId}&valid=true`,
{
headers: {
'Authorization': `Bearer ${process.env.WHOP_API_KEY}`,
},
}
);
const data = await response.json();
return data.data.length > 0;
}
// Use in API routes or Server Components
export async function GET(req: Request) {
const userId = await getCurrentUserId();
const hasAccess = await checkMembershipAccess(userId, 'prod_xxx');
if (!hasAccess) {
return Response.json({ error: 'No access' }, { status: 403 });
}
// Return protected content
return Response.json({ data: 'Premium content' });
}
TypeScript Types
// types/whop.ts
export interface WhopMembership {
id: string;
user_id: string;
product_id: string;
plan_id: string;
status: 'active' | 'paused' | 'canceled' | 'expired';
valid: boolean;
license_key: string;
quantity: number;
renews_at: string | null;
expires_at: string | null;
cancel_at_period_end: boolean;
created_at: string;
}
export interface WhopProduct {
id: string;
title: string;
description: string;
visibility: 'visible' | 'hidden';
created_at: string;
}
export interface WhopPayment {
id: string;
amount: number;
currency: string;
status: 'succeeded' | 'pending' | 'failed';
user_id: string;
product_id: string;
created_at: string;
}
export interface WhopWebhookEvent {
action: string;
data: any;
timestamp: string;
}
Documentation Quick Reference
Need to find something specific?
Use Grep to search the 212 documentation files:
# Search all docs grep -r "search term" .claude/skills/api/whop/docs/ # Search API references only grep -r "memberships" .claude/skills/api/whop/docs/docs_whop_com/api-reference* # List all endpoints ls .claude/skills/api/whop/docs/docs_whop_com/api-reference*/
Common doc locations:
- •API Reference:
api-reference_*/ - •Developer Guides:
developer_*/ - •Affiliates:
affiliates_*/
Resources
- •Dashboard: https://whop.com/dashboard
- •Developer Portal: https://whop.com/dashboard/developer
- •API Base: https://api.whop.com/api/v5
- •Documentation: https://docs.whop.com
Implementation Checklist
Setup:
- • Create Whop account and company
- • Get API keys from Dashboard → Settings → API Keys
- • Set environment variables (WHOP_API_KEY, WHOP_WEBHOOK_SECRET)
- • Install fetch or axios for API calls
Core Features:
- • Create products and plans
- • Implement checkout flow
- • Add membership access checking
- • Set up webhook endpoint with signature verification
- • Handle payment succeeded/failed events
- • Handle membership activated/deactivated events
- • Test in development environment
Advanced (if needed):
- • Build Whop App with OAuth
- • Implement course management
- • Set up community features (forums, chat)
- • Configure affiliate system
- • Add notification system
- • Implement analytics tracking
Error Handling
async function safeWhopRequest(
url: string,
options: RequestInit
): Promise<any> {
try {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Whop API error');
}
return response.json();
} catch (error) {
console.error('Whop API error:', error);
throw error;
}
}
Common errors:
- •
401 Unauthorized- Invalid or expired API key - •
403 Forbidden- Insufficient permissions - •
404 Not Found- Resource doesn't exist - •
429 Too Many Requests- Rate limit exceeded - •
500 Internal Server Error- Whop service issue
Rate Limiting
Whop implements rate limiting. Best practices:
- •Cache membership status where appropriate
- •Use webhooks instead of polling
- •Implement exponential backoff on failures
- •Batch operations when possible