Every message on the mesh is one of eleven payload types, discriminated on the type field. For how to authenticate and open a stream in the first place, see Connecting to the IRC.
type | Purpose |
|---|---|
chat | A chat line, heard by the whole channel |
death | Player died |
entity_owner | Associates an entity id with a player |
keepalive | Client liveness signal |
login | Player joined the channel's server |
logout | Player left, with a final snapshot |
message | A direct message — chat delivered to exactly one player |
minigame | Freeform minigame state |
ping | A world ping at a position |
presence | Full-state snapshot of a player: inventory and vitals |
resend_request | Asks others to re-announce state |
Unknown type values are rejected at ingress with a 400, not silently dropped.
Sending as another user
Ordinarily the sender of a POST /irc payload is the authenticated account behind the request — the mesh stamps the meta frame's uuid from your Mojang session, and the payload is spoken as you.
A key holding the irc.connection.unauthenticated scope can instead post a re-attribution envelope: an ordinary payload wrapped in an explicit sender. It connects and posts with no Authorization header at all — the API key alone — and names the sender in the body:
{
"user": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"agent": "my-bridge/1.0",
"host": "mc.hypixel.net",
"message": { "type": "chat", "message": "spoken as that user" }
}| Field | Type | Notes |
|---|---|---|
user | string | A UUID. Becomes the delivered meta frame's uuid. |
agent | string | Becomes the delivered meta frame's agent. |
host | string | Optional. The multiplayer server this message is for. Omitted, it falls back to the x-irc-multiplayer-server header — so a bridge that names it needs no header at all. channel is accepted as this field's legacy spelling; host wins when both are present. |
message | object | Any payload above — validated exactly as if sent bare. |
The envelope has no type of its own, which is what distinguishes it from a payload; the two shapes may be mixed freely in a batch. It is honoured only for a key holding irc.connection.unauthenticated — for anyone else it is silently dropped, so it is never an identity-spoofing primitive. The inner payload's type is still checked against the key's send scopes, exactly as if it were sent bare: a bridge narrowed to irc.messages.send.presence relays presence and nothing else, whoever it speaks as. On the wire, the delivered message is indistinguishable from one the named user sent themselves.
Because each envelope carries its own host, one POST can fan out to several multiplayer servers at once — send a batch whose entries name different hosts, and each is delivered to the clients on the server it names.
The same scope also lets a stream cross the direct-message privacy boundary: DMs addressed to other players are delivered to it, where an ordinary stream only ever hears its own. Which payload types it is delivered at all remains its receive scopes' question — a bridge that should hear the whole mesh holds plain irc.messages.receive alongside the connection grant; one narrowed to irc.messages.receive.presence hears presence and keepalives, nothing else. Together these are the bridge / network-operator grant; it is never given to a game client.
Common shapes
A few object shapes recur across the payload types. They are documented once here; the per-type pages link back.
The meta frame
Every delivered payload is preceded by a meta SSE event describing who sent it and where:
{
"uuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"agent": "hackware/1.2.3+abc123",
"channel": "ingress.2b2t.org"
}| Field | Type | Notes |
|---|---|---|
uuid | string | The sender's UUID — the authenticated account, or the user a re-attribution envelope named. |
agent | string | The sender's user agent. May be null. |
channel | string | The readable channel (multiplayer server) the message was delivered on. |
Player
Identifies a player wherever one appears (player, owner), in one of two shapes depending on who the payload is about.
A payload about the sender carries a bare UUID string (ping) — or no player field at all where the reference would be pure redundancy (chat, message, presence):
"069a79f4-44e9-4726-a5be-fca90e38aaf5"There is no display name in these: every frame is delivered behind a meta event whose uuid field is the authenticated sender's uuid. Display names are the receiver's to resolve — every sender on a channel is on the same multiplayer server, so the tab list has them.
A payload about a third party — login, logout, death, entity_owner, all reports about players the sender observed — carries the full object, because the name is payload data the meta frame cannot supply:
{
"uuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5",
"name": "Notch"
}| Field | Type | Constraints |
|---|---|---|
uuid | string | A UUID. Identity lives here — coalescing, eviction, and echo attribution all key on it. |
name | string | Mojang-shaped: 3–16 characters, [A-Za-z0-9_]. |
Position
A location in a world:
{
"x": 1024.5,
"y": 64,
"z": -2048.5,
"dimension": "overworld"
}| Field | Type | Constraints |
|---|---|---|
x, z | number | Within ±30,000,000 |
y | number | ≥ −64 |
dimension | string | overworld, nether, or end — case-insensitive at ingress, lowercased in transit |
logout extends this with the player's posture:
| Field | Type | Constraints |
|---|---|---|
phase | string | standing, sneaking, or swimming |
pitch | number | −90 to 90 |
yaw | number | Unbounded |
Inventory slot
An inventory is an array of slots, each pairing a slot index with an item stack:
{
"slot": 0,
"item": {
"id": "minecraft:netherite_sword",
"count": 1
}
}| Field | Type | Constraints |
|---|---|---|
slot | integer | ≥ 0 |
item | object | An item stack |
Item stack
Mirrors what Minecraft's ItemStack serializes to. Required fields are always present; optional fields are omitted when not applicable:
| Field | Type | Notes |
|---|---|---|
id | string | Registry id, e.g. minecraft:diamond_pickaxe |
count | integer | Stack size, ≥ 1 |
damage | integer | Current damage (0 = pristine). Damageable items only. |
maxDamage | integer | Durability ceiling. Damageable items only. |
enchantments | array | { "id": string, "level": int ≥ 1 } per enchantment |
name | string | Display name (custom or translated) |
nbt | object | Arbitrary extra NBT/component data |
Timestamps
Every timestamp field is the sender's clock in epoch milliseconds. The mesh relays them untouched and never interprets them — ordering on the wire comes from the stream, not from these values.