Skip to content
Micup API

Cosmetics

The cosmetic catalog holds the avatar decorations, ultra decorations, profile effects and nameplates accounts can wear. Cosmetics defines the item object and wearing. Every route on this page requires cosmetics:manage. An admin can also give an item to an account or take it back, which is how an owned item reaches an account outside the shop and item codes.

An item image is stored as a branding asset under branding/cosmetic-{type}-{id}/ and is refused unless it meets every rule below.

RuleValue
FormatPNG, APNG or WebP, detected from the bytes; SVG is refused
SizeAt most 524288 bytes; ultra decorations at most 1228800 bytes
Width and height96 through 480 pixels each

An image that breaks a format or dimension rule fails with 400 INVALID_FORM_BODY and the validation code INVALID_IMAGE_FORMAT on asset. For an otherwise valid image, a frame, nameplate or profile effect above 524288 bytes returns IMAGE_SIZE_EXCEEDS_LIMIT if its base64 payload stays within 1638400 characters. A payload above that character limit, including every ultra decoration above 1228800 bytes, returns BASE64_LENGTH_INVALID.

An ultra decoration can also carry the images a client draws in front of the avatar and opens on interaction, as the ultra front object describes. They are sent all at once as the ultra front upload object in ultra_front on Update cosmetic item. Each part is the left half of the decoration and follows the format rule of the item image, with these rules in place of its size and dimension rules.

PartImageSize
closedStillAt most 153600 bytes
open2 through 30 still framesAt most 65536 bytes each and 512000 bytes together
idleAnimatedAt most 1500000 bytes

Every part is 96 through 576 pixels tall and exactly twice as tall as it is wide. The first opening frame should be the closed image, since a client shows it as the opening starts; the server does not check this.

The item image in asset keeps the item image rules and stays the full fallback: an older client draws it behind the avatar, and so does a client that cannot read the item. Make it the whole closed decoration, both halves.

A part that breaks the format, height or shape rule, a closed image or opening frame that moves, and a loop that does not move each fail with 400 INVALID_FORM_BODY and the validation code INVALID_IMAGE_FORMAT at path ultra_front.closed, ultra_front.open.{index} or ultra_front.idle. The base64 payload of each part is capped at the characters its size needs: 204800 for closed, 87384 for each opening frame and 2000000 for idle. A part above its size therefore returns BASE64_LENGTH_INVALID at its path, except an opening frame of 65537 or 65538 bytes, which fits its cap and returns IMAGE_SIZE_EXCEEDS_LIMIT. Opening frames above 512000 bytes together return IMAGE_SIZE_EXCEEDS_LIMIT at path ultra_front.open before any image is stored. Fewer than 2 or more than 30 opening frames return 400 INVALID_FORM_BODY at path ultra_front.open.

The request stores closed, the opening frames in order, idle and then asset, reads the item again and writes it once, so a change made while the images upload, such as taking the item off sale, is kept. If one part fails, or the item cannot be written, none are kept: every image the request stored is deleted again, except an image the item already uses, and the item does not change.

FieldTypeDescription
closedbase64 stringThe still image shown while the decoration is closed, left half
openarray[base64 string]The still opening frames in order, left half, 2 to 30 of them
idlebase64 stringThe animated loop played while the decoration is open, left half

A profile effect can carry an animated intro and loop as the effect layers object. Send both images together in effect_layers on Update cosmetic item, after creating the item. Create does not accept the layers. The main image in asset keeps the item image rules and remains the fallback.

Both layers must be animated WebP, with each side 128 through 1024 pixels. Each layer is at most 3000000 bytes, and the two together are at most 4500000 bytes. Export the intro to play once and give its duration as an integer from 250 through 30000 milliseconds; the client then plays the loop.

A layer that is not animated WebP or breaks the dimension rules returns 400 INVALID_FORM_BODY with validation code INVALID_IMAGE_FORMAT at effect_layers.intro or effect_layers.loop. Each base64 payload is capped at 4000000 characters; a payload above that limit returns BASE64_LENGTH_INVALID at its layer path. Layers above 4500000 bytes together return IMAGE_SIZE_EXCEEDS_LIMIT at effect_layers before any image is stored. A missing layer fails body validation at its layer path. A non-integer or out-of-range duration fails body validation at effect_layers.intro_ms.

