Gateway events

Every dispatch frame the Gateway sends carries op: 0 plus a t field naming the event. This page lists every event, its payload shape, and what triggers it.

Most payloads carry only the affected entity's id — clients refetch the canonical shape from REST. That keeps the wire small and means there's one place to change a row's shape (the API) when fields evolve.


Lifecycle

READY

First dispatch on a fresh connection. Carries the full user row so the app can hydrate immediately.

{
  "op": 0,
  "s": 1,
  "t": "READY",
  "d": {
    "user": { "id": "...", "email": "...", "display_name": "...", ... },
    "session_id": "31293...",
    "heartbeat_interval": 30000
  }
}
  • Name
    user
    Type
    User
    Description

    Same shape as GET /v1/users/@me.

  • Name
    session_id
    Type
    string
    Description

    The Gateway session id. Persist this — it's required for RESUME.

  • Name
    heartbeat_interval
    Type
    integer
    Description

    Milliseconds between client heartbeats. Echoed from HELLO for clients that bypass it.

RESUMED

Sent after a successful RESUME. Payload is empty — its purpose is to mark the end of the replay buffer so the client can return to normal flow.

{ "op": 0, "s": 9999, "t": "RESUMED", "d": {} }

Messages

MESSAGE_CREATE

Dispatched to every subscriber of the channel.

d

{
  "channel_id": "3123...",
  "message_id": "9182...",
  "ts": 1716929213,
  "nonce": "9182374ab"
}

nonce echoes the optimistic id the author submitted on POST /messages so the author's session can reconcile the pending row. Recipients with no matching pending row ignore it. The client should fetch GET /channels/:channel_id/messages/:message_id to hydrate the body.

MESSAGE_UPDATE / MESSAGE_DELETE

Minimal — entity id only. Refetch on update; on delete render the placeholder.

d

{ "channel_id": "3123...", "message_id": "9182..." }

MESSAGE_REACTION_ADD / MESSAGE_REACTION_REMOVE

d

{
  "channel_id": "3123...",
  "message_id": "9182...",
  "user_id": "31223...",
  "emoji_id": null,
  "emoji_name": "🎉"
}

emoji_id is set when the reaction is a custom emoji; null for unicode. emoji_name is always present — the glyph itself for unicode, the shortcode for custom.


Channels

All three are minimal — the channel id only. Clients refetch when they care.

EventPayloadTriggered when
CHANNEL_CREATE{ channel_id }A new channel becomes visible to the recipient.
CHANNEL_UPDATE{ channel_id }Name, topic, icon, scene, owner, etc. changed.
CHANNEL_DELETE{ channel_id }Channel removed.

TYPING_START

Real-time typing indicator. Server side has no TYPING_STOP — clients infer stop from a timer (typically 10s of no activity).

d

{ "channel_id": "3123...", "user_id": "31223..." }

Users

USER_UPDATE

Dispatched to every live session for the affected user when their profile changes. Carries only the user id; refetch /v1/users/@me.

d

{ "user_id": "31223..." }

PRESENCE_UPDATE

Status changes are dispatched to friends + channel co-members.

d

{ "user_id": "31223...", "status": 1 }

status follows the PresenceStatus enum: 0 OFFLINE, 1 ONLINE, 2 IDLE, 3 DND, 4 INVISIBLE.

RELATIONSHIP_UPDATE

Friend request sent / accepted, block applied — both sides receive this event so each can refetch their /relationships list.

d

{ "user_id": "31223...", "target_id": "412412..." }

Inventory + equipment

INVENTORY_ADD / INVENTORY_REMOVE

d

{ "instance_id": "9182..." }

Refetch /v1/users/@me/inventory/:instanceId on add; remove the row locally on remove.

EQUIPMENT_UPDATE

d

{ "user_id": "31223...", "slot": 4, "instance_id": "9182..." }

slot is an EquipmentSlot int. instance_id: null means the slot was unequipped — clients should clear that slot in local state.


Spatial

WARP_UPDATE

"This player is now in HUB/HOME X." Dispatched whenever the wormhole authoritative game server reports a join, room switch, or leave.

d

{
  "user_id": "31223...",
  "channel_id": "3123...",
  "session_id": "wmh_9281..."
}
  • Name
    user_id
    Type
    string
    Description

    The player whose location changed.

  • Name
    channel_id
    Type
    string | null
    Description

    The HUB or HOME they joined. null means the user is no longer in any spatial channel (logged out, dropped, etc.).

  • Name
    session_id
    Type
    string | null
    Description

    The wormhole's per-spawn id. Clients use it to correlate the dispatch with their own join attempt so a stale leave from a previous session can't blow away current state.


Adding new events

New events land as:

  1. A new EventName entry in packages/common/src/gateway.ts.
  2. A new payload type in the same file.
  3. A new variant on the Dispatch discriminated union — the exhaustive switch in client gateway handlers won't compile until it's wired up.
  4. A dispatch site somewhere in the API or game server.
  5. A row in this page.

Was this page helpful?