Adding UGC Catalog Items to Players for RPG Character Customization

I’m making an RP game where I want to allow players during character customization to either

A) Input catalog IDs and be able to wear accessories obtained through that. (ex: the character customization of Neon District, Clear Skies Over Milwaukee, and Kingdom Life)


B) Select from pre-existing accessories in the game and be able to wear them via use of a ClickDetector (preferred) or onTouch. (ex: the character customization of Submarine Roleplay, some fashion games have a setup like this, and the Occupations theme in Super Simon Says)

I had some success inserting a model of a UGC item while testing on Studio, however I couldn’t find the model in the Workspace (where it had parented, according to the script I found) and gave up.

I’m clueless and really have no idea what I’m doing or where to start. Everywhere I’ve looked really doesn’t help with what I’m trying to accomplish, and I don’t know how else to word what I’m trying to do.

Sources differ on saying InsertService requires that you own the item or Roblox made it, but when I inserted the item mentioned previously, I didn’t own it. I’m not even sure if InsertService is what I want to use for this.
I specifically want users to be able to add any accessory from the UGC catalog to their character, think all those “Try On Catalog Clothing” or “Outfit Tester” games that let you do just that.

And no, I’m not talking about clothing like Shirts and Pants, I mean Accessories/Hats and Hair.

Help is appreciated.
I’m autistic and still relatively new to scripting, so please be patient with me. Thank you.


If it’s just intended to be simple character customisation with catalog items you might be interested in using HumanoidDescription. This can work for both allowing users to enter in their own ids or you setting up a list of buttons for users to select. In the latter case you will need to keep ids and the accessory type handy so you can match the id to the appropriate property.

The xAccessory propertie are CSVs (comma-separated values), so whenever you want to add an accessory there you will want to concatenate a new string there containing the id. Just as a rough example, here’s how you’d add The Raven to a user’s shoulder accessories:

local description = humanoid:GetAppliedDescription()
description.ShouldersAccessory = description.ShouldersAccessory .. ",20945145"

In terms of case B, that one I find is pretty simple and I love to use it when I want to add extra items to players. I set up an accessory that follows Roblox convention (AccessoryHandleAttachment, where Attachment has the same name of one in the character - for example, a hat will have HatAccessory or a hair will use HairAccessory) and then just call AddAccessory with it. This I find is the best for those click-to-wear-hat use cases.

Regarding inserting assets: when you call LoadAsset it returns a model but it doesn’t parent it anywhere meaning its a nil-parented asset, so you can’t find it because it doesn’t actually put itself anywhere. It loads in the asset and gives back a reference to the model that you can use later. For example, you can hold the reference in a variable so you can interact with it after loading it:

local hat = InsertService:LoadAsset(20945145)
hat.Parent = workspace -- NOW it exists in the Workspace

As far as you’re concerned with loading assets from the catalog, the assets you insert will always be wrapped in a model, so the hat is inside the model which is returned to the script after the LoadAsset call. In the case of the above example, suppose you want to directly add the hat:

local hat = InsertService:LoadAsset(20945145):GetChildren()[1]

This chains a few evaluations and at the end of them all gives back the result of those evaluations. For the sake of simplicity I omitted good practice with using LoadAsset and made some assumptions like that the id is valid but be sure to read the article so you can get some information on how to use it well. In English, this is what the above code does:

  1. Call LoadAsset, I want to load in an asset with the id 20945145.
  2. Now that I have a model, get all the children of it and then select the first one in that list.
    (LoadAsset on hats return a model with one child, so the first in the list is the accessory.)
  3. Give that first child back to me as the hat variable.

Your hat variable now holds a reference to the hat you loaded in, which you can do whatever you like with it like changing its parent.

In terms of your sources about InsertService, I do not believe this applies to the UGC hat catalog. There is a security and privacy restriction that only allows you to insert assets that you or Roblox own. That being said, UGC accessories still relatively new and previously only the Roblox account could upload hats to the site.

With the above in mind, it’s safe to say the security rule didn’t apply to certain asset types because it was always the assumption that only the Roblox account would upload those asset types and not users. I wouldn’t bank on this not being changed in the future but until then, InsertService is a great way to load and add accessories without knowing their type. HumanoidDescriptions on the other hand do require you to know the type.

Hope that’s a sufficient explanation.


Regarding HumanoidDescription, which seems to require the type of accessory, how would one go about obtaining that accessory type from the ID, so that the player could wear it? I’m thinking in the sense of, the player inputs the ID, but HumanoidDescription wouldn’t be able to add it to the player because it doesn’t know what type of accessory it is? Assuming off of legacy and language still used today, could that just be run through HatAccessory?

