Skip to main content
This guide takes you from zero to a working embedded app: registered in the Builder, rendering inside Fanvue, and calling the Fanvue API as the creator using it. New to embedded apps? Start with the overview.

How authentication works

An embedded app renders inside Fanvue in an iframe. On load, Fanvue injects a short-lived session token that proves “this creator is using your app inside Fanvue right now.” Your backend trades that token for real OAuth access + refresh tokens through a delegated authorize-on-behalf flow, so you can call the Fanvue API on the creator’s behalf, including in the background, after they’ve closed the iframe. The security model is a capability split: Fanvue owns the authorize half (the creator’s identity + consent), your app owns the exchange half (your client secret + PKCE verifier). Neither side sees the other’s secrets, Fanvue hands you an authorization code but never sees your client secret, PKCE verifier, or the resulting tokens; your app never forges a creator’s identity. The recommended way to implement this is the @fanvue/builder-sdk SDK, which handles the whole exchange (PKCE, state, code exchange, refresh) so it’s hard to get wrong. If you can’t use the SDK, the full manual flow is in the appendix.

Step 1, Register your app

In Fanvue, go to App Store → Builder → Create App:
1

Set the app type

Choose App Type → Embedded App, and set the Embed URL to the page Fanvue should load, e.g. https://your-app.com/embedded.
2

Configure authentication

  • Copy the Client ID.
  • Reset secret and copy the Client Secret, it’s shown once.
  • Add a redirect URI (e.g. https://your-app.com/oauth/callback). It’s never visited by a browser, it only binds the authorization code, but it must match exactly what you send at token exchange.
  • Add the scopes your app needs (e.g. read:self, read:creator). See Scopes.
3

Complete the listing

Fill in App Details (icon, tagline) and Publish (preview image, description, test credentials), then submit for review. See Publishing Your App.

Step 2, Hosting requirements

  • Serve your embed URL over HTTPS with a browser-trusted certificate.
  • Send a Content-Security-Policy: frame-ancestors https://www.fanvue.com header so Fanvue can frame your page (use https://dev.fanvue.com against the dev environment).
  • Keep the client secret server-side only, never ship it to the browser.
When Fanvue opens your app in the iframe, it appends the session token to your embed URL (?token=...). The SDK exchanges it server-side for OAuth tokens and manages the session from there.
1

Add the session-exchange route

app/api/fanvue/session/route.ts
createConfig() reads your OAUTH_* and SESSION_* environment variables, set your Client ID, Client Secret, and registered redirect URI there.
2

Authenticate in your embedded page

app/embedded/page.tsx
useEmbeddedAuth reads the session token from the URL and exchanges it on mount, well inside the token’s ~60-second lifetime. It also returns theme, see Match the creator’s theme below.
3

Call the Fanvue API from your own routes

app/api/me/route.ts
Not on Next.js?
@fanvue/builder-sdk/react works with any React framework, and the core @fanvue/builder-sdk entrypoint exposes the low-level OAuth primitives (including getThemeFromUrl) for Node, Deno, or a custom server. If you can’t use the SDK at all, implement the flow yourself per the appendix.

Working on the creator’s behalf in the background

To act for the creator after the iframe closes (scheduled jobs, webhooks, automation), persist the refresh token when the exchange completes:
app/api/fanvue/session/route.ts
The SDK’s API client refreshes expired access tokens automatically. Once you hold the refresh token, you no longer need the iframe open.

Match the creator’s theme

To feel like a native part of Fanvue, your app should render in the same light or dark theme the creator is currently using. When Fanvue opens your iframe, it appends the creator’s active colour scheme to your embed URL as ?theme=light or ?theme=dark, alongside the ?token= session token. useEmbeddedAuth reads it for you and returns theme: 'light' | 'dark' | null, null when your app is opened outside Fanvue (no ?theme= present), so always fall back to a sensible default:
app/embedded/page.tsx
Unlike the session token, the theme isn’t single-use, it’s read straight from the URL, so you can pick it up anywhere on the client. Outside React (or in a plain server/iframe page), use the core helper directly:

Environments

Gotchas / checklist

  • PKCE is mandatory, an authorize-on-behalf request without a code_challenge (S256) is rejected. The SDK handles this for you.
  • redirect_uri at token exchange must exactly equal a registered redirect URI.
  • consent_required (403) from authorize-on-behalf means the creator hasn’t approved the in-platform consent screen yet (or revoked it), your app can’t create that consent; it happens in the Fanvue UI.
  • Request offline_access-capable scopes at consent time if you want refresh tokens (the in-platform consent grants it so one approval covers both the live surface and offline use).
  • Verify and track state, keep the client secret and PKCE verifier server-side, and never forward the session token anywhere but your own backend.
  • The session token has a ~60s TTL, if a user leaves the iframe open and clicks much later, re-read a fresh token (reopening the surface re-issues one).

Appendix, the manual flow (without the SDK)

Everything the SDK does, by hand. Use this if you’re not on Node/React, or you want to understand exactly what’s happening.

The endpoints

{PLATFORM}, {HYDRA}, and {API} are the environment base URLs above.

Frontend (the iframe page)

Fanvue loads your Embed URL with the session token as a query param (?token=…), plus the creator’s active colour scheme as ?theme=light|dark. Read the token and hand it to your backend, and nowhere else. The theme is yours to use client-side for styling.
Serve it with the CSP header (Node example):
The session token is short-lived (~60s) and single-purpose. Use it promptly to get tokens; don’t store it. Once you have the access + refresh tokens, you no longer need the iframe open.

Backend (the delegated flow)

Node 18+ (built-in fetch/crypto), framework-agnostic:

Using and refreshing tokens later (background work)

Store the refresh token per creator. When the access token expires, mint a new one, no iframe, no session token needed:

Next steps

Publishing Your App

Submit your embedded app for review and go live on the App Store.

API Reference

Every endpoint you can call once you hold an access token.