Getting Started

We're excited for you to get integrating with Whop! If you haven't already created your company or made a product to sell, you'll want to start there.

Integration process

Our integration allows customers who purchase your product via Whop to easily access and use your software. The primary integration process is relatively simple, and mainly involves creating a route in your webapp to handle incoming users from the marketplace. The route will handle a code parameter, which can be used to log the user in or check their access to your product. Users are sent directly to your URL with the code parameter automatically added. This is how you look up the user.

Example: https://your-site.xyz/callback/whop?code=xxxxxxxx

When all is said and done, the integration process involves creating a seamless transition from the marketplace to your software product for a smooth user experience.

Prerequisites

To start the integration process, you will need to head to your developer settings page to obtain your Client ID and Client Secret. These keys will be used to identify your website/app and authenticate it to the Whop API.

Auth Setup

Creating your callback

Callback

<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> Router </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;next/router&#39;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">default</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">function</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Callback</span><span style="color: var(--shiki-color-text)">({ query }) {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-comment)">// Use the code parameter to request an authorization token</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">code</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">query</span><span style="color: var(--shiki-color-text)">.code</span></span>
<span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
<span></span>

This code creates a callback route that accepts the code from the callback url and you can use the code to request an authorization token.

Getting authorization token

Callback

POST
/api/v2/oauth/token
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> axios </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;axios&#39;</span><span style="color: var(--shiki-color-text)">;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">getAuthToken</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> (code</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> clientId</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> clientSecret</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> redirectUri) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">response</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">axios</span><span style="color: var(--shiki-token-function)">.post</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">&#39;https://api.whop.com/api/v2/oauth/token&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">            grant_type</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;authorization_code&#39;</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            code</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            client_id</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> clientId</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            client_secret</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> clientSecret</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            redirect_uri</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> redirectUri</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">        });</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">response</span><span style="color: var(--shiki-color-text)">.data;</span></span>
<span><span style="color: var(--shiki-color-text)">    } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> (error) {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">throw</span><span style="color: var(--shiki-color-text)"> error;</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">};</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">default</span><span style="color: var(--shiki-color-text)"> getAuthToken;</span></span>
<span></span>

This function takes in the code, clientId, clientSecret, and redirectUri as arguments and makes a POST request to the specified API endpoint. The code and other parameters are passed in the body of the request. The function returns the data from the API response which includes the access_token.

Checking for access

Callback

GET
/v2/me/has_access
<span><span style="color: var(--shiki-token-keyword)">import</span><span style="color: var(--shiki-color-text)"> axios </span><span style="color: var(--shiki-token-keyword)">from</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">&#39;axios&#39;</span><span style="color: var(--shiki-color-text)">;</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">checkAccess</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">async</span><span style="color: var(--shiki-color-text)"> (accessPassId</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> accessToken) </span><span style="color: var(--shiki-token-keyword)">=&gt;</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">    </span><span style="color: var(--shiki-token-keyword)">try</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">response</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">await</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">axios</span><span style="color: var(--shiki-token-function)">.get</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-string-expression)">`https://api.whop.com/api/v2/me/has_access/</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">accessPassId</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">            headers</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)">                Authorization</span><span style="color: var(--shiki-token-keyword)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">`Bearer </span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-color-text)">accessToken</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">            }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)">        });</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">return</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">response</span><span style="color: var(--shiki-color-text)">.data;</span></span>
<span><span style="color: var(--shiki-color-text)">    } </span><span style="color: var(--shiki-token-keyword)">catch</span><span style="color: var(--shiki-color-text)"> (error) {</span></span>
<span><span style="color: var(--shiki-color-text)">        </span><span style="color: var(--shiki-token-keyword)">throw</span><span style="color: var(--shiki-color-text)"> error;</span></span>
<span><span style="color: var(--shiki-color-text)">    }</span></span>
<span><span style="color: var(--shiki-color-text)">};</span></span>
<span></span>
<span><span style="color: var(--shiki-token-keyword)">export</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">default</span><span style="color: var(--shiki-color-text)"> checkAccess;</span></span>
<span></span>

This function takes in the resource ID and access token as arguments and makes a GET request to the specified API endpoint. The access token is included in the Authorization header of the request. The function returns the data from the API response.

Linking your software with your Whop product

In order for your customers to seamlessly use your software after they purchase, you will need to add your callback URL to your product on Whop.

Adding Webapp

Thats it! When a customer purchases a product on Whop, they will automatically be sent to your callback URL with the code parameter in the URL and they will be able to seamlessly use your software.

Integration examples

Integration #1

  1. Subscribe to our webhooks in such a way that you create a user using the info we send on the membership.went_valid webhook.
  2. Create a callback in your webapp such as /callback/whop that can handle a code parameter such as https://your-site.xyz/callback/whop?code=xxxxxxxx and exchange this code for a user authorization token.
  3. Use the webhook event membership.went_invalid to shut off the users access and assume they can no longer use your app.
  4. View the Webhook Integration for a full guide on this implementation.

Integration #2

  1. Create a callback your webapp such as /callback/whop that can handle a code parameter such as https://your-site.xyz/callback/whop?code=xxxxxxxx and exchange this code for a user authorization token.
  2. When the user comes in, either log them in if they have an account or create them an account if they do not yet have one.
  3. Check if the user should be able to access your product by using this endpoint.
  4. View the API Integration for a full guide on this implementation.

Integration #3

  1. Allow for users to input a license key before being able to use your software.
  2. Validate the license using our validate license endpoint and save any important info such as the users machine ID, or IP in order to prevent people from using their license key more than once.
  3. View the License Key Integration for a full guide on this implementation.

Adding sign in with Whop (Optional)

If you would like to give your users a better experience, add a way for them to authenticate if they originally purchased from Whop. First, you will need to send your users to the Whop OAuth Portal. You can do this by having the user click a login button, which might look something like this:

<span><span style="color: var(--shiki-color-text)">&lt;</span><span style="color: var(--shiki-token-string-expression)">a</span></span>
<span><span style="color: var(--shiki-color-text)">  </span><span style="color: var(--shiki-token-function)">href</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)">{</span><span style="color: var(--shiki-token-string-expression)">`https://whop.com/oauth?client_id=</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-token-constant)">process</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-constant)">env</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-constant)">CLIENT_ID</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">&amp;redirect_uri=</span><span style="color: var(--shiki-token-keyword)">${</span><span style="color: var(--shiki-token-constant)">process</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-constant)">env</span><span style="color: var(--shiki-color-text)">.</span><span style="color: var(--shiki-token-constant)">REDIRECT_URI</span><span style="color: var(--shiki-token-keyword)">}</span><span style="color: var(--shiki-token-string-expression)">`</span><span style="color: var(--shiki-color-text)">}</span></span>
<span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">  &lt;</span><span style="color: var(--shiki-token-string-expression)">button</span><span style="color: var(--shiki-color-text)">&gt;Login with Whop&lt;/</span><span style="color: var(--shiki-token-string-expression)">button</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span><span style="color: var(--shiki-color-text)">&lt;/</span><span style="color: var(--shiki-token-string-expression)">a</span><span style="color: var(--shiki-color-text)">&gt;</span></span>
<span></span>

After being redirected, the user will be prompted to sign in with their existing Whop account, or create a new one.

Sign in with Whop example