Authentication
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.
My API is on a different 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
/apionexample.comto rewrite toapi.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.

