HELIX Docs
Platform API

World media (thumbnail, preview image, preview video)

Set a world's card artwork and the images + YouTube video that play in its detail carousel. Images upload through a presigned PUT; video is referenced from YouTube, not hosted.

Every world has a thumbnail (its card artwork across discovery) and an optional preview image plus an optional preview video that appear as extra slides in the world detail carousel.

The thumbnail is always the cover

Until a creator uploads one, a world's thumbnailUrl is a deterministic auto-generated data:image/svg+xml placeholder — it is never null. Check hasCustomThumbnail to tell a real upload from the placeholder; testing thumbnailUrl for null will always say "set".

The media slots

Prop

Type

Video is referenced, not uploaded

The preview slot accepts images only. video/mp4 and video/webm uploads are rejected with a 400. Use the YouTube endpoint below instead — YouTube handles transcoding, adaptive streaming, moderation and bandwidth, and the world detail embeds it privacy-first via youtube-nocookie.

Uploading an image

Two steps, mirroring the build-upload flow. Both are owner-gated.

# 1. Presign. `kind` is "thumbnail" (default) or "preview".
curl -X POST "$API/api/v1/instant-worlds/$WORLD_ID/media/upload" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"contentType":"image/png","size":267583,"kind":"preview"}'
# → { "item": { "key": "...", "url": "<presigned PUT>", "contentType": "...", "cacheControl": "..." } }

The presigned URL is bound to the exact contentType and byte size you declared — send the same bytes you measured, or the PUT is rejected.

# 2. PUT the bytes straight to storage, then finalize with the key you were given.
curl -X PUT "$UPLOAD_URL" -H "Content-Type: image/png" --data-binary @preview.png

curl -X POST "$API/api/v1/instant-worlds/$WORLD_ID/media/finalize" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"key":"'"$KEY"'","kind":"preview"}'
# → the updated world

Accepted image types: image/png, image/jpeg, image/webp, image/gif, image/avif.

Your image is live immediately

Moderation runs asynchronously — you never wait on a verdict. If a scan later flags the image, the slot is cleared and the world falls back to its generated thumbnail.

Setting a preview video

Paste any normal YouTube link. It is normalised server-side to a video id and stored; nothing is downloaded or re-hosted.

curl -X POST "$API/api/v1/instant-worlds/$WORLD_ID/media/youtube" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"url":"https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=30s"}'

All of these resolve to the same id — extra params like &t= and &list= are discarded:

Prop

Type

Anything that is not a YouTube reference — another host, a javascript: URL, a malformed id — is rejected with a 400. There is no best-effort fallback.

The world then carries both a raw id and a ready-made embed URL:

Prop

Type

Prefer previewYoutubeUrl over building your own

Embedding previewYoutubeUrl keeps you on the nocookie host and matches what the HELIX world detail does. If you build your own player, load it on click rather than on page load — an eagerly mounted YouTube iframe pulls ~1MB of player JS into your page's critical path.

Clearing a slot

# kind = thumbnail (reverts to the generated SVG) | preview | youtube
curl -X DELETE "$API/api/v1/instant-worlds/$WORLD_ID/media?kind=youtube" \
  -H "Authorization: Bearer $TOKEN"

Reading media back

Every world response carries the resolved URLs:

const world = await fetch(`${API}/api/v1/instant-worlds/${slug}`).then((r) => r.json());

world.item.thumbnailUrl;       // always set (custom upload or generated SVG)
world.item.hasCustomThumbnail; // false → it's the generated placeholder
world.item.previewMediaUrl;    // preview image, or null
world.item.previewYoutubeId;   // video id, or null
world.item.previewYoutubeUrl;  // nocookie embed URL, or null

Image URLs are served through the CDN's image transform, so they arrive already resized and in a modern format for the requesting browser.

The world detail builds its carousel in this order, skipping anything unset and de-duplicating identical URLs:

  1. Thumbnail — always the cover, and the page's LCP image.
  2. Preview image — if set and different from the thumbnail.
  3. Screenshots — the gallery a creator arranges in the editor, in their order.
  4. Preview video — last, as a click-to-play slide.

A preview that duplicates the thumbnail is worse than no preview

The carousel de-duplicates identical URLs, but it cannot tell that two different URLs hold the same picture. Uploading a copy of your thumbnail as the preview gives players a carousel that shows the same image twice. Make the preview earn its slide: a different area, a different moment, something the cover doesn't already show.

On this page