NodeLink
API Reference

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

WebSocket Endpoint

WS
/v4/websocket
Connect to this endpoint to establish a WebSocket session.

Headers

AuthorizationstringRequired
The server password defined in config.js.
Client-NamestringRequired
Name and version of your client (e.g., "MyMusicBot/1.0.0").
User-IdstringRequired
Discord User ID of the bot. Must be 18-19 digits.
Session-Idstring
Session ID to resume. If provided, attempts to resume previous session.

Response

Resume a previous session after disconnection within the timeout period (default: 60 seconds).

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

Sent immediately after WebSocket connection is established.

Payload Structure

opstring
Always "ready"
resumedboolean
Whether this is a resumed session or new session
sessionIdstring
Unique session identifier for this connection

Example

{
  "op": "ready",
  "resumed": false,
  "sessionId": "xY7kP2mN8vQ4rL1s"
}

Player Update

Sent periodically (default 2s, configurable via playerUpdateInterval) with current playback position and connection status for each active player.

Payload Structure

opstring
Always "playerUpdate"
guildIdstring
Discord guild ID where player is active
stateobject
Current player state
state.timenumber
Current Unix timestamp in milliseconds
state.positionnumber
Current track position in milliseconds
state.connectedboolean
Whether player is connected to voice channel
state.pingnumber
Voice connection ping in milliseconds

Example

{
  "op": "playerUpdate",
  "guildId": "987654321098765432",
  "state": {
    "time": 1701350400000,
    "position": 45230,
    "connected": true,
    "ping": 42
  }
}

Stats Event

Server statistics sent on the statsUpdateInterval (default 30s).

Payload Structure

opstring
Always "stats"
playersnumber
Total number of active players across all guilds
playingPlayersnumber
Number of players currently playing audio
uptimenumber
Server uptime in milliseconds since start
memoryobject
Memory usage information in bytes
memory.freenumber
Free system memory
memory.usednumber
Memory used by NodeLink process
memory.allocatednumber
Total allocated memory
memory.reservablenumber
Maximum reservable memory
cpuobject
CPU usage information
cpu.coresnumber
Number of CPU cores
cpu.systemLoadnumber
System-wide CPU load (0.0-1.0)
cpu.processLoadnumber
NodeLink process CPU load (0.0-1.0)
frameStatsobject
Audio frame statistics (null if no active players)
frameStats.sentnumber
Total audio frames sent to Discord
frameStats.nullednumber
Frames that were null/empty
frameStats.deficitnumber
Frames that failed to send
frameStats.expectednumber
Total expected frames

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

Sent when a track begins playing on any player.

Payload Structure

opstring
Always "event"
typestring
Always "TrackStartEvent"
guildIdstring
Discord guild ID where track started
trackobject
Full track information including encoded data
track.encodedstring
Base64 encoded track data
track.infoobject
Track metadata
playingQualitynumber | null
YouTube itag or null if unavailable.

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

Sent when a track finishes playing or is stopped for any reason.

Payload Structure

opstring
Always "event"
typestring
Always "TrackEndEvent"
guildIdstring
Discord guild ID
trackobject
Track information
reasonstring
End reason: stopped, finished, loadFailed, replaced, cleanup

End Reasons

finishedstring
Track completed normally
stoppedstring
Track was manually stopped
replacedstring
Track was replaced by another track
loadFailedstring
Track failed to load
cleanupstring
Track ended due to cleanup (player destroyed)

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

Sent when an error occurs during track playback.

Payload Structure

opstring
Always "event"
typestring
Always "TrackExceptionEvent"
guildIdstring
Discord guild ID
trackobject
Track that caused the exception
exceptionobject
Error details
exception.messagestring
Human-readable error message
exception.severitystring
Error severity: common, suspicious, fault
exception.causestring
Root cause description

Severity Levels

