Skip to content

API Reference

The web dashboard uses these endpoints. They are stable for third-party integrations but not formally versioned yet – expect additive changes.

Base URL: http://<host>:<port>
WebSocket URL: ws://<host>:<port + 1>

All /api/v1/ endpoints except /api/v1/auth/link and /api/v1/health require a JWT in the Authorization header:

Authorization: Bearer <jwt>

Obtain a JWT by exchanging a /bazaar link code:

POST /api/v1/auth/link
Content-Type: application/json
{ "code": "ABC123" }

Returns:

{
"token": "eyJ...",
"expiresAt": 1740000000000,
"user": { "uuid": "...", "name": "...", "isAdmin": false }
}
MethodPathPurpose
POST/api/v1/auth/linkExchange link code for JWT.
GET/api/v1/auth/meCurrent user info, including admin flag.
MethodPathPurpose
GET/api/v1/listingsPaginated active listings. Query: market (gts|ah), channel, sort, page, pageSize, search.
GET/api/v1/listings/{id}Listing detail.
GET/api/v1/listings/{id}/historyTransaction history + sparkline for this listing’s identity key.
GET/api/v1/listings/{id}/similarSimilar listings (same identity key).
POST/api/v1/listings/{id}/buyBuy it now. Body: { "confirm": true }.
POST/api/v1/listings/{id}/bidPlace a bid. Body: { "amount": 1500 }.
MethodPathPurpose
GET/api/v1/ordersPaginated active buy orders.
POST/api/v1/ordersCreate a buy order. Body: { "identityKey": "...", "price": 1500, "quantity": 1 }.
MethodPathPurpose
GET/api/v1/players/{uuid}/profilePublic profile.
GET/api/v1/me/listingsYour active listings.
GET/api/v1/me/ordersYour active buy orders.
GET/api/v1/me/collectionYour unclaimed collection entries.
GET/api/v1/me/transactionsYour transaction history.
POST/api/v1/me/collection/{id}/claimClaim a collection entry.
MethodPathPurpose
GET/api/v1/stats/overviewGlobal stats (active listings, active orders, freeze state, sync mode).
GET/api/v1/stats/summary/{key}Summary for an identity key (avg price, volume, high/low).
GET/api/v1/stats/history/{key}Transaction history for an identity key.
GET/api/v1/stats/trendingTop items by 24h volume.
GET/api/v1/stats/leaderboardTop sellers/buyers.
GET/api/v1/stats/distribution/{key}Price distribution histogram.
MethodPathPurpose
GET/api/v1/themeResolved theme JSON for the current preset selected in web-theme.conf.
GET/api/v1/theme/presetsAvailable preset names.
GET/api/v1/theme/cssResolved CSS overrides.
MethodPathPurpose
GET/api/v1/channelsList channels visible to the caller.
GET/api/v1/channels/{id}/listingsListings filtered to that channel.
MethodPathPurpose
GET/api/v1/admin/alertsSecurity alerts.
GET/api/v1/admin/economyEconomy-wide stats.
GET/api/v1/admin/inspect/{uuid}Full player inspection.
GET/api/v1/admin/players/search?q=...Autocomplete by name.
POST/api/v1/admin/freeze{ "frozen": true | false }.
POST/api/v1/admin/remove/{id}Remove a listing or buy order.
POST/api/v1/admin/purgePurge all expired listings.
MethodPathPurpose
GET/api/v1/health{ "status": "ok" } if the server is up. No auth.

Connect to ws://<host>:<port+1>.

Send:

{ "type": "auth", "token": "<jwt>" }

Notifications only arrive after successful auth. Unauthenticated sockets can still subscribe to public channels (market events), but not player-specific channels.

{ "type": "subscribe", "channels": ["market", "notifications"] }
{ "type": "unsubscribe", "channels": ["market"] }
ChannelWho receives itPayload
marketanyoneNew listings, bids, sales, cancellations.
channel:<id>anyone with channel browse permissionSame as market but filtered.
notificationsauthed userPlayer-scoped events: outbid, sold, won, collection ready.
admin.alertsadmins onlySecurity alerts, system events.

All events use a consistent envelope:

{
"type": "listing.created",
"timestamp": 1740000000000,
"payload": { ... }
}

Event types currently emitted:

  • listing.created
  • listing.updated
  • listing.cancelled
  • listing.expired
  • listing.sold
  • bid.placed
  • bid.outbid
  • order.created
  • order.filled
  • collection.ready
  • notification.push (for the notifications channel)

The API enforces the same per-minute limits as in-game actions (see limits.conf). A rejected request returns 429 Too Many Requests with a Retry-After header.

Endpoints that return lists accept page (0-based) and pageSize (default 24, max 100). Responses wrap:

{
"items": [ ... ],
"page": 0,
"pageSize": 24,
"total": 183
}