Claude Code Plugins

Community-maintained marketplace

Feedback

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.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

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:

  1. Search for specific topics:

    # Use Grep to find relevant docs
    grep -r "memberships" .claude/skills/api/whop/docs/ --include="*.md"
    
  2. Read specific API references:

    # API docs are organized by resource
    cat .claude/skills/api/whop/docs/docs_whop_com/api-reference_memberships_list-memberships.md
    
  3. Find integration guides:

    # Developer guides
    ls .claude/skills/api/whop/docs/docs_whop_com/developer*
    

Core Authentication

API Keys

Two types of API keys:

  1. Company API Keys - Access your own company data

    • Found in: Dashboard → Settings → API Keys
    • Use for: Your own payments, memberships, products
  2. 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

  1. API Keys:

    • Never expose API keys client-side
    • Use environment variables
    • Rotate keys periodically
    • Use scoped keys when possible
  2. Webhooks:

    • ALWAYS verify webhook signatures
    • Use HTTPS endpoints only
    • Return 200 immediately, process async
    • Handle retries gracefully
  3. Access Tokens:

    • Store user tokens encrypted
    • Refresh tokens when expired
    • Never log sensitive data
  4. 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

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