Two files, split by what they answer:
menus.yml β where things sit. Titles, rows, slots, dialog labels.items.yml β how things look. Material, name, lore.Every button and every item takes the same fields, so learn them once.
send-mail:
slot: 48
item: FEATHER
name: "<#f2c42f>Send mail"
lore:
- "<#E9FDFB>Write a letter and attach items."
amount: 1
glow: false
| Field | Notes |
|---|---|
item | A material, or a custom-item id β see Custom Items. material: is an alias |
name / lore | MiniMessage or legacy codes |
amount | Stack size |
glow | Enchant shimmer without the enchantment |
item-model | Vanilla item model override |
flags | Item flags to hide |
hide-tooltip | Hide the tooltip entirely |
inbox:
title: "<dark_gray><b>Mail"
inspect-title: "<dark_gray><b>%player%" # /mail admin
rows: 6
filler-item: BLACK_STAINED_GLASS_PANE
buttons:
send-mail: { slot: 48, ... }
claim-all: { slot: 49, ... } # %count%
clear-mail: { slot: 50, ... }
prev-page: { slot: 45, ... } # %page% %pages%
next-page: { slot: 53, ... }
rows is the whole screen. mail.inbox-rows-per-page from config.yml decides how many of them hold mail; the last one is always the control bar.
compose:
title: "<dark_gray><b>New mail"
admin-title: "<dark_gray><b>Server mail"
rows: 5
item-slots: [12, 13, 14, 21, 22, 23, 30, 31, 32]
filler-item: BLACK_STAINED_GLASS_PANE
write-letter-button: { slot: 40, ... }
item-slots must hold exactly mail.max-items-per-letter entries. Only these slots accept items; everything else is locked, including drags and shift-clicks that would land outside them.
dialogs:
compose:
title: "<#f2c42f><b>Write a letter"
fields:
recipient: { label: "Player" }
subject: { label: "Subject" }
message: { label: "Message", multiline: true }
money: { label: "Money" }
anonymous: { label: "Send anonymously" }
buttons:
confirm: "<#95d027>Send"
cancel: "<#eb4b30>Cancel"
Three dialogs: compose, compose-admin (no recipient, no anonymous β the audience came from the command) and read-letter.
read-letter is what opens when a player right-clicks a letter item, with %subject%, %sender% and %message%.
The record shown inside the inbox. It never leaves the menu.
| Entry | Used for |
|---|---|
pending / claimed | Player mail |
pending-server / claimed-server | Server mail |
pending-<name> / claimed-<name> | A server-mail entry that names <name> with mail-item |
A missing variant falls back to the plain pair, so you never have to define all of them.
Available in every name and lore:
%subject% %sender% %sent% %claimed% %expires% %money%
%sender% reads Anonymous for an anonymous mail β except in /mail admin, where it reads
Anonymous (Steve). Both wordings are yours:
text:
sender-anonymous: "Anonymous"
sender-anonymous-admin: "Anonymous (%player%)"
%contents% is special. On its own lore line it expands to one row per attachment:
lore:
- "<#E9FDFB>Contents"
- "%contents%"
Item names are rendered by the client, not the server. A vanilla item's name is a translation key rather than a word, and it is passed through untouched β so a player running the game in Spanish reads Diamante and one running it in Russian reads ΠΠ»ΠΌΠ°Π·, from the same config line.
The rows themselves are configurable:
contents:
item: "<#f3842c> β β’ <#b6c1ec>%item%"
letter: "<#289bd0> β β’ <#b6c1ec>A letter"
money: "<#95d027> β β’ <#b6c1ec>%amount%"
| Section | What |
|---|---|
text | "Anonymous", "Never", and the %expires% units (d, h, m, s) |
inspect-entry | Lore used in /mail admin, with %state% and %recipient% |
letter-item | The item handed over when letter-type: ITEM |
letter-book | The book written when letter-type: BOOK |