Hooks
Hook events are pushed to client mods over the plugin channel and to backend apps over webhooks. Every event is gated by a backing read scope, and grouped here by who can see it.
Event envelope
Webhooks receive every hook event in this envelope. Only data changes per event type.
{
"id": "evt_01J...",
"type": "player.enchant_proc",
"version": 1,
"createdAt": "2026-06-04T00:00:00.000Z",
"serverScope": "aether",
"producer": { "kind": "minecraft_server", "id": "aether" },
"actor": { "playerId": "00000000-0000-0000-0000-000000000000" },
"subject": { "playerId": "00000000-0000-0000-0000-000000000000" },
"visibility": "self",
"requiredScopes": ["hooks.player.enchant_proc:read"],
"data": { /* event-specific fields */ }
}idstringGlobally unique, stable id. Delivery is at least once, so dedupe on this.
typestringThe event type from the catalog below, e.g. player.enchant_proc.
versionnumberPayload contract version. Branch on it when a payload shape changes.
createdAtISO 8601When the event occurred.
serverScopestringWhich server it happened on, e.g. aether or celestial.
producerobjectWhat emitted the event, as { kind, id }.
actorobjectWho caused it, with a playerId when a player is known.
subjectobjectWho or what the event is about — a player, gang, or the server.
visibilitystringself, gang, server, or app owner — who is allowed to receive it.
requiredScopesstring[]The scopes that gate this event.
dataobjectEvent-specific payload. Some events pass runtime data through unchanged — read it defensively.
On the plugin channel, the event arrives inside an event message. The same fields webhooks get in data sit at payload.payload. When that data is mock, the message also carries testingData: true.
{
"type": "event",
"event": "hook",
"sessionId": "sess_01J...",
"eventType": "player.enchant_proc",
"payload": {
"id": "evt_1042",
"type": "player.enchant_proc",
"eventType": "player.enchant_proc",
"version": 1,
"subjectType": "player",
"serverScope": "aether",
"occurredAt": "2026-06-04T00:00:00.000Z",
"playerId": "00000000-0000-0000-0000-000000000000",
"playerName": "Steve",
"payload": { /* the same fields webhooks get in data */ }
}
}Delivery on the plugin channel
Hook events have their own packet budget per player, apart from HUD, overlay and marker traffic.
Today the budget refills 20 tokens a second and holds up to 40. A packet costs one token plus one per KiB.
A packet over budget is dropped, not queued or sent again. The game counts drops on your session as droppedMessages, which GET /v1/cosmic-api/runtime/servers/:serverScope/mod-sessions/self returns with mod.sessions.same_mod:read.
After a gap, read the matching endpoint to catch up. Webhooks do not share this budget.
Your player
Events about your own player, delivered only to the player who granted access.
enchantIdstringThe enchant id, e.g. powerball.
displayNamestringName and level as the game shows them, color codes included.
levelnumberThe enchant level.
sourcestringWhat set it off: mining, combat or manual. manual is a Powerball launched by a click. Some procs add a detail after a colon, such as combat:buff or mining:chain, so match the part before it.
cooldownRemainingMsnumbermanual only. The cooldown the launch started, in milliseconds.
causestringProcs from an attack only. The damage cause, e.g. ENTITY_ATTACK.
targetId / targetTypestringThe entity you hit, when known. targetType is the entity type, e.g. PLAYER.
ticknumberThe server tick of the proc.
{
"enchantId": "powerball",
"displayName": "&9Powerball §b§lIII",
"level": 3,
"source": "manual",
"cooldownRemainingMs": 45000,
"tick": 1843021
}Cooldowns changed
player.cooldowns.changedFires when your cooldowns change.
player.cooldowns:readEffects changed
player.effects.changedFires when your active effects change.
player.effects:readAbsorber used
player.absorber.usedFires when your absorber is used.
hooks.player.absorber:readCommand succeeded
player.command.succeededFires when one of your approved commands runs.
hooks.player.command:readPet changed
player.pet.changedFires when your active pet changes.
player.pets:readTrinket changed
player.trinket.changedFires when your trinkets change.
player.trinkets:readchannelstringglobal, gang, truce or ad.
previousChannelstring | nullThe channel before. null on the session event.
gangId / gangTitlestringYour gang, or empty strings when you have none.
reasonstringcommand, truce_ended, normalized, or session when your session resolves.
The session event means a mod that connects mid-game learns the current channel without waiting for a change.
{
"previousChannel": null,
"channel": "gang",
"gangId": "00000000-0000-0000-0000-000000000002",
"gangTitle": "Wolves",
"reason": "session"
}Bandit killed
bandit.killedFires when you kill a bandit.
hooks.bandit.kill:readLibrary changed
player.library.changedFires when your library, or a library shared with you, gains or loses items.
player.library:readLibrary access changed
player.library.access.changedFires when a library share you own or receive is added, changed or removed.
player.library:readVault contribution changed
player.vault.contribution.changedFires about once a second while your damage or rank in a mine vault raid changes, while you are in the top 25.
events:readEmblem changed
player.emblem.changedFires when your equipped emblem, its tier or your buffs toggle changes.
player.emblems:readEmblem progress changed
player.emblem.progress.changedFires at most every five seconds while your emblem progress changes.
player.emblems:readEmblem unlocked
player.emblem.unlockedFires when you unlock an emblem tier.
player.emblems:readTop credits changed
player.top_credits.changedFires when your Top Credit balance changes.
player.top_credits:readbagIdstringThe backpack's id.
slotnumberThe inventory slot it sits in.
level / capacitynumberBackpack level and how many satchels it holds.
usedSlotsnumberSatchels stored now.
energy / maxEnergynumberThe backpack's energy.
revisionnumberGoes up with each change to the bag.
storedSatchelsobject[]Every satchel inside, in the satchel row shape, with slot as the place in the bag.
There is no need to read the satchels endpoint again. A busy bag sends its latest state at most once a second.
{
"bagId": "00000000-0000-0000-0000-000000000001",
"slot": 8,
"level": 2,
"usedSlots": 1,
"capacity": 10,
"energy": 120,
"maxEnergy": 300,
"revision": 57,
"storedSatchels": [
{ "slot": 0, "type": "iron_ore_satchel", "material": "IRON_ORE", "name": "Iron Ore Satchel", "amount": 1, "level": 1,
"energy": 40, "maxEnergy": 100, "ore": "IRON", "refined": false, "tier": "regular", "count": 300, "capacity": 1000 }
]
}Custom set changed
player.custom_set.changedFires when you complete or break a Death or Greed set.
player.custom_sets:readPayday changed
player.payday.changedFires when your Greed set payday starts or ends.
player.custom_sets:readDeath mark changed
player.death_mark.changedFires when a death mark is placed on you, cleared or expires.
player.custom_sets:readGround Zero points changed
player.ground_zero.points.changedFires about once a second while your Ground Zero points change.
player.ground_zero:readenchantIdstringThe armor enchant that answered your hit, or valor_mask_dodge when a Valor Mask dodged it.
displayNamestringName and level as the game shows them.
levelnumberThe enchant level.
sourcestringAlways combat.
causestring | nullThe damage cause of your hit, e.g. ENTITY_ATTACK or PROJECTILE.
targetIdstringThe player you hit.
targetNamestringTheir name.
targetTypestringAlways PLAYER.
ticknumberThe server tick. Repeats of one enchant on one target in a tick merge into one event.
{
"enchantId": "maneuver",
"displayName": "&fManeuver §bII",
"level": 2,
"source": "combat",
"cause": "ENTITY_ATTACK",
"targetId": "00000000-0000-0000-0000-000000000001",
"targetName": "Alex",
"targetType": "PLAYER",
"tick": 1843090
}reasonstringmining, sell, store or extract.
revisionnumberGoes up by one per event and starts over when the player rejoins. A gap means you missed one.
changedobject[]The full row of each touched slot that still holds a satchel, shaped like data.satchels[] on the satchels read.
removedSlotsnumber[]Touched slots that no longer hold a satchel.
The rows are complete, so apply them as they come. There is no need to read the satchels endpoint again.
{
"reason": "mining",
"revision": 418,
"changed": [
{ "slot": 3, "type": "diamond_ore_satchel", "material": "DIAMOND_ORE", "name": "Diamond Ore Satchel", "amount": 1, "level": 2,
"energy": 80, "maxEnergy": 200, "ore": "DIAMOND", "refined": false, "tier": "regular", "count": 1250, "capacity": 5000 }
],
"removedSlots": []
}Every field of data.wallet on the balances read, plus:
changedstring[]The fields that moved since the last event.
balanceDeltanumberHow much balance moved since the last event. 0 on the session event.
reasonstringsession on the first event after your session resolves. That event carries every field. Absent otherwise.
The game compares your balances once a second and sends only when one moved. A new balanceRevision alone sends nothing.
{
"balance": 1262500.5,
"balanceRevision": 813,
"seasonalCosmicCoins": 340,
"itemFlipCredits": 12,
"giftCred": 5,
"bloodDiamonds": 18,
"bloodDiamondsPeriodEndsAt": "2026-10-12T00:00:00Z",
"changed": ["balance"],
"balanceDelta": 12500
}Every field of data.progression on the progression read, plus:
reasonstringprestige, level_up, skill_tree or xp. session on the first event after your session resolves. That event carries every field.
experienceDeltanumberExperience gained since the last event. 0 on the session event.
{
"level": 64,
"experience": 18420.5,
"experienceToNextLevel": 39830,
"levelCap": 100,
"prestige": 2,
"prestigePerks": [ { "id": "level_101", "unlocked": false } ],
"skillTree": { "availablePoints": 1, "totalPoints": 12, "spentPoints": 11, "freeResetsRemaining": 1,
"perks": [ { "tree": "prospector", "key": "ore_duplication", "level": 3, "maxLevel": 3 } ] },
"reason": "xp",
"experienceDelta": 170
}Every field of data.state on the state read except sidebar, plus:
changedstring[]The fields that moved since the last event. Empty on the session event.
reasonstringsession on the first event after your session resolves. That event carries every field. Absent otherwise.
Energy moves as you mine, so expect about one event a second while mining.
{
"planet": "aether",
"world": "prison",
"dimension": "main",
"zone": "diamond",
"regions": ["mine_diamond"],
"mine": { "region": "mine_diamond", "ore": "diamond" },
"combatTag": { "active": false, "expiresAt": null },
"boosters": [],
"energy": { "current": 12620, "max": 50000, "chargePercent": 25, "pickaxeLevel": 42 },
"changed": ["energy"]
}layoutstringtoggles, pickaxe, or the board an area shows, such as boss or koth.
compactbooleanWhether compact mode is on.
componentsobject[]Every sidebar line as key, enabled and order.
reasonstringsession on the first event after your session resolves. Absent otherwise.
Line values are not sent. Read them from sidebar.values on the state read, or with the player.state.read action.
{
"layout": "pickaxe",
"compact": false,
"components": [
{ "key": "current_zone", "enabled": true, "order": 0 },
{ "key": "balance", "enabled": false, "order": 1 }
]
}Your gang
Events shared with the members of your current gang.
Gang chat message
gang.chat.message.createdFires when a message is posted in your gang chat.
gang.messages:readGang ping
gang.ping.createdFires when a gang or truce ping is created.
gang.pings:readServer-wide
Live world events broadcast for the whole server.
Server event changed
server.event.changedFires when the current public server event changes.
events:readEvent schedule changed
server.event.schedule.changedFires when the server event schedule changes.
events:readMeteor landing changed
server.meteor.landing.changedFires when meteor landing information changes.
server.meteors:readMerchant spawned
server.merchant.spawnedFires when a merchant spawns.
server.merchants:readMerchant despawned
server.merchant.despawnedFires when a merchant despawns.
server.merchants:readGuards snapshot changed
server.guards.snapshot.changedFires when the server's guard counts change. It carries counts only, by design; read single guards from the nearby guards endpoint.
server.guards:readWeather changed
server.weather.changedFires when a weather event starts or ends.
events:readWeather surge changed
server.weather.surge.changedFires when a weather surge starts or ends.
events:readMine vault raid started
server.vault.raid.startedFires when a mine vault raid begins.
events:readMine vault raid ended
server.vault.raid.endedFires when a mine vault raid ends, with the outcome and top damagers.
events:readPrison Break phase changed
server.prisonbreak.phase.changedFires when the Prison Break match moves to a new phase.
events:readTop credit checkpoint paid
server.top_credit.checkpoint.completedFires when a Top Credit checkpoint pays out, with the awarded ranks.
player.top_credits:readGround Zero reset changed
server.ground_zero.reset.changedFires when a Ground Zero reset is scheduled, starts, completes or retries.
player.ground_zero:readpunishmentIdstringThe ban's id. Each planet sends its own copy, so dedupe on it.
player{ uuid, name }The banned player. name is null when the game could not look it up.
typestringban or temporary_ban.
reasonstringThe ban reason.
durationMsnumber | nullnull for a permanent ban.
issuedAt / expiresAtISO 8601expiresAt is null for a permanent ban.
staffMemberstringWho issued it.
These are the fields the public bans page shows, and nothing else. IP bans are not sent.
{
"punishmentId": "6710a3f2c9e77b2d4f1e8a90",
"player": { "uuid": "00000000-0000-0000-0000-000000000000", "name": "Steve" },
"type": "temporary_ban",
"reason": "Cheating",
"durationMs": 604800000,
"issuedAt": "2026-10-11T00:00:00Z",
"expiresAt": "2026-10-18T00:00:00Z",
"staffMember": "Landon"
}fingerstringindex, thumb, pinky or middle.
alivebooleanWhether the banker is up.
respawnInSecondsnumber0 while alive.
respawnAtISO 8601 | nullWhen it comes back. null while alive.
reasonstringspawned, defeated or lost.
Banker locations are on the Midas read.
{
"finger": "index",
"alive": false,
"respawnInSeconds": 300,
"respawnAt": "2026-10-11T00:05:00Z",
"reason": "defeated"
}phase / previousPhasestringlocked, open, ongoing or ending.
openedBy{ uuid, name } | nullWho opened the room. null while locked.
openedAtISO 8601 | nullWhen it opened.
secondsRemainingnumber | nullTime left in open or ending. null in other phases.
playersInside / maxPlayersnumberPlayers in the room and the cap.
{
"phase": "open",
"previousPhase": "locked",
"openedBy": { "uuid": "00000000-0000-0000-0000-000000000001", "name": "Steve" },
"openedAt": "2026-10-11T00:00:00Z",
"secondsRemaining": 120,
"playersInside": 1,
"maxPlayers": 10
}App owner
Operational events about your app's sessions, delivered to webhooks.
Session connected
mod.session.connectedFires when a player connects with your mod.
mod.sessions:readSession disconnected
mod.session.disconnectedFires when a player disconnects from your mod.
mod.sessions:read