WebSocket API
Real-time events and session management via WebSocket connection.
WebSocket API
NodeLink uses WebSocket connections to provide real-time communication between the server and clients. This connection is essential for receiving player events, track updates, and server statistics.
Connection
Headers
Response
Response
When successfully resumed using the Session-Id header, the server sends a ready event with resumed: true. All queued events from the disconnection period are immediately sent, followed by current state updates from all active players.
Incoming Events (Server → Client)
NodeLink sends various events to keep clients informed about playback status, player state, and server statistics. All events are sent as JSON messages.
Ready Event
ReadyEvent
Payload Structure
Example
{
"op": "ready",
"resumed": false,
"sessionId": "xY7kP2mN8vQ4rL1s"
}Player Update
PlayerUpdate
Payload Structure
Example
{
"op": "playerUpdate",
"guildId": "987654321098765432",
"state": {
"time": 1701350400000,
"position": 45230,
"connected": true,
"ping": 42
}
}Stats Event
StatsEvent
Payload Structure
Example
{
"op": "stats",
"players": 5,
"playingPlayers": 3,
"uptime": 3600000,
"memory": {
"free": 4294967296,
"used": 536870912,
"allocated": 1073741824,
"reservable": 8589934592
},
"cpu": {
"cores": 8,
"systemLoad": 0.25,
"processLoad": 0.08
},
"frameStats": {
"sent": 15000,
"nulled": 0,
"deficit": 0,
"expected": 15000
}
}Track Start Event
TrackStartEvent
Payload Structure
Example
{
"op": "event",
"type": "TrackStartEvent",
"guildId": "987654321098765432",
"track": {
"encoded": "QAAAjQIAJVJpY2sgQXN0bGV5IC0gTmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXAADlJpY2tBc3RsZXlWRVZPAAAAAAACyPgAC2RRdzR3OVdnWGNRAAEAK2h0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9ZFF3NHc5V2dYY1EAB3lvdXR1YmUAAAAAAAAAAA==",
"info": {
"identifier": "dQw4w9WgXcQ",
"isSeekable": true,
"author": "Rick Astley",
"length": 212000,
"isStream": false,
"position": 0,
"title": "Never Gonna Give You Up",
"uri": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"artworkUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
"isrc": null,
"sourceName": "youtube"
}
},
"playingQuality": 251
}Track End Event
TrackEndEvent
Payload Structure
End Reasons
Example
{
"op": "event",
"type": "TrackEndEvent",
"guildId": "987654321098765432",
"track": {
"encoded": "QAAAjQIAJVJpY2sgQXN0bGV5IC0gTmV2ZXIgR29ubmEgR2l2ZSBZb3UgVXAADlJpY2tBc3RsZXlWRVZPAAAAAAACyPgAC2RRdzR3OVdnWGNRAAEAK2h0dHBzOi8vd3d3LnlvdXR1YmUuY29tL3dhdGNoP3Y9ZFF3NHc5V2dYY1EAB3lvdXR1YmUAAAAAAAAAAA==",
"info": {
"identifier": "dQw4w9WgXcQ",
"title": "Never Gonna Give You Up",
"author": "Rick Astley",
"length": 212000,
"sourceName": "youtube"
}
},
"reason": "finished"
}Track Exception Event
TrackExceptionEvent
Payload Structure
Severity Levels
Example
{
"op": "event",
"type": "TrackExceptionEvent",
"guildId": "987654321098765432",
"track": {
"encoded": "...",
"info": {
"identifier": "invalid123",
"title": "Invalid Track"
}
},
"exception": {
"message": "Failed to load track",
"severity": "fault",
"cause": "Video unavailable"
}
}Track Stuck Event
TrackStuckEvent
Payload Structure
Example
{
"op": "event",
"type": "TrackStuckEvent",
"guildId": "987654321098765432",
"track": {
"encoded": "...",
"info": {
"identifier": "dQw4w9WgXcQ",
"title": "Never Gonna Give You Up"
}
},
"thresholdMs": 10000,
"reason": "Recovery attempt failed"
}StreamMetadataEvent
Payload Structure
Example
{
"op": "event",
"type": "StreamMetadataEvent",
"guildId": "...",
"stream": {
"metadata": {
"streamTitle": "Artist - Song Name",
"streamUrl": "http://radio.example.com"
}
}
}EternalBoxInfoEvent
Payload Structure
EternalBoxJumpEvent
Payload Structure
WebSocket Closed Event
WebSocketClosedEvent
Payload Structure
Common Discord Voice Close Codes
Example
{
"op": "event",
"type": "WebSocketClosedEvent",
"guildId": "987654321098765432",
"code": 4014,
"reason": "Disconnected",
"byRemote": true
}WorkerFailedEvent
Payload Structure
Notes
When a worker fails, all players assigned to that worker are destroyed. NodeLink will automatically emit a WebSocketClosedEvent for each affected guild with code 5001 and reason worker_failed.
Example
{
"op": "event",
"type": "WorkerFailedEvent",
"affectedGuilds": ["987654321098765432"],
"message": "Players for guilds 987654321098765432 lost due to worker failure."
}Player Lifecycle Events
PlayerCreatedEvent
Payload Structure
Example
{
"op": "event",
"type": "PlayerCreatedEvent",
"guildId": "987654321098765432",
"player": {
"guildId": "987654321098765432",
"track": null,
"paused": false,
"volume": 100
}
}PlayerDestroyedEvent
Payload Structure
Example
{
"op": "event",
"type": "PlayerDestroyedEvent",
"guildId": "987654321098765432"
}PlayerConnectedEvent
Payload Structure
Example
{
"op": "event",
"type": "PlayerConnectedEvent",
"guildId": "987654321098765432",
"voice": {
"sessionId": "abc",
"token": "token",
"endpoint": "us-central123.discord.media",
"channelId": "123456789012345678"
}
}Payload Structure
Example
{
"op": "event",
"type": "PlayerReconnectingEvent",
"guildId": "987654321098765432",
"voice": {
"sessionId": "abc",
"token": "token",
"endpoint": "us-central123.discord.media",
"channelId": "123456789012345678"
}
}Player State Events
VolumeChangedEvent
Payload Structure
Example
{
"op": "event",
"type": "VolumeChangedEvent",
"guildId": "987654321098765432",
"volume": 80
}FiltersChangedEvent
Payload Structure
Example
{
"op": "event",
"type": "FiltersChangedEvent",
"guildId": "987654321098765432",
"filters": {
"volume": 1.0,
"equalizer": [
{ "band": 0, "gain": 0.2 }
]
}
}SeekEvent
Payload Structure
Example
{
"op": "event",
"type": "SeekEvent",
"guildId": "987654321098765432",
"position": 60000
}PauseEvent
Payload Structure
Example
{
"op": "event",
"type": "PauseEvent",
"guildId": "987654321098765432",
"paused": true
}Payload Structure
Example
{
"op": "event",
"type": "ConnectionStatusEvent",
"status": "connected",
"metrics": {
"speed": {
"mbps": 180.59
},
"timestamp": 1764813556344
}
}Lyrics Events
LyricsFoundEvent
Payload Structure
LyricsLineEvent
Payload Structure
LyricsNotFoundEvent
Payload Structure
Audio Mixer Events
MixStartedEvent
Payload Structure
Example
{
"op": "event",
"type": "MixStartedEvent",
"guildId": "990369410344701964",
"mixId": "835e8475f0a53815",
"track": {
"encoded": "QAAAAAIAEFRUUzogaGVsbG8gd29ybGQACkdvb2dsZSBUVFP//////////wAQZ3R0czpoZWxsbyB3b3JsZAEBAHBodHRwczovL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV90dHM/aWU9VVRGLTgmcT1oZWxsbyUyMHdvcmxkJnRsPWVuLVVTJnRvdGFsPTEmaWR4PTAmdGV4dGxlbj0xMSZjbGllbnQ9Z3R4AApnb29nbGUtdHRzAAAAAAAAAAA=",
"info": { ... },
"userData": {}
},
"volume": 0.9
}MixEndedEvent
Payload Structure
Possible Reasons
Example
{
"op": "event",
"type": "MixEndedEvent",
"guildId": "990369410344701964",
"mixId": "835e8475f0a53815",
"reason": "FINISHED"
}