Entropia Central broadcasts live activity over SignalR. Three feeds are public and open to anyone: globals, trade messages and PvP actions. Connect to a hub, listen for its event, and the server pushes records as they are recorded.

These feeds are read only. The hubs push data to you and there is nothing to send back beyond the subscription calls described below.

Available hubs

All hubs live under https://api.entropiacentral.com.

HubPathEvent you listen forWhat it carries
Globals/hubs/globalsReceiveGlobalGlobals, HOFs and ATHs as they are recorded
Trade messages/hubs/trademessagesReceiveTradeMessageMessages from the in-game trade channels
PvP actions/hubs/pvpactionsReceivePvpActionPvP kills

Connecting

Build a connection, register your handler, then start it. Register handlers before calling start() so you do not miss records that arrive during the handshake.

ts
import { HubConnectionBuilder } from '@microsoft/signalr';

const connection = new HubConnectionBuilder()
  .withUrl('https://api.entropiacentral.com/hubs/globals')
  .withAutomaticReconnect()
  .build();

connection.on('ReceiveGlobal', (global) => {
  console.log(global.avatarName, global.globalValue, global.type);
});

await connection.start();

Payload properties are camelCased on the wire, so the C# property AvatarName arrives as avatarName.

Globals and trade messages start streaming immediately

Connecting to the globals hub or the trade messages hub is enough. The server adds you to that hub's broadcast group as part of the connection, so records begin arriving as soon as start() resolves.

PvP actions require an explicit join

The PvP hub is the exception. Connecting alone delivers nothing. Call JoinPvpActions after starting:

ts
const connection = new HubConnectionBuilder()
  .withUrl('https://api.entropiacentral.com/hubs/pvpactions')
  .withAutomaticReconnect()
  .build();

connection.on('ReceivePvpAction', (action) => {
  console.log(action.attacker, 'killed', action.defender);
});

await connection.start();
await connection.invoke('JoinPvpActions');

Call LeavePvpActions to stop receiving them without dropping the connection. Re-invoke JoinPvpActions after a reconnect, because group membership does not survive one.

Narrowing what you receive

Each hub accepts subscription calls that add you to a narrower group.

Globals

CallArgumentEffect
SubscribeToGlobalTypetype string, for example hofGlobals of that type only
UnsubscribeFromGlobalTypetype stringStops the above
SubscribeToEventnumeric event idGlobals scored towards that community event
UnsubscribeFromEventnumeric event idStops the above

Trade messages

CallArgumentEffect
SubscribeToChannelchannel name, for example tradeMessages on that channel only
UnsubscribeFromChannelchannel nameStops the above

PvP actions

CallArgumentEffect
JoinPvpActionsnoneAll PvP actions
JoinAttackeravatar nameActions where that avatar is the attacker
JoinDefenderavatar nameActions where that avatar is the defender
LeavePvpActions, LeaveAttacker, LeaveDefendermatching argumentStops the above

Avatar and channel names are matched case insensitively.

Subscribing causes duplicate deliveries

This is the one behaviour that surprises people. A narrower subscription does not replace the broad feed, it adds a second delivery path, and the server broadcasts to every matching group.

On the globals hub you are already in the broadcast group from the moment you connect. If you then call SubscribeToGlobalType('hof'), a HOF is sent to the broad group and to the type group, so your ReceiveGlobal handler fires twice for the same record. Trade message channels and PvP attacker and defender groups behave the same way.

Two ways to deal with it:

  • Use the broad feed and filter client side, which is the simplest option and what the Entropia Central site itself does.
  • Or subscribe narrowly and deduplicate. Globals carry strippedMessage, which is the value the server itself hashes for deduplication, and trade messages carry a stable id.

Subscribing to a specific attacker and defender who appear in the same kill will deliver that action three times: once for the broad group and once for each name group.

Payloads

ReceiveGlobal

PropertyTypeNotes
avatarNamestringAvatar that recorded the global
globalValuenumberValue in PED
typestringGlobal category, for example hunting or mining
dateTimestringUTC timestamp, always set by the server
isHof, isAth, isTeambooleanClassification flags
fullMessagestringOriginal in-game message
strippedMessagestringMessage without timestamp or channel prefix, used for deduplication
creatureName, depositName, instanceName, craftedItemName, landareaName, rareItemName, discoveredItemName, tieredItemNamestringPopulated according to type, empty otherwise
tieredItemTiernumberTier reached, for tiering globals
pvpSpreenumberKill count, for PvP globals
detailRoutestring or nullWiki path for the subject, for example /wiki/creatures/some-slug

The name fields are always present and are empty strings when they do not apply, rather than being omitted.

ReceiveTradeMessage

PropertyTypeNotes
idnumberStable identifier, useful for deduplication
channelstringOriginating in-game channel
authorstringAvatar that posted
contentstringMessage text
dateTimestringUTC timestamp, most recent posting when reposts were collapsed
repeatCountnumber1 for a single posting, higher when identical reposts were collapsed
firstPostedAtstring or nullFirst posting time, set only when reposts were collapsed

ReceivePvpAction

PropertyTypeNotes
attackerstringAvatar that got the kill
defenderstringAvatar that was killed
weaponstringWeapon used
dateTimestringUTC timestamp

Reconnects

Use withAutomaticReconnect(). Two things to keep in mind when a connection drops and comes back:

Nothing is replayed. Records broadcast while you were disconnected are gone. If you need an unbroken series, re-fetch the current state from the REST endpoints on reconnect and treat the socket as live updates only.

Group membership is not restored. Automatic group joins are re-applied by the server, so globals and trade messages resume on their own. Anything you joined by calling a method, including every PvP subscription, has to be re-invoked:

ts
connection.onreconnected(async () => {
  await connection.invoke('JoinPvpActions');
});