Skip to content
xivdyetools.app ↗

Character Equipment ​

2 endpoints · .chara import · 6 languages

Resolve the equipment model keys stored in an Anamnesis / Ktisis / Brio .chara file to in-game item names (six languages), icons, and their families of visually identical items. This is what powers Swatch Matcher → Dyes on this glamour in the web app.

Why model keys, not item IDs?

A .chara file never names the items a character wears — it stores each slot's ModelBase / ModelVariant (weapons add ModelSet), the lanes of the Item sheet's packed ModelMain. Those keys resolve deterministically, but the slot is a mandatory second key (one gear set shares a ModelMain across head/body/hands/legs/feet), and 35 % of keys are families of items that share one mesh (Augmented / Replica / +1 / role variants). This endpoint does that resolution once, server-side, and caches it per key.

POST /v1/chara/resolve ​

Resolve every worn piece of one character in a single call. The body is the twelve small integers below and nothing else — no names, no appearance data, no screenshot.

POST/v1/chara/resolveResolve .chara gear model keys to items.

Resolve .chara gear model keys to items.

Body · JSON
POST /v1/chara/resolve
STATUS · TIME · X-REQUEST-ID — after Send
// tap Send — nothing is fetched until you ask
FieldTypeNotes
geararray, required, ≤ 12One entry per worn slot. Empty slots (base 0) are rejected — send worn pieces only.
gear[].slotstringMainHand, OffHand, HeadGear, Body, Hands, Legs, Feet, Ears, Neck, Wrists, LeftRing, RightRing — each at most once
gear[].baseinteger 0–65535, requiredThe file's ModelBase
gear[].variantinteger 0–65535The file's ModelVariant (default 0)
gear[].setinteger 0–65535Weapon slots only — the file's ModelSet
glassesinteger 1–65535The file's Glasses.GlassesId (or bare Glasses integer). Omit or 0 for none.

Response ​

json
{
  "success": true,
  "data": {
    "version": "284bb7f44b9c0976",
    "items": {
      "HeadGear": {
        "itemId": 18085,
        "names": { "en": "Beech Mask of Casting", "ja": "ビーチキャスターマスク", "de": "Buchenmaske der Magie", "fr": "Masque d'incantateur en hêtre", "ko": "너도밤나무 마술사 가면", "zh": "山毛榉咏咒面具" },
        "iconId": 41716,
        "familySize": 1,
        "alternates": [],
        "viaMainHand": false,
        "acquisition": "Crafted (CRP Lvl. 61) / Norlaise - Ishgard - The Pillars (19,994 Gil)"
      },
      "Body": null,
      "MainHand": { "itemId": 49486, "names": { "en": "Runaway Bow", "…": "…" }, "iconId": 32065, "familySize": 1, "alternates": [], "viaMainHand": false, "acquisition": "Hell on Rails (Extreme)" },
      "OffHand":  { "itemId": 49486, "names": { "en": "Runaway Bow", "…": "…" }, "iconId": 32065, "familySize": 1, "alternates": [], "viaMainHand": true, "acquisition": "Hell on Rails (Extreme)" }
    },
    "glasses": { "id": 40, "names": { "en": "Black Rose-colored Spectacles", "…": "…" }, "iconId": 200018 }
  },
  "meta": { "requestId": "…", "apiVersion": "v1" }
}
FieldMeaning
versionThe XIVAPI game-version key the upstream answered with; null when the whole answer came from cache
items.<slot>Present for every requested slot. null = the key has no Item row (NPC-only / prop models) — show the raw key, it is not an error.
items.<slot>.itemIdItem sheet row — the lowest row_id of the family
items.<slot>.namesen / ja / de / fr always (soft hyphens stripped); ko / zh when the regional tables know the item — fall back to en per item when absent
items.<slot>.iconIdFor GET /v1/chara/icon/:iconId; null when the row has none
items.<slot>.familySizeRows sharing this (slot, key). 1 = unique. Every family member is visually identical — the file cannot tell them apart and neither can the game.
items.<slot>.alternatesThe other family members (row_id ascending, at most 8), each { itemId, names } plus its own acquisition when known. The lowest row of every rule set in rules is always among them, so a twin that passes the in-game check can always be named
items.<slot>.rulesWhat the game allows, as the family's distinct rule sets, lowest row first (so the first set holds itemId): each { itemIds, dyeCount, glamourable, wearMask, grandCompany }. dyeCount is the dye channels (0–2); glamourable is false on relic weapons, whose Replicas carry the look; wearMask has bit 2i for race i as a man and 2i + 1 as a woman, in EquipRaceCategory column order (Hyur, Elezen, Lalafell, Miqo'te, Roegadyn, Au Ra, Hrothgar, Viera), null when unknown; grandCompany is the company the piece is locked to, 0 for any. [] when the upstream answer lacked the fields. There is no job list: since patch 7.4 any job can wear any piece for glamour.
items.OffHand.viaMainHandtrue when the off-hand key is the main-hand item's own ModelSub (quiver, focus, card holder, fist pair…) or the main-hand key itself — the row is the main weapon. Genuine off-hands (shields) resolve on their own and say false.
items.<slot>.acquisitionWhere the named item comes from, as one English line in the GPOSERS glamour-submission format — e.g. Crafted (WVR Lvl. 92) / Independent Merchant - Urqopacha - Worlar's Echo (28,483 Gil). Omitted when unknown. Built after each patch from game data and Teamcraft's data files; describes itemId — each alternate carries its own.
glassesPresent only when the request carried glasses; null when the row does not exist

