Skip to main content
Look up any key of app-manifest.json for manifestVersion 1: its type, whether it’s required, the values it accepts and the URL rules it follows. To connect a manifest to your app, start with the App Manifest overview; when a check fails, the error code leads you to Manifest statuses and errors.

Complete example

This manifest uses every key. For a first manifest, start from the minimal manifest instead.
app-manifest.json
x-build is optional metadata of your own; any key prefixed x- is ignored.

Rules for the whole file

  • The file is a single JSON object, served at https://<your app domain>/app-manifest.json. See Serving requirements.
  • Unknown keys are rejected at every level.
  • Keys prefixed x- are ignored at every level. Use them for your own metadata, for example "x-build" or "x-commit".
  • Don’t add "$schema". It’s an unknown key and fails the check. No JSON Schema file is published for the manifest.
  • Every URL starts with lowercase https://, except a redirect URI on localhost, 127.0.0.1 or [::1], which may use http://. See URL rules for what else each URL allows.

Top-level keys

If a key is present, the manifest sets that field and the Developer Area marks it From manifest. Leave a key out to keep using your settings value; Settings or manifest has the full rules. Every field has a Developer Area equivalent except access.postInstallUrl:
  • access maps to the app type select, App URL and Embed settings on App details.
  • oauth maps to the Authentication tab.
  • webhooks maps to the Events tab.
The manifest contains no app identity, so no Client ID and no app UUID. Your app domain is what links the file to your app.

manifestVersion

manifestVersion must be the number 1. The string "1" fails with unsupported_manifest_version, and when this check fails it’s the only issue reported.

access

access sets your app’s type and the URLs creators and fans open, and Fanvue always reads it. The Developer Area labels the two types On-platform and Off-platform. All four fields are staged onto your store draft on every successful check and reach your live app after review. Leaving out fanExperienceUrl or postInstallUrl keeps the current value. Neither can be cleared from the manifest, so to change one, set a new URL. A manifest that would remove the fan experience surface is refused at check; remove the surface in Embed settings instead, as described in Removing a fan experience. For an off_platform app, url must be a public production URL using https, checked when the manifest is staged. These hosts are refused:
  • localhost, 127.0.0.1, 0.0.0.0, ::1, any IP address and any host without a dot
  • any host that is or ends in local, localhost, internal, test or example
  • any host that is or ends in ngrok.io, ngrok-free.app, ngrok.app, ngrok.dev, trycloudflare.com, loca.lt, serveo.net, localtunnel.me, tunnelto.dev, lhr.life, localhost.run, repl.co, replit.dev or netlify.app
  • any host that is or ends in fanvue.com
A netlify.app domain can still host your manifest; only the app URL is restricted. The error names the manifest field, access.url, followed by the reason.

surfaces

surfaces places an on-platform app on Fanvue surfaces. Changes are staged onto your store draft on every successful check and reach your live app after review. surfaces isn’t allowed when access.type is off_platform, even as [] (surfaces_on_off_platform). Changing access.type to off_platform removes all of your app’s surfaces when that version is approved. creator_landing and fan_experience always follow access. access.url sets creator_landing and access.fanExperienceUrl sets fan_experience, so you don’t need a surfaces block for either. Add a surfaces block to set fullScreen or presentation, or to declare other surfaces. When present, the file is the whole list, and fan_chat or fan_post surfaces missing from it are removed when your next version is approved. creator_landing and fan_experience are never removed this way. When surfaces is present, a creator_landing src must equal access.url, and a fan_experience src must equal access.fanExperienceUrl when that is set. Otherwise the check fails with conflicting_surface_url. Fanvue sets the iframe sandbox permissions, and a top-level sandbox key fails with sandbox_not_configurable.

presentation

presentation on the fan_experience surface decides whether the experience opens as a page or a dialog when a fan clicks it on a chat card or the creator’s profile. The Open as field in Embed settings sets the same value. Direct links and the experience detail page always open as a page, and on phones a dialog is always full screen. Declare desktopWidth and desktopHeight together, or neither to open as large as possible; one without the other fails with incomplete_dialog_size. A page presentation can’t declare either (dialog_size_on_page).

oauth

oauth sets the scopes your app can request and the redirect URIs Fanvue accepts, both of which you can also set on the Authentication tab. Each key you include replaces that list on every successful check and overrides the tab’s value; leave a key out to keep using the tab’s value. Redirect URI changes need no review. A scope added after your app is published waits for review, and the Authentication tab shows it as Pending review until then. Allowed scopes values: read:self, read:creator, write:creator, read:chat, write:chat, read:fan, read:media, write:media, read:post, write:post, read:insights, read:tracking_links, write:tracking_links, read:experience, write:experience, read:agency, write:agency. openid and offline_access are always allowed for your app. Don’t list them, because the check fails if you do. Request them in your /oauth2/auth URL if you need an ID token or a refresh token. The Fanvue App Starter and SDK request them by default. Adding a scope makes every user authorise your app again, so read When changes take effect before you add one. "oauth": { "scopes": [], "redirectUris": [] } clears both lists, whatever the Authentication tab holds, and stops sign-in working for your app. To submit for review, your app needs at least one redirect URI in use, from the Authentication tab or the manifest. Until it has one, the submission checklist flags the missing redirect URI.

webhooks

webhooks.destinations subscribes endpoints to webhook events, which you can also set on the Events tab; see Webhooks. When webhooks is present, the file’s list replaces your app’s destinations on every successful check. Endpoints on the Events tab stay saved and editable, but the tab marks them From manifest and they aren’t in use while the manifest sets the list. Remove the webhooks key to go back to the tab’s list.
  • A destination is created only once your app has the scope that event requires. Add the scope to oauth.scopes in the same file and both apply in one check, or add it on the Authentication tab if the manifest doesn’t set scopes. Until then the destination waits, and the status shows Out of sync.
  • Each app has a destination limit, 20 by default, counted across all your app’s destinations after the check. If your file goes over it, the check shows Invalid with too_many_destinations, but oauth changes in the same file are already applied.
  • Every destination uses your app’s one signing secret, shown on the Events tab. See Verify webhook signatures.
  • creator.payment.pending, creator.payment.failed, checkout_link.refund.rejected, checkout_link.refund.failed and app.subscription.deactivated pass validation but Fanvue never delivers them.

URL rules

Every URL must parse as an absolute URL (invalid_url), be 2048 characters or fewer (too_big) and start with lowercase https:// (not_https). The one exception is a redirect URI on a loopback host, which may use http://. No URL has to be on your app domain.

Not configured in the manifest

Keys for these settings (appName, tagline, listing, tracking) fail with unknown_key.