The request stores the intro and then the loop before the main image. If an upload or the item write fails, newly stored images are removed, except those the item already uses.

FieldTypeDescription
introbase64 stringThe animated WebP intro, exported to play once
loopbase64 stringThe animated WebP loop played after the intro
intro_msintegerThe intro duration in milliseconds (250-30000)
GET/v1/admin/cosmetics/itemsAudit reason

Returns every item in ascending ID order, which is creation order, including items taken off sale. An item taken off sale reads owned as its availability.

The body is an array of cosmetic item objects.

StatusBodyCondition
200response bodyThe items were returned

The operation records one Admin audit entry with action list_cosmetic_items, target type cosmetic_item, target ID 0, and metadata key result_count.

60 requests per minute for each authenticated user, on the admin:cosmetics:list bucket.

POST/v1/admin/cosmetics/itemsAudit reason

Adds an item to the catalog and puts it on sale. Returns the cosmetic item object.

FieldTypeDescription
typestringThe cosmetic item type
namestringThe display name of the item (1-64 characters)
description??stringA short description (1-256 characters)
availabilitystringThe cosmetic availability
asset?1base64 stringThe item image
model_key?2?stringThe character model or preset of a room character, or the part of a room character part

1 Required for every type except the room_<slot> parts, which show the preview of their part manifest without one. A missing image returns 400 INVALID_FORM_BODY at path asset

2 Required for a room_character, a ready-made outfit (preset) of the part manifest this server version ships, and for a room_<slot> part, a part of that slot in the part manifest this server version ships. Any other type returns 400 INVALID_FORM_BODY at path model_key when it is set

StatusBodyCondition
200cosmetic item objectItem was created
400error responseThe body or the image fails validation

The operation records one Admin audit entry with the action create_cosmetic_item and the target type cosmetic_item. Its metadata has type and availability.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.

POST/v1/admin/cosmetics/room-parts/importAudit reason

Adds one item for every part of the room character part manifest that no item uses yet, taken off sale or not, and puts it on sale. Each new item is a room_<slot> item of the part’s slot without an image and without a price, named after its key: a face is free, and every other slot owned. The import also repairs the faces of the manifest that items already use: a face item becomes free and loses its standalone price, unless a shop bundle contains it, in which case it stays as it is and is reported in skipped_faces. Names and sale membership stay. A second run adds nothing. Prices are set with Set shop prices, which needs SHOP_MANAGE; an item without a price is only sold inside a bundle.

FieldTypeDescription
itemsarray[cosmetic item object]The items created, then the faces made free, empty when nothing changed
skipped_facesarray[skipped face object]The faces left unchanged because a shop bundle contains them
FieldTypeDescription
item_idsnowflakeThe ID of the face item
bundle_idsarray[snowflake]The IDs of the shop bundles that contain it
StatusBodyCondition
200response bodyThe items created and repaired, and the faces skipped
423error responseAnother import is running, returned as GENERAL_ERROR with a Retry-After of 2 seconds

The operation records one Admin audit entry with the action import_room_character_parts, target type cosmetic_item, and target ID 0, even when the import stops half way. Its metadata has created_count and created_ids, and, when there are any, updated_count and updated_ids for the faces made free, skipped_faces as JSON, and removed_price_skus for the standalone prices removed.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.

PATCH/v1/admin/cosmetics/items/{item_id}Audit reason

Changes the name, description, availability, image, ultra decoration sizes, in-front images or profile effect layers of an item, or puts an item taken off sale back in the catalog. Returns the updated cosmetic item object.

Every field of Create cosmetic item except type, each optional, and the fields below. A null description clears it.

FieldTypeDescription
ultra_layout?1?ultra layout objectThe sizes of an ultra decoration, or null to go back to the fixed sizes
ultra_front?2?ultra front upload objectEvery in-front image, or null to draw the item behind the avatar again
on_sale?3booleanTrue puts an item taken off sale back in the catalog
effect_layers?4?effect layers upload objectBoth HD layers, or null to play the main image again