If I were to work a way in to allow players to input their own IDs, how would I even go about that?
I’d get making a GUI that allows players to input the ID text, button to click that would apply that text into a script (plugging those numbers into the Accessory CSV strings), then change the HumanoidDescription accordingly, but again I’m not sure how I’d do that if HumanoidDescription require accessory types.

Hope this makes sense?

You can feed an asset’s id through GetProductInfo and check if the AssetTypeId is 8 or 41-47 (see AssetType; 8 is for hats and 41-47 are for the other accessory types).

Given that GetProductInfo is internally a web call, you might want to consider caching the results in a table somewhere so that if the id is ever entered again by the same player or another, you don’t have to make the call again. You can just fetch the asset’s type from the script.

Here’s an example of how I might do it with a ModuleScript:

local MarketplaceService = game:GetService("MarketplaceService")

local IS_SERVER = game:GetService("RunService"):IsServer()

local ASSET_TYPE_ENUM = Enum.AssetType

local accessoryIds = {}
local cache = {}

-- Fill our accessoryIds table with accessory asset type ids
-- Forwards compatible in case values ever get shifted or changed
for _, enumItem in ipairs(ASSET_TYPE_ENUM:GetEnumItems()) do
	if not enumItem.Name:match("Accessory$") then continue end
	accessoryIds[enumItem.Value] = enumItem.Name

-- Manually add the Hat AssetTypeId
accessoryIds[ASSET_TYPE_ENUM.Hat.Value] = "HatAccessory"

--- Search our cache for the id and return the type or false if it's invalid.
-- If id doesn't exist in cache, make an info call.
local function getAssetType(id)
	-- Since we use false, we need to check if the entry is specifically
	-- non-nil to be able to return it. If we don't have that check, then
	-- false asset type ids will retry the calls.
	if cache[id] ~= nil then
        -- print("fetching from cache") -- Uncomment for debugging
		return cache[id]
    -- print("fetching from website") -- Uncomment for debugging

	local success, info = pcall(function ()
		return MarketplaceService:GetProductInfo(id)
	if success and info then
		local assetTypeId = info.AssetTypeId
		-- Cache the call's results as the asset type id if it exists or
		-- false if it's invalid. We cache false to prevent performing
		-- another web call on a previously invalid id.
		cache[id] = accessoryIds[assetTypeId] or false
		return cache[id]
		-- Provide a warning in the server console so you can debug an issue in
		-- the future if there's mass failures.
		if IS_SERVER then
            warn(debug.traceback("Issue loading an asset's info: " .. info))
		-- Return false anyway if success or info are false. User needs
		-- to know "either id is invalid or there was an issue loading this id".
		return false

return getAssetType

Then you can just test it out by requiring the module and calling it:

local foobar = require(game:GetService("ReplicatedStorage").GetAssetType)

-- First three calls represent the first time these assets are
-- entered into boxes in your experience.

-- Next three calls represent any other time a user enters the same
-- assets. These are much faster.

-- This represents a user entering an invalid id and how you can get back a
-- warning for debugging purposes.

-- This is an example for an actual use case.
local enteredId = 1029025
local assetType = getAssetType(enteredId)
-- We don't care about false or nil here, so just check if it's neither
if assetType then
    description[assetType] = description[assetType] .. tostring(enteredId)

I do think GetProductInfo is subject to some arbitrary call limit (100 requests per minute?) so do be mindful to warn the player if they’re entering ids too fast or their hats can’t be loaded for any reason. The cache is there to help reduce the amount of times GetProductInfo needs to be called because surely there will be more than one user who wants to wear a specific asset in a session’s lifetime.

After half an hour of script writing and testing with failure, I caved and searched around the Toolbox (risky I know), and found EXACTLY what I needed- it also works with shirts and pants, which is super cool, and it moves itself into the required places so you don’t have to.

@ewjadestinks 's ID Avatar editor! puts a neat little GUI in your game that players can put their IDs into. It runs off of InsertService, LoadAsset, and AddAccessory.
Phew. I got really worried for a second that I wasn’t going to be able to figure this out.
Can confirm- virus free, no funny business. Comes working right out the box.

Chat, I’m cooked.

I need something that works exactly the same but @ewjadestinks was banned and so their “ID Avatar editor!” model is lost to time.

Any archeologists nearby?