| name | whop-expert |
| description | Comprehensive Whop platform expert with access to 212 official documentation files covering memberships, payments, products, courses, experiences, forums, webhooks, and app development. Invoke when user mentions Whop, digital products, memberships, course platforms, or community monetization. |
| allowed-tools | Read, Write, Edit, Grep, Glob, Bash, WebFetch |
| model | sonnet |
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