HTTP API
Everything BridgeServer knows is available over plain HTTP on HttpPort (default 55981). Responses are JSON, requests are GET only, and there is no write path.
Endpoints#
| Endpoint | Returns |
|---|---|
/v1/health | Status, version, connected HTTP clients, events stored |
/v1/stream | Live event feed (Server-Sent Events) |
/v1/feed/history | Recent events from the history store |
/v1/status | Accounts, characters, guilds and the live online gauge |
/v1/events/schedule | Current event timetable with countdowns |
/v1/rankings/{kind} | resets · grandresets · killers · level · guilds · online |
/v1/character/{name} | Public character profile |
/v1/guild/{name} | Guild profile with members |
/v1/castle-siege | Current castle owner and siege state |
Any other path returns 404. A ranking or profile request returns 503 when the game database is not configured or has not finished its first refresh.
Event Shape#
Every event is one flat JSON object:
{
"t": 1754049312,
"type": 23,
"kind": "raredrop",
"actor": { "name": "Nyx", "level": 400, "class": 16, "map": 30, "x": 128, "y": 128 },
"item": 13,
"itemName": "Dragon Soul",
"itemLevel": 13,
"exc": 1,
"anc": 0,
"map": 30, "x": 128, "y": 128
}
| Field | Always present | Meaning |
|---|---|---|
t | yes | Unix timestamp |
type | yes | Numeric event type, used by the type= history filter |
kind | yes | Readable event kind - switch on this |
actor | most kinds | Who caused it: name, level, class, map, x, y |
target | pair events | The other character, same shape as actor |
Remaining fields depend on the kind. Consumers should switch on kind and ignore everything they do not recognise, so new event kinds never break an existing integration.
Event Kinds#
kind | type | Fires when |
|---|---|---|
login | 1 | A character comes online |
logout | 2 | A character goes offline |
chat | 3 | Public chat (channel: 0), when chat export is on |
post | 3 | A global /post message (channel: 1) |
stats | 4 | Population snapshot, every 30 seconds |
schedule | 5 | Event timetable snapshot, every 60 seconds |
pk | 10 | A player kills another player |
bosskill | 11 | A configured boss class dies |
mobkill | 12 | Any monster dies, when monster-kill export is on |
gameevent | 13 | Blood Castle, Devil Square, Chaos Castle, an invasion or Castle Siege starts or ends |
eventwin | 14 | An event is won |
trade | 20 | A trade completes |
pshopsell | 21 | A personal shop sale completes |
chaosmix | 22 | A Chaos Machine attempt resolves |
raredrop | 23 | An excellent, ancient or socketed item drops on the ground |
sellpost | 24 | An item is listed through the sell-post channel |
levelup | 30 | A character gains a level |
reset grandreset masterreset | 31 | A character resets |
stats and schedule are snapshots: they are streamed live and served from their own endpoints, but never written to history or relayed to Discord.
Live Feed#
/v1/stream is a standard Server-Sent Events endpoint. Each event arrives as one data: frame containing the JSON object.
const feed = new EventSource("http://your-host:55981/v1/stream");
feed.onmessage = (msg) => {
const e = JSON.parse(msg.data);
if (e.kind === "raredrop") {
announce(`${e.actor.name} found ${e.itemName} +${e.itemLevel}`);
}
};
Browsers reconnect automatically when the connection drops. Events that happened while you were disconnected are not replayed - use /v1/feed/history to fill a gap.
There is a fixed cap on simultaneous stream subscribers, and a slow subscriber is dropped rather than allowed to back everything up. Point one server-side consumer at the stream and fan out from there instead of opening a stream per website visitor.
History#
# last 100 events of any kind
curl "http://your-host:55981/v1/feed/history?limit=100"
# boss kills since a timestamp
curl "http://your-host:55981/v1/feed/history?type=11&since=1754049312"
| Parameter | Default | Effect |
|---|---|---|
type | all | Numeric event type from the table above |
limit | 50 | Maximum events returned. Values above 500 are capped at 500 |
since | 0 | Only events at or after this unix timestamp |
Events come back newest first.
Status#
/v1/status merges two sources:
{
"db": { "onlineCount": 143, "totalAccounts": 5210, "totalCharacters": 12874, "totalGuilds": 233 },
"live": { "t": 1754049312, "type": 4, "kind": "stats", "online": 143,
"maps": [ { "map": 0, "count": 61 }, { "map": 2, "count": 24 } ] }
}
db comes from the game database and is null when no data source is configured. live is the most recent population gauge from the GameServer and is null until the first one arrives.
Rankings and Profiles#
/v1/rankings/{kind} returns an array of rows with the database column names as keys - Name, cLevel, Class, ResetCount, MasterResetCount, Kills, and so on depending on the ranking.
curl "http://your-host:55981/v1/rankings/resets"
curl "http://your-host:55981/v1/character/Kaelen"
curl "http://your-host:55981/v1/guild/Sahara"
curl "http://your-host:55981/v1/castle-siege"
Ranking lists are served from a cache rebuilt every MuDbRefreshSeconds, so they can be slightly stale. Character, guild and siege lookups are answered live.
Deployment Notes#
- No authentication, no TLS, no rate limiting. BridgeServer is a plain HTTP server on purpose. Put it behind your existing reverse proxy if you want any of those, exactly as you would for any small internal service.
- Do not point browsers straight at it. Read it from your website's backend or your bot, cache there, and serve your own visitors. That keeps subscriber slots free and stops the API being exposed publicly.
- The API only reads. No endpoint can change game state, and BridgeServer never writes to the game database.
Common Issues#
503on rankings or profiles - the game database is not configured, or the first refresh has not completed- Stream connects, then goes quiet - no events are being exported; check the Bridge plugin config on the GameServer
- Stream refused - every subscriber slot is taken; consolidate to one backend consumer
- History empty after a restart -
bridge.dbwas deleted, or nothing has been exported yet - Rankings are stale - expected within
MuDbRefreshSeconds; lower it only if you accept the extra database load - Nothing responds at all - BridgeServer is not running, or
HttpPortis blocked between you and it
Related#
- BridgeServer overview
- BridgeServer.ini
- Bridge Config.xml - which events get exported