Skip to main content
Whop apps render inside an iframe on whop.com. To figure out who is making a request to your app, Whop passes a short-lived JWT in the x-whop-user-token header on every same-origin request. You verify this token with the SDK to get a user ID, then check what that user is allowed to see.

Verify the iframe user token

Call verifyUserToken on each request. It reads the x-whop-user-token header, validates the JWT signature and expiry, and returns the user ID. Invalid tokens throw by default. Each SDK also has a non-throwing variant:
The token is only attached to requests hitting the window.location.origin of your iframe. Examples:
  • Relative page navigations: <a href="/sub_page">...</a>
  • Fetches without a domain: await fetch("/api/quizzes")
All of these resolve to your App.base_url domain.
If your frontend runs on example.com but your API runs on api.example.com, reverse-proxy API requests through example.com/api so they carry the x-whop-user-token header.
  • Cloudflare: create an “origin rule” from /api on example.com to rewrite to api.example.com/api.
  • Next.js: add a rewrite in next.config.mjs.
  • nginx / Caddy: use the rewrite primitive in your server config.
This setup is required because of strict browser cross-origin cookie policies.

Local setup

Whop ships a local reverse proxy that matches the production iframe + cookie behavior, so the code you write works identically on localhost and in production. See the dev proxy guide for setup.

Check access

Now that you know who’s making the request, check what they’re allowed to see. Use the checkAccess method to verify access to an Experience, Company, or Product.

Resource IDs and access levels

The response’s access_level is one of:

Customer app (Experience View)

Customer apps should gate on the experienceId passed in path params. Return no_access or redirect if the viewer doesn’t have a valid membership.
The same pattern works in an API route. Swap headers() for request.headers and return a 403 Response instead of JSX.

Dashboard app (Dashboard View)

Dashboard apps should gate on access_level === "admin" so only company team members can load the view.
Same pattern in an API route. Swap headers() for request.headers and return a 403 Response instead of JSX.

Next steps

Run a local dev proxy

Match the production iframe + cookie setup on localhost.

Request permissions

Add the scopes your app needs before publishing.

Listen to webhooks

Receive payment, membership, and entry events on your server.

Build an app view

Set up dashboard views, experiences, and discover listings.