commonstring
Expected error (e.g., track unavailable)
suspiciousstring
Unusual but recoverable error
faultstring
Critical error that prevents playback

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

Sent when playback is stuck and hasn't progressed for the threshold period.

Payload Structure

opstring
Always "event"
typestring
Always "TrackStuckEvent"
guildIdstring
Discord guild ID
trackobject
Track that is stuck
thresholdMsnumber
Time in milliseconds before considering stuck (typically 10000ms)
reasonstring
Why the track was marked as stuck.

Example

{
  "op": "event",
  "type": "TrackStuckEvent",
  "guildId": "987654321098765432",
  "track": {
    "encoded": "...",
    "info": {
      "identifier": "dQw4w9WgXcQ",
      "title": "Never Gonna Give You Up"
    }
  },
  "thresholdMs": 10000,
  "reason": "Recovery attempt failed"
}

WebSocket Closed Event

Sent when the voice WebSocket connection to Discord closes.

Payload Structure

opstring
Always "event"
typestring
Always "WebSocketClosedEvent"
guildIdstring
Discord guild ID
codenumber
Discord voice WebSocket close code
reasonstring
Human-readable close reason
byRemoteboolean
Whether the close was initiated by Discord (true) or locally (false)

Common Discord Voice Close Codes

4014number
Disconnected - channel was deleted or you were kicked
4006number
Session no longer valid
4015number
Voice server crashed

Example

{
  "op": "event",
  "type": "WebSocketClosedEvent",
  "guildId": "987654321098765432",
  "code": 4014,
  "reason": "Disconnected",
  "byRemote": true
}
Emitted when a cluster worker fails and affected players are dropped.

Payload Structure

opstring
Always "event"
typestring
Always "WorkerFailedEvent"
affectedGuildsstring[]
Guild IDs impacted by the worker failure.
messagestring
Human-readable summary.

Example

{
  "op": "event",
  "type": "WorkerFailedEvent",
  "affectedGuilds": ["987654321098765432"],
  "message": "Players for guilds 987654321098765432 lost due to worker failure."
}

Player Lifecycle Events

Emitted when a new player is created for a guild.

Payload Structure

opstring
Always "event"
typestring
Always "PlayerCreatedEvent"
guildIdstring
Discord guild ID
playerobject
Player state snapshot.

Example

{
  "op": "event",
  "type": "PlayerCreatedEvent",
  "guildId": "987654321098765432",
  "player": {
    "guildId": "987654321098765432",
    "track": null,
    "paused": false,
    "volume": 100
  }
}
Emitted when a player is destroyed.

Payload Structure

opstring
Always "event"
typestring
Always "PlayerDestroyedEvent"
guildIdstring
Discord guild ID

Example

{
  "op": "event",
  "type": "PlayerDestroyedEvent",
  "guildId": "987654321098765432"
}
Emitted when player successfully connects to Discord voice channel.

Payload Structure

opstring
Always "event"
typestring
Always "PlayerConnectedEvent"
guildIdstring
Discord guild ID
voiceobject
Voice connection details.

Example

{
  "op": "event",
  "type": "PlayerConnectedEvent",
  "guildId": "987654321098765432",
  "voice": {
    "sessionId": "abc",
    "token": "token",
    "endpoint": "us-central123.discord.media",
    "channelId": "123456789012345678"
  }
}
Emitted when player is attempting to reconnect to voice.

Payload Structure

opstring
Always "event"
typestring
Always "PlayerReconnectingEvent"
guildIdstring
Discord guild ID
voiceobject
Voice connection details.

Example

{
  "op": "event",
  "type": "PlayerReconnectingEvent",
  "guildId": "987654321098765432",
  "voice": {
    "sessionId": "abc",
    "token": "token",
    "endpoint": "us-central123.discord.media",
    "channelId": "123456789012345678"
  }
}

Player State Events

Emitted when player volume is changed.

Payload Structure

