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:
# Use Grep to find relevant docs grep -r "memberships" .claude/skills/api/whop/docs/ --include="*.md"Read specific API references:
# API docs are organized by resource cat .claude/skills/api/whop/docs/docs_whop_com/api-reference_memberships_list-memberships.mdFind integration guides:
# 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 completedpayment.failed- Payment failedpayment.pending- Payment pendingmembership.activated- Membership startedmembership.deactivated- Membership ended/cancelledinvoice.paid- Recurring payment succeededinvoice.past_due- Payment failed, retry in progressdispute.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 key403 Forbidden- Insufficient permissions404 Not Found- Resource doesn't exist429 Too Many Requests- Rate limit exceeded500 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
Converted and distributed by TomeVault — claim your Tome and manage your conversions.