Names are never "cleaned": Augmented / Replica / +1 prefixes stay, because the naming is inconsistent across languages.

Caching ​

  • Each (slot, key) is cached at the edge for ~7 days, namespaced by the game-version pin, so twenty people importing the same glamour is one upstream XIVAPI search. An empty answer (no item row) is cached too.
  • X-Cache: HIT means no upstream call was made for this request; MISS means at least one key was fetched. (Not CORS-exposed — readable from servers, plugins and bots, not from browser code.)
  • The POST response itself is Cache-Control: no-store — caching happens per key behind it, not on the envelope.

Errors ​

StatuserrorWhen
400INVALID_BODYNot JSON / not an object
400VALIDATION_ERRORBad slot, lane out of 0–65535, duplicate slot, empty piece, bad glasses, more than 12 entries — the message names the field
413INVALID_BODYBody over 8 KB (enforced while the body streams in — Content-Length is not required)
503UPSTREAM_UNAVAILABLEXIVAPI is down, timed out, or re-indexing search after a game patch (details.upstreamStatus). Retry later; treat as "names unavailable", never as a failed import — the dyes in the file are unaffected.

GET /v1/chara/icon/:iconId ​

The item's icon as PNG (the 80 px _hr1 asset), proxied from XIVAPI and edge-cached.

GET/v1/chara/icon/:iconIdItem icon proxy (PNG).

Item icon proxy (PNG).

Parameters · path
From items.<slot>.iconId or glasses.iconId — canonical decimal, 1–999999 (no leading zeros, sign or trailing characters)
GET /v1/chara/icon/41716
STATUS · TIME · X-REQUEST-ID — after Send
// tap Send — nothing is fetched until you ask

Returns image/png (always — the upstream content type is never reflected; a 2xx upstream body that is not a PNG is refused) with Cache-Control: public, max-age=2592000, immutable, Content-Disposition: inline, Content-Security-Policy: sandbox and an X-Cache header (HIT / MISS). 041716 and 41716abc are 400 VALIDATION_ERROR; 404 NOT_FOUND when the upstream has no such asset; 503 UPSTREAM_UNAVAILABLE when XIVAPI is down, answers with something other than a PNG, or exceeds the 1 MB ceiling. A missing icon should cost the tile, not the row.

Model key packing (for reference) ​

The request carries the raw lanes, but if you want to compute keys yourself (they are what the cache is keyed on):

armour / accessory  ModelMain = base | variant << 16
weapon              ModelMain = set  | base << 16 | variant << 32   (exceeds 2^32 — use BigInt)

HeadGear {361, 5} → 328041 → Item #18085 Beech Mask of Casting. MainHand {634, 19, 1} → 4296213114 → Item #49486 Runaway Bow; its OffHand {698, 149, 1} packs to that item's ModelSub, which is why it resolves through the main hand. @xivdyetools/core exports gearModelKey, weaponModelKey and charaModelKey for this.

Languages ​

en / ja / de / fr come from XIVAPI v2 in the same call. Korean and Chinese come from build-time tables generated from the community regional datamining exports (scripts/build-item-names.mjs — equippable rows only, same Item row IDs as global). The regional clients can lag a patch by weeks or months, so a brand-new item may have no ko / zh key for a while; fall back to en.

FINAL FANTASY XIV © 2010-2026 SQUARE ENIX CO., LTD. All Rights Reserved.
XIV Dye Tools is a fan-made application and is not affiliated with or endorsed by Square Enix. · Discord ↗