1 Only an ultra decoration takes it, and any other type returns 400 INVALID_FORM_BODY at path ultra_layout. All three surfaces are required, each null for its fixed size. An omitted ultra_layout keeps the sizes

2 Only an ultra decoration takes it, and any other type returns 400 INVALID_FORM_BODY at path ultra_front. The whole set is replaced at once under the in-front image rules. An omitted ultra_front keeps the images

3 Only true is accepted. The item goes back with the availability it has, which is owned after Take cosmetic item off sale, unless availability is sent too. An item on sale stays on sale, and an omitted on_sale keeps the item on or off sale

4 Only a profile effect takes it; every other type returns 400 INVALID_FORM_BODY at effect_layers. Both layers and the duration are replaced together under the profile effect layer rules. An omitted effect_layers keeps the layers; null removes them without changing asset

StatusBodyCondition
200cosmetic item objectItem was updated
400error responseThe body or the image fails validation
404error responseNOT_FOUND: no item has this ID

New sizes, in-front images and profile effect layers reach other clients within 60 seconds through List cosmetic items by ID, since each API process keeps an item that long, and a client that already drew the item uses them from its next session.

The operation records an Admin audit entry with the action update_cosmetic_item. When on_sale is sent, the action is relist_cosmetic_item, the counterpart of retire_cosmetic_item, and other fields sent with it add a second entry with update_cosmetic_item. The metadata of each entry has fields, the changed field names including ultra_layout, ultra_front, effect_layers and on_sale, and availability.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.

DELETE/v1/admin/cosmetics/items/{item_id}Audit reason

Removes an item from the catalog without deleting it. Accounts that wear it keep showing it, and accounts that own it can still put it on; no other account can. The image stays stored. Update cosmetic item with on_sale puts it back.

StatusBodyCondition
204noneItem was taken off sale
404error responseNOT_FOUND: no item has this ID

The item’s availability becomes owned. The operation records one Admin audit entry with the action retire_cosmetic_item.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.

FieldTypeDescription
item_idsnowflakeThe ID of the item
ownedbooleanWhether the account owns the item now
equippedbooleanWhether the account wears the item now
changedbooleanFalse when the account already owned the item, or on a take back did not own it
unequippedbooleanOn a take back, whether the item was worn and has been taken off
POST/v1/admin/users/{user_id}/cosmetics/{item_id}Audit reason

Adds an item to the items an account owns, without wearing it. Returns the admin user cosmetic object. Giving an item the account already owns changes nothing.

FieldTypeDescription
user_idsnowflakeThe ID of the account
item_idsnowflakeThe ID of the item
StatusBodyCondition
200admin user cosmetic objectThe account owns the item
404error responseUNKNOWN_USER for an unknown or deleted account, or NOT_FOUND for an unknown item

The operation records one Admin audit entry with action grant_cosmetic_item, target type user, the account ID as target ID, and metadata keys item_id and changed. It emits no Gateway Dispatch.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.

DELETE/v1/admin/users/{user_id}/cosmetics/{item_id}Audit reason

Removes an item from the items an account owns, and takes it off when the account can no longer wear it. Returns the admin user cosmetic object. A free item, or a premium item on an account with premium, stays on.

FieldTypeDescription
user_idsnowflakeThe ID of the account
item_idsnowflakeThe ID of the item
StatusBodyCondition
200admin user cosmetic objectThe account no longer owns the item
404error responseUNKNOWN_USER for an unknown account, or NOT_FOUND for an unknown item

A pending delivery of the item to the account, from a shop order or an item code, is cancelled first. The item therefore never comes back from the same order or code, even when that delivery is retried. The account row is read again after the take-off, so a wear request that raced the take back is undone too.

Taking a worn item off sends User Update to the account’s sessions. For an avatar decoration, ultra decoration or nameplate it also sends Guild Member Update to every guild the account is in.

The operation records one Admin audit entry with action revoke_cosmetic_item, target type user, the account ID as target ID, and metadata keys item_id, changed and unequipped.

30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.