Upstreet Docs
    SDK

    Asset subclients—characters, scenes, items, sprites, mobs, music, sound effects, and voices. Unified interface for listing, creating, and fetching asset data.

    Asset Subclients

    The PUClient exposes several asset subclients that share a unified interface. Each handles a specific asset type: characters, scenes, items, sprites, mobs, music, sound effects, and voices. Use them to list, create, fetch metadata, and download asset binaries.


    Asset Subclients

    SubclientPurpose
    client.charactersCharacter assets
    client.scenesScene/level assets
    client.itemsItem assets
    client.spritesSprite assets
    client.mobsMob (mobile entity) assets
    client.musicMusic track assets
    client.soundEffectsSound effect assets
    client.voicesVoice assets (slightly different API)

    Common Methods

    All asset subclients (except voices) support the same interface:

    MethodSignatureDescription
    listAll(params?) => Promise<AssetRecord[]>List assets with optional filters
    getOne(id: string) => Promise<AssetRecord>Get asset metadata by ID
    getDataFor(id: string) => Promise<ArrayBuffer>Fetch asset binary data
    delete(id: string) => Promise<void>Delete an asset
    createOne(params) => Promise<AssetRecord>Create a new asset

    listAll(params?)

    listAll(params?: {
      own?: boolean;
      search?: string;
      sortBy?: string;
      sortOrder?: 'asc' | 'desc';
    }): Promise<AssetRecord[]>

    Lists assets with optional filters.

    ParamTypeDescription
    ownbooleanOnly assets you own
    searchstringSearch by name/description
    sortBystringField to sort by
    sortOrder'asc' | 'desc'Sort direction
    const characters = await client.characters.listAll({ own: true });
    const scenes = await client.scenes.listAll({ search: 'forest', sortBy: 'name' });

    getOne(id)

    getOne(id: string): Promise<AssetRecord>

    Fetches asset metadata by ID.

    const character = await client.characters.getOne('character-uuid');
    console.log(character.name, character.description);

    getDataFor(id)

    getDataFor(id: string): Promise<ArrayBuffer>

    Fetches the raw binary data for an asset (e.g. image, audio, model file).

    const buffer = await client.characters.getDataFor('character-uuid');
    // Use buffer for blob, base64, or file save

    delete(id)

    delete(id: string): Promise<void>

    Deletes an asset by ID.

    await client.characters.delete('character-uuid');

    createOne(params)

    createOne(params: {
      id?: string;
      name: string;
      description?: string;
      file: Blob | ArrayBuffer | File;
      preview?: Blob | ArrayBuffer | File;
      metadata?: Record<string, unknown>;
    }): Promise<AssetRecord>

    Creates a new asset. Requires name and file (the binary data).

    ParamTypeDescription
    idstringOptional custom ID
    namestringAsset name
    descriptionstringOptional description
    fileBlob | ArrayBuffer | FileBinary asset data
    previewBlob | ArrayBuffer | FileOptional preview image
    metadataRecord<string, unknown>Optional metadata
    const character = await client.characters.createOne({
      name: 'Hero',
      description: 'Main protagonist',
      file: imageBuffer,
      preview: thumbnailBuffer,
    });

    Voices Subclient

    The client.voices subclient is slightly different—voice assets have a different shape and may expose alternate methods (e.g. for listing available voices for NPCs or TTS). Check the SDK types for the exact voices API.

    Voice assets are often used for NPC speech. See NPCs for configuring voice on an NPC (e.g. kittentts:expr-voice-5-m).


    Code Examples

    Characters

    import { PUClient } from 'pu-client';
    
    const client = new PUClient({ apiKey: process.env.PU_API_KEY! });
    
    const characters = await client.characters.listAll({ own: true });
    const char = await client.characters.getOne(characters[0].id);
    const data = await client.characters.getDataFor(char.id);
    
    console.log(char.name, data.byteLength);
    import { PUClient } from 'pu-client';
    
    const client = new PUClient({ apiKey: process.env.PU_API_KEY! });
    
    const file = await fetch('hero.png').then(r => r.arrayBuffer());
    const preview = await fetch('hero-thumb.png').then(r => r.arrayBuffer());
    
    const character = await client.characters.createOne({
      name: 'Knight',
      description: 'A brave knight',
      file,
      preview,
      metadata: { style: 'fantasy' },
    });
    
    console.log(`Created: ${character.id}`);

    Scenes

    import { PUClient } from 'pu-client';
    
    const client = new PUClient({ apiKey: process.env.PU_API_KEY! });
    
    const scenes = await client.scenes.listAll({ search: 'dungeon' });
    const scene = scenes[0];
    const buffer = await client.scenes.getDataFor(scene.id);
    
    // Save or process scene data
    const blob = new Blob([buffer]);
    import { PUClient } from 'pu-client';
    
    const client = new PUClient({ apiKey: process.env.PU_API_KEY! });
    
    const sceneData = await fetch('my-scene.glb').then(r => r.arrayBuffer());
    const preview = await fetch('scene-preview.png').then(r => r.arrayBuffer());
    
    const scene = await client.scenes.createOne({
      name: 'Castle Hall',
      description: 'Grand entrance hall',
      file: sceneData,
      preview,
    });
    
    console.log(`Scene ready: ${scene.id}`);

    AssetRecord Fields

    Common fields across asset types:

    FieldTypeDescription
    idstringAsset ID
    namestringDisplay name
    descriptionstring | nullDescription
    urlstring | nullDirect URL when available
    metadataRecord<string, unknown>Custom metadata
    created_atstringCreation timestamp
    updated_atstringLast update

    Relationship to Generations

    Many assets are produced by the generations pipeline. After a generation completes, its output (e.g. character ID, scene URL) becomes an asset you can fetch via these subclients.

    1. Create generation

    const id = await client.generations.create({
      type: 'character',
      input: { prompt: 'Steampunk fox', smartPrompt: true },
    });

    2. Poll until done

    let gen = await client.generations.get(id);
    while (gen.state !== 'done' && gen.state !== 'error') {
      await new Promise(r => setTimeout(r, 2000));
      gen = await client.generations.get(id);
    }

    3. Fetch via assets

    const charId = gen.output?.character_id;
    if (charId) {
      const char = await client.characters.getOne(charId);
      const data = await client.characters.getDataFor(charId);
    }

    Next Steps