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.
Cosmetic images
Section titled “Cosmetic images”An item image is stored as a branding asset under branding/cosmetic-{type}-{id}/ and is refused unless it meets every rule below.
| Rule | Value |
|---|---|
| Format | PNG, APNG or WebP, detected from the bytes; SVG is refused |
| Size | At most 524288 bytes; ultra decorations at most 1228800 bytes |
| Width and height | 96 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.
In-front images
Section titled “In-front images”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.
| Part | Image | Size |
|---|---|---|
| closed | Still | At most 153600 bytes |
| open | 2 through 30 still frames | At most 65536 bytes each and 512000 bytes together |
| idle | Animated | At 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.
Ultra front upload object
Section titled “Ultra front upload object”| Field | Type | Description |
|---|---|---|
| closed | base64 string | The still image shown while the decoration is closed, left half |
| open | array[base64 string] | The still opening frames in order, left half, 2 to 30 of them |
| idle | base64 string | The animated loop played while the decoration is open, left half |
High-resolution profile effect layers
Section titled “High-resolution profile effect layers”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.
Effect layers upload object
Section titled “Effect layers upload object”| Field | Type | Description |
|---|---|---|
| intro | base64 string | The animated WebP intro, exported to play once |
| loop | base64 string | The animated WebP loop played after the intro |
| intro_ms | integer | The intro duration in milliseconds (250-30000) |
List cosmetic items
Section titled “List cosmetic items”GET/v1/admin/cosmetics/itemsReturns 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.
Response body
Section titled “Response body”The body is an array of cosmetic item objects.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The items were returned |
Side effects
Section titled “Side effects”The operation records one Admin audit entry with action list_cosmetic_items, target type cosmetic_item, target ID 0, and metadata key result_count.
Rate limit
Section titled “Rate limit”60 requests per minute for each authenticated user, on the admin:cosmetics:list bucket.
Create cosmetic item
Section titled “Create cosmetic item”POST/v1/admin/cosmetics/itemsAdds an item to the catalog and puts it on sale. Returns the cosmetic item object.
JSON body
Section titled “JSON body”| Field | Type | Description |
|---|---|---|
| type | string | The cosmetic item type |
| name | string | The display name of the item (1-64 characters) |
| description? | ?string | A short description (1-256 characters) |
| availability | string | The cosmetic availability |
| asset?1 | base64 string | The item image |
| model_key?2 | ?string | The 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
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | cosmetic item object | Item was created |
| 400 | error response | The body or the image fails validation |
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.
Import room character parts
Section titled “Import room character parts”POST/v1/admin/cosmetics/room-parts/importAdds 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.
Response body
Section titled “Response body”| Field | Type | Description |
|---|---|---|
| items | array[cosmetic item object] | The items created, then the faces made free, empty when nothing changed |
| skipped_faces | array[skipped face object] | The faces left unchanged because a shop bundle contains them |
Skipped face object
Section titled “Skipped face object”| Field | Type | Description |
|---|---|---|
| item_id | snowflake | The ID of the face item |
| bundle_ids | array[snowflake] | The IDs of the shop bundles that contain it |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | response body | The items created and repaired, and the faces skipped |
| 423 | error response | Another import is running, returned as GENERAL_ERROR with a Retry-After of 2 seconds |
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.
Update cosmetic item
Section titled “Update cosmetic item”PATCH/v1/admin/cosmetics/items/{item_id}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.
JSON body
Section titled “JSON body”Every field of Create cosmetic item except type, each optional, and the fields below. A null description clears it.
| Field | Type | Description |
|---|---|---|
| ultra_layout?1 | ?ultra layout object | The sizes of an ultra decoration, or null to go back to the fixed sizes |
| ultra_front?2 | ?ultra front upload object | Every in-front image, or null to draw the item behind the avatar again |
| on_sale?3 | boolean | True puts an item taken off sale back in the catalog |
| effect_layers?4 | ?effect layers upload object | Both 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
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | cosmetic item object | Item was updated |
| 400 | error response | The body or the image fails validation |
| 404 | error response | NOT_FOUND: no item has this ID |
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.
Take cosmetic item off sale
Section titled “Take cosmetic item off sale”DELETE/v1/admin/cosmetics/items/{item_id}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.
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 204 | none | Item was taken off sale |
| 404 | error response | NOT_FOUND: no item has this ID |
Side effects
Section titled “Side effects”The item’s availability becomes owned. The operation records one Admin audit entry with the action retire_cosmetic_item.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.
Admin user cosmetic object
Section titled “Admin user cosmetic object”| Field | Type | Description |
|---|---|---|
| item_id | snowflake | The ID of the item |
| owned | boolean | Whether the account owns the item now |
| equipped | boolean | Whether the account wears the item now |
| changed | boolean | False when the account already owned the item, or on a take back did not own it |
| unequipped | boolean | On a take back, whether the item was worn and has been taken off |
Give user a cosmetic item
Section titled “Give user a cosmetic item”POST/v1/admin/users/{user_id}/cosmetics/{item_id}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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the account |
| item_id | snowflake | The ID of the item |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | admin user cosmetic object | The account owns the item |
| 404 | error response | UNKNOWN_USER for an unknown or deleted account, or NOT_FOUND for an unknown item |
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.
Take cosmetic item back from user
Section titled “Take cosmetic item back from user”DELETE/v1/admin/users/{user_id}/cosmetics/{item_id}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.
Path parameters
Section titled “Path parameters”| Field | Type | Description |
|---|---|---|
| user_id | snowflake | The ID of the account |
| item_id | snowflake | The ID of the item |
Response
Section titled “Response”| Status | Body | Condition |
|---|---|---|
| 200 | admin user cosmetic object | The account no longer owns the item |
| 404 | error response | UNKNOWN_USER for an unknown account, or NOT_FOUND for an unknown item |
Side effects
Section titled “Side effects”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.
Rate limit
Section titled “Rate limit”30 requests per minute for each authenticated user, on the admin:cosmetics:manage bucket.