opstring
Always "event"
typestring
Always "VolumeChangedEvent"
guildIdstring
Discord guild ID
volumenumber
New volume level (0-1000)

Example

{
  "op": "event",
  "type": "VolumeChangedEvent",
  "guildId": "987654321098765432",
  "volume": 80
}
Emitted when audio filters are modified.

Payload Structure

opstring
Always "event"
typestring
Always "FiltersChangedEvent"
guildIdstring
Discord guild ID
filtersobject
Active filter configuration

Example

{
  "op": "event",
  "type": "FiltersChangedEvent",
  "guildId": "987654321098765432",
  "filters": {
    "volume": 1.0,
    "equalizer": [
      { "band": 0, "gain": 0.2 }
    ]
  }
}
Emitted when seeking to a position in the track.

Payload Structure

opstring
Always "event"
typestring
Always "SeekEvent"
guildIdstring
Discord guild ID
positionnumber
New position in milliseconds

Example

{
  "op": "event",
  "type": "SeekEvent",
  "guildId": "987654321098765432",
  "position": 60000
}
Emitted when playback is paused or resumed.

Payload Structure

opstring
Always "event"
typestring
Always "PauseEvent"
guildIdstring
Discord guild ID
pausedboolean
Whether playback is now paused (true) or resumed (false)

Example

{
  "op": "event",
  "type": "PauseEvent",
  "guildId": "987654321098765432",
  "paused": true
}
Emitted when voice connection status changes.

Payload Structure

opstring
Always "event"
typestring
Always "ConnectionStatusEvent"
statusstring
Connection status (connected, unstable, disconnected).
metricsobject | null
Connection metrics payload (speed, latency, error, timestamp).

Example

{
  "op": "event",
  "type": "ConnectionStatusEvent",
  "status": "connected",
  "metrics": {
    "speed": {
      "mbps": 180.59
    },
    "timestamp": 1764813556344
  }
}

Audio Mixer Events

Emitted when a new mix layer is successfully added and begins buffering.

Payload Structure

opstring
Always "event"
typestring
Always "MixStartedEvent"
guildIdstring
Discord guild ID where the mix started
mixIdstring
Unique identifier for the mix layer
trackobject
Full track information of the mixed layer
volumenumber
Volume of the mixed layer (0.0 to 1.0)

Example

{
    "op": "event",
    "type": "MixStartedEvent",
    "guildId": "990369410344701964",
    "mixId": "835e8475f0a53815",
    "track": {
        "encoded": "QAAAAAIAEFRUUzogaGVsbG8gd29ybGQACkdvb2dsZSBUVFP//////////wAQZ3R0czpoZWxsbyB3b3JsZAEBAHBodHRwczovL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV90dHM/aWU9VVRGLTgmcT1oZWxsbyUyMHdvcmxkJnRsPWVuLVVTJnRvdGFsPTEmaWR4PTAmdGV4dGxlbj0xMSZjbGllbnQ9Z3R4AApnb29nbGUtdHRzAAAAAAAAAAA=",
        "info": { ... },
        "userData": {}
    },
    "volume": 0.9
}
Emitted when a mix layer is removed (finished, error, or manual stop).

Payload Structure

opstring
Always "event"
typestring
Always "MixEndedEvent"
guildIdstring
Discord guild ID where the mix ended
mixIdstring
Unique identifier for the mix layer
reasonstring
Reason the mix layer ended (FINISHED, REMOVED, ERROR, MAIN_ENDED)

Possible Reasons

FINISHEDstring
Playback completed naturally (empty buffer threshold reached).
REMOVEDstring
Manually removed via API.
ERRORstring
Stream error occurred.
MAIN_ENDEDstring
The main track ended, triggering auto-cleanup.

Example

{
    "op": "event",
    "type": "MixEndedEvent",
    "guildId": "990369410344701964",
    "mixId": "835e8475f0a53815",
    "reason": "FINISHED"
}

On this page