Skip to content

Media

client.media handles every file that touches the platform: token images, series asset bundles, and generative code. For the end-to-end pattern, see media upload flow.

Every asset declares its kind up front — kind is not inferred from MIME type:

KindUse for
MediaKind.FileSingle blob (image, video, audio, metadata JSON)
MediaKind.DirectoryZip archive — extracted after upload
import { MediaKind } from "@highlightxyz/sdk";
const res = await client.media.createUploadSession({
kind: MediaKind.File,
fileName: "artwork.png",
mimeType: "image/png",
fileSize: 1024000,
// scope: "CollectionLogo" | "CollectionAsset" | ... (optional)
});
// { data: { media: Media.Entity, upload: { url, method, headers } } }

Returns a signed upload URL. fileSize is validated against the per-kind limit up front (see size limits).

You can either use fetch directly against upload.url or call client.media.upload which does the same with the SDK’s auth headers:

// Preferred: direct PUT
await fetch(session.upload.url, {
method: session.upload.method,
headers: {
...session.upload.headers,
authorization: `Bearer ${accessToken}`,
},
body: buffer,
});

After PUT, File assets transition straight to Ready. Directory assets trigger extraction and go PendingProcessingReady.

If you need to force progression (rare — mostly for retrying a failed Directory extraction):

await client.media.process({ mediaId });

Idempotent. Ready and Processing assets are returned as-is.

const res = await client.media.get({ mediaId });
// res.data.status: "Pending" | "Processing" | "Ready" | "Failed"
// res.data.url: string | null (resolved URL when Ready)
// res.data.locations: Array<{ provider, role, ref, ... }>
// res.data.archive?: { totalFiles, manifest: [...] } (Directory only)

Poll at ~2s intervals until status === "Ready" or "Failed".

const res = await client.media.listChildren({
mediaId,
limit: 100,
cursor: nextCursor,
});
// res.data.entries[].key, .size, .mimeType, .id

Fetch one child’s full Entity (including a resolved URL):

const res = await client.media.getChild({
mediaId,
path: "index.html",
});
// res.data.url, .status, .locations
await client.media.publish({ mediaId });
  • File → publishes the blob to Arweave.
  • Directory → publishes a manifest over all children.

Idempotent. Adds an Arweave entry to the asset’s locations.

For collection metadata specifically, prefer client.collection.finalizeBaseUri — it archives the token-metadata directory and flips the on-chain baseURI in one call.

await client.media.delete({ mediaId });

Returns MediaInUseError (409) if the media is referenced by a deployed contract or live generative code. Arweave locations are immutable and remain on-chain.

KindMax
File (image)50 MB
File (audio)250 MB
File (video)500 MB
File (metadata)1 MB
Directory1 GB