API
Everything the site and the Discord bot show comes from one JSON service. The public reads are documented below for everyone; signing in, your account and the admin tools are shown to moderators and admins after sign-in.
Signing in
Two routes return the same signed token, valid 7 days. The token names the account; no endpoint takes an account id from the caller, so one token cannot read another account's data. Sign-in itself is one of the few routes that do not need a token.
POST /api/auth/signin (programs, no captcha)
Body fields: username (game account name), password (plain, over HTTPS), token (empty string; the captcha answer is only used by the web route).
curl -s https://api.hvmhfz.eclipseforge.xyz/api/auth/signin \
-H 'Content-Type: application/json' \
-d '{"username":"YourGameName","password":"your-password","token":""}'
{"accessToken":"eyJhbGciOiJIUzI1NiJ9...","id":12345,"roles":["ROLE_USER"],"tokenType":"Bearer"}
Send it on every request that needs an account: Authorization: Bearer <accessToken>. The token is a JWT: sub is the account name, roles lists ROLE_USER / ROLE_MODERATOR / ROLE_ADMIN, exp is the expiry. The server checks the signature and the role on every request; editing the payload breaks the signature.
POST /api/auth/signinweb (the website)
Same body, but token carries the Cloudflare Turnstile answer. The site's form does this for you. Wrong or missing captcha answers are refused; repeated failures are rate limited (429).
Registration and password reset
POST /api/auth/signup takes username, password and token (the Turnstile answer, as for signinweb) and makes a game account: a username of 3 to 20 characters from A-Z, a-z, 0-9 and _ . -, a password of 6 to 72 plain ASCII characters. A name that differs from an existing one only by capital letters is refused (409), because logins are case-sensitive. It answers like sign-in (accessToken), or a refusal with a one-sentence message. PUT /api/auth/passwordreset takes username, password and discordId for accounts that linked Discord. Both are rate limited.
| Header | Value |
|---|---|
| Authorization | Bearer <accessToken> |
| Content-Type | application/json on requests with a body |
Status codes: 400 bad body, 401 missing or expired token, 403 signed in but not allowed, 429 too many sign-in attempts.
Public endpoints
No token, cached 30 seconds unless stated. Paged endpoints return {items, page, size, total}; page starts at 0 and size is clamped to 1 to 100. Every other route, including the account and admin pages, needs an account token: sign in as described above and send it as a bearer token.
| Path | Parameters | Returns |
|---|---|---|
| GET /api/public/summary | none | counts: hunters, guilds, characters, online players |
| GET /api/public/guilds | q (search, case-insensitive), sort (default rp), page (0), size (30, max 100) | paged guild summaries |
| GET /api/public/guilds/{id} | id: guild id | one guild: name, rank, points, roster, board |
| GET /api/public/hunters | q, sort (hr default, name, gr, last), page, size (30, max 100) | paged hunter summaries |
| GET /api/public/hunters/{id} | id: character id | one hunter: name, HR, GR, weapon, guild, playtimeSeconds (the game's own counter), online, hunts, top monsters, weaponTime[] (weaponType, weapon, seconds; most played first) and weaponTimeSince |
| GET /api/public/catalog/items | q, page, size (40, max 100) | paged items: id (hex), name, type, icon |
| GET /api/public/catalog/equipment | slot (melee default, ranged, head, chest, arms, waist, legs), q, page, size (40) | paged equipment |
| GET /api/public/worlds | none | worlds: name, description, players, capacity |
| GET /api/public/status | none | state (online: the sign and game ports answer; degraded: only some ports answer; offline: none), online, players (hunters on all worlds; null while offline or unknown), worldsOpen, checkedAt, ports[] (name, port, open), connect (host, launcherPort, signPort, gamePort, mode, fallbackMode, version). Checked at most every 30 s |
| GET /api/public/weapons | none | hours played per weapon type by every hunter: since (when tracking began), seconds, hunters, weapons[] (all 14 types, most played first: weaponType, weapon, seconds, hunters) |
| GET /api/public/road/hunter/{id} | id: character id | one hunter's place on the live Road's and the Original Road's boards at any rank: [{key, name, current, open, solo}], open/solo = {rank, of, floor, points, reachedAt}, rank null with no clear |
| GET /api/public/worlds/history | range (24h default, 7d, 30d, 90d, all) | players online over time (sampled every minute: points[] with average and peak per bucket), active hunters per hour/day/week from the hunt log (active[]), and the players seen in the range: name, HR, GR, playtimeSeconds, sessions, secondsInRange, lastSeen, online (newest 500) |
| GET /api/public/monsters | limit (300 default, 1 to 300) | monster kill totals |
| GET /api/public/leaderboards/hunts | period (week default, lastweek, month, lastmonth, all), limit (100 default, 1 to 100) | period, from, to, resetsAt, hunters[], guilds[], monsters[] |
| GET /api/public/road/leaderboard | rotation (current default, original, or a cycle number), mode (open default, solo), limit (100 default, 1 to 100) | rotation, rotations[], tracking, hunters, entries[] |
| GET /api/public/road/history | none | every Road that has gone live, newest first |
| GET /api/public/search | q (required in practice) | hunters, guilds and monsters matching q |
| GET /api/public/events | none | current and upcoming events |
The hunt board scans the whole kill log, so it is cached for 5 minutes per period and size. Weeks start Monday 00:00 UTC and months on the 1st, so week resets every Monday.
Road leaderboard entry fields
| Field | Meaning |
|---|---|
| rank | 1-based position on this board |
| characterId, name, guildName, gr | who reached the floor |
| floor | highest floor reached in one run during the rotation |
| reachedAt | when that floor was first reached (ISO 8601 UTC) |
| floorsClimbed | how many floors were recorded for that hunter in the rotation |
| points | Road point balance at that floor, or null when it was not recorded |
rotation.sha is the sha256 of the Road data file, the map's permanent id. Rotations are listed in rotations[], newest first.
Errors, limits and caching
Errors are {"message":"..."}. 400 bad input (missing field, out of range, a minimum above its maximum), 401 missing or expired token, 403 wrong role or a character that is not yours, 404 unknown id, 409 conflicts such as an existing vote, 429 rate limit, 502 an upstream tool failed.
Public reads are cached 30 seconds; hunt boards 5 minutes per period and size. Sign-in and the public endpoints are rate limited per client: back off on 429 instead of retrying in a loop.
Paged endpoints take page (0-based) and size (1 to 100). Sizes above the maximum are clamped, not refused.
The account and admin half of this page is for moderators and admins. Sign in with one and reload; the public endpoints above stay readable by everyone. The server enforces every role regardless of what this page shows.
Loading the private reference...
