Skip to main content
Media endpoints return a creator’s vault items and chat attachments as stable UUIDs plus short-lived variant URLs. Follow this guide and your app will load a vault folder or a chat thread in a handful of requests, never render an expired URL, and read price and purchase state from the right place. You need an access token with read:media. Two rules shape everything below:
  1. Variant URLs are short-lived. Re-fetch a URL before you use it.
  2. List endpoints return media without URLs unless you ask for variants, so resolve in batches rather than one lookup per item.

How media and variants work

A media item has a stable uuid and one or more variants. A variant is one rendition of the item at one size or treatment, each with its own URL. Variant URLs appear only when you ask for them. List endpoints return a minimal shape (uuid, status) by default. Pass the variants query parameter to get the full object, including a variants array where each entry has url, variantType, width, height and displayPosition. Filter to ready media with status=ready, because an item that is still processing has no usable variant URLs.

Treat media URLs as short-lived

Variant URLs are signed and expire, so fetch them when you need them.
Don’t persist signed media URLs and render them later. By the time a user opens the page, a stored URL has expired and returns 403 or 404 from the CDN.
Store the stable identifiers instead (mediaUuid, and for chat media the messageUuid), then resolve a fresh URL at display time:
1

Store IDs, not URLs

Persist mediaUuid (and messageUuid for chat attachments). These never change.
2

Resolve on demand

When something scrolls into view, request the variants you need for those IDs.
3

Cache in memory, briefly

Hold the resolved URL in memory for the current view. Do not write it to durable storage.
4

Refetch on failure

If a render fails with 403 or 404, resolve the URL again rather than retrying the dead one.

Age verification for media access

In regions where Fanvue must verify age, a user completes age verification before your app can read their media. Until then, the media read endpoints return 403:
  • GET /media
  • GET /media/{uuid}
  • GET /media/bulk
  • GET /vault/folders/{folderName}/media
The 403 body tells you where to send the user:
ageVerificationUrl points to the Fanvue-hosted verification the user completes themselves. Once they have verified, the media endpoints return normally. The check applies only to users whose region requires age verification, and only until they have verified, so creators and already-verified users are not affected.

Display Vault media efficiently

GET /vault/folders/{folderName}/media lists a folder’s items without variant URLs, and resolving each one by scanning the full media list costs hundreds of calls per folder view. Use GET /media with a folderName filter, a variants list and status instead:
Each item in data carries its variants array, so one paged response gives you every URL you need. Page through with page and size. Pick the smallest variant that fits the surface. Render grids and lists with thumbnail, and resolve main only when the user opens an item full-screen.

The {folderName} path segment is a name, not an ID

On the folder-scoped endpoints, {folderName} is the folder’s exact display name, URL-encoded:
  • GET /vault/folders/{folderName}/media lists a folder’s media
  • DELETE /vault/folders/{folderName}/media/{mediaUuid} detaches one media item from a folder
The name is case-sensitive and scoped to the owning creator. URL-encode spaces and special characters, so the folder Folder 1 becomes Folder%201. A Folder not found 404 means no folder with that exact name exists for that creator, so check the casing and encoding of the name, and don’t send a folder ID.

Resolve specific media by ID

When you hold a set of media UUIDs, resolve them in one request. GET /media/bulk takes a comma-separated mediaUuids list of 1 to 20 UUIDs and an optional variants list:
The response has two keys:
results maps each requested UUID to its item, or null when it couldn’t be resolved. errors lists one entry per unresolved UUID with code NOT_FOUND or INTERNAL. One missing item never fails the request.

Check processing status

GET /media/{uuid} returns one item. Its status is created, processing, ready or error. When status is error, errorReason names the cause: Poll GET /media/{uuid} after an upload until status is ready or error.

Upload media

Media enters Fanvue through a multipart upload session. You create the session with POST /media/uploads, PUT each part to a signed URL, complete the session and poll GET /media/{uuid} until ready. Upload media in parts has the full sequence, limits and SDK helpers.

Display chat media reliably

Chat attachments follow the same short-lived URL rule. Store each message’s stable IDs (messageUuid, mediaUuid), then resolve fresh URLs for the media a user is viewing. GET /chats/{userUuid}/messages/{messageUuid}/media resolves attachments for one message. It takes mediaUuids (comma-separated, up to 20) and an optional variants list:
Call it as media scrolls into view, in batches of up to 20 UUIDs, and cache each resolved URL in memory until the user leaves the view.

Pricing fields on chat media

Chat media items carry pricing, purchasedAt and amountPaid, which describe the pay-to-view message the item came from rather than the item itself. The two kinds of media on a paid message report them differently:
  • Body media, the items being sold, carry the message’s price. pricing is the listed price in USD cents, and purchasedAt and amountPaid fill in once the fan unlocks the message. pricing doesn’t reflect discounts, so amountPaid is what the fan paid.
  • The free preview, the item shown before the paywall, reports all three as null, even on a priced message the fan has bought.
The preview is the media item whose uuid matches the message’s mediaPreviewUuid, which is null when the sender configured no preview.
Don’t read a message’s price or purchase state off one media item. A null pricing on a chat media item means either that the message was free or that the item is the free preview of a paid one. Take pricing and purchasedAt from the message; see Check PPV payment state.

Putting it together

  • Store IDs, never signed URLs.
  • Ask for the variants you need, and filter with status=ready.
  • Load a Vault folder with folderName on GET /media, one call per page.
  • Re-resolve URLs on demand with GET /media/bulk or the per-message media endpoint.
  • Treat 403 and 404 on a media URL as a signal to resolve again.
Resolving in batches, only for visible media, keeps you inside the rate limits.

See also