docs(website): one self-service surface — /auth/me/account

Companion to RunicGateway/website's collapse of /admin/account (6 routes) and
/player/account (8 routes) onto /auth/me/account, which was already a strict
superset of both.

  - BACKEND_DESIGN.md: the two router-tree entries go; the /auth/me prose is
    rewritten from "additive, the older routes stay for web back-compat" to the
    single surface it now is, recording why /auth/me was the one to keep and
    that gating was equivalent. account.controller.js moved to router/v1/auth/.
    The /player-group paragraph loses account.router.js from its mount list.
  - ENGAGEMENT.md: new Phase 1a records the collapse as built, and §0.6 finding 4
    is corrected — it named router/v1/player/account.router.js, which is gone.
    Phase 1b's change-and-verify flow now lands on /auth/me/account and nowhere
    else, which was the reason to do this first: a self-service field would
    otherwise have been written three times, in 1b and again in Phase 3.
  - android/PLAN.md §6.4/§8: the "routes stay for web back-compat" note is now
    false. The app needed no change — MeApi.kt was already 100% /auth/me/*.
  - API_V2_PLAN.md: a forward pointer only. Its router inventories are a record
    of the domain split as it landed and are deliberately left as written.

api-route-inventory.json is regenerated wholesale, not partially updated. It is
a generated mirror of server/routes.manifest.json with no CI gate, and it had
drifted on its own: 168 entries against the manifest's 203, missing every Teams
route and still listing the two Gmail connect routes Phase 1 deleted. It is now
byte-identical to the manifest. A gate for it is flagged in ENGAGEMENT.md
Phase 1a as later work — a mirror nothing checks will drift again.

PROJECT_TREE.md is deliberately untouched: it is auto-generated by the
sync-project-tree workflow in the website repo, which regenerates it from
tracked files on main.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-29 00:49:40 -05:00
parent cde362e588
commit 35abf61277
5 changed files with 308 additions and 95 deletions

View File

@@ -659,7 +659,7 @@ not rank).
| **Rules / Leaderboards / Market** | everyone, *if the shard publishes them* | `/public/shard/{ruleset,points,market}` (M11) |
| **Atlas** (bestiary) | everyone, *if the shard publishes it* | `/public/atlas/*` (M11) |
| Contact | everyone | `/public/contact` |
| **My Account** | signed-in | `/player/account/*` (or `/admin/account/*` for staff — see §6.4) |
| **My Account** | signed-in | `/auth/me/account/*` — one surface for every role (see §6.4) |
| **My Characters / Vendors / Houses** | `player` (linked) | `/player/shard/*` |
| Sign in / Sign out | toggles on session | `/auth/mobile/*` |
@@ -707,8 +707,10 @@ Guidelines:
and `GET /public/atlas/*` are the new reads. Full contract and traps in §9 M11.
### 6.3 Player self-service & game data (bearer)
- **Account** — `GET /player/account`; `PATCH /player/account/username`;
`PATCH /player/account/password`; TOTP `setup`/`enable`/`disable`; identities `GET` / `DELETE`.
- **Account** — `GET /auth/me/account`; `PATCH /auth/me/account/username`;
`PATCH /auth/me/account/password`; TOTP `setup`/`enable`/`disable`; identities `GET` / `DELETE`.
Role-agnostic — see §6.4. (These were `/player/account/*` when this section was written; that
surface was deleted on 2026-08-29. The app was already calling `/auth/me/*` and needed no change.)
- **Game account linking** — `POST /player/shard/link` (one-time `[link` code),
`POST /player/shard/account` (hybrid signup, when enabled), `GET /player/shard/accounts`.
- **My game data** — `GET /player/shard/roster/:account`, `/char/:serial`, `/vendors/:account`,
@@ -724,13 +726,16 @@ Guidelines:
art/asset work on the platform side) and is explicitly out of the first release.
### 6.4 Self-service is role-agnostic under `/auth/**` (decided)
Player self-service is under `/player/account/*` (gated to `role='player'`) and staff use the *same*
handlers under `/admin/account/*`. Rather than have the app branch by role (and touch `/admin`), we
**add a role-agnostic self surface under `/auth/**`** — the canonical "me" endpoints for every role.
The app calls these regardless of role. This is an **additive v1**
change (see §8): the existing `/player/account/*` and `/admin/account/*` routes stay for web
back-compat; `/auth/me/*` reuses the same `account.controller` handlers behind `requireAuth` (any
authenticated role), so there's no logic duplication.
Self-service account security lives under `/auth/me/account/*` — the canonical "me" endpoints for
every role, behind `requireAuth` (any authenticated role). The app calls these regardless of role and
never touches `/admin`.
> **Update (2026-08-29).** When this was decided it was an *additive* change: `/player/account/*`
> (gated to `role='player'`) and `/admin/account/*` served the same `account.controller` handlers and
> stayed for web back-compat. **Those 14 routes have since been deleted** — `/auth/me/account` was
> already a strict superset of both, so the web client moved onto it and `/auth/me/*` is now the only
> self-service surface. See `website/ENGAGEMENT.md` Phase 1a. **The app required no change**: `MeApi.kt`
> was already 100% `/auth/me/account/*`.
> **M10 update (2026-07-21):** *self-service* stays role-agnostic under `/auth/me/*` as above. Separately,
> the **operational** admin subset (§1, §10 — moderation, support queue, dashboard/site-mode, content)
@@ -784,8 +789,9 @@ maintenance cost. Reserve v2 for a real breaking re-shape if one ever arises.
1. **Role-agnostic self-service under `/auth/**` (§6.4, decided).**
✅ **DONE (2026-07-19, RunicGateway/website#76 (+ this docs PR)).** A `me.routes.js`
sub-router mounts the existing `account.controller` self handlers behind `requireAuth` (any role) at
`/auth/me/*`, so the app has one self surface and never touches `/admin`. The old
`/player/account/*` + `/admin/account/*` routes stay for web back-compat. Shipped routes:
`/auth/me/*`, so the app has one self surface and never touches `/admin`. (The old
`/player/account/*` + `/admin/account/*` routes stayed for web back-compat at the time; they were
deleted on 2026-08-29 — see §6.4.) Shipped routes:
- `GET /auth/me` — current `{ id, username, role }` (already existed; the app's role source).
- `GET /auth/me/account` — full self account.
- `PATCH /auth/me/account/username`, `PATCH /auth/me/account/password`.

View File

@@ -14,6 +14,14 @@ Target repo: `website/` · Docs owner: this file + `BACKEND_DESIGN.md`
> 2. **The domain split** — `admin.routes.js` (1552 lines, 110 routes) broken into one router file per
> business capability, **in place, with every URL unchanged**. This is the actual driver.
> **Later change, recorded here so the tables below are not read as current (2026-08-29).** The router
> inventories in this file are a **record of the split as it landed**, and are deliberately left as
> written. Since then, `admin/account.router.js` (6 routes) and `player/account.router.js` (8 routes)
> were **deleted**: `/auth/me/account/*` was already a strict superset of both, and is now the single
> self-service surface. `account.controller.js` moved to `router/v1/auth/`. See
> [ENGAGEMENT.md](./ENGAGEMENT.md) Phase 1a and [BACKEND_DESIGN.md](./BACKEND_DESIGN.md) §4. The
> authoritative URL list is, as this file argues throughout, the generated manifest — never a table.
---
## Why the auth merge is out

View File

@@ -87,7 +87,15 @@ server/
/me sub-routers below also match the
bare /me and supply its noindex header
me.routes.js (23) /auth/me/account*, sessions, trusted
devices — router-level requireAuth
devices — router-level requireAuth.
The ONLY self-service account surface
(see below); account.controller.js
sits beside it and is reached from
nowhere else
account.controller.js the self-service handlers: username,
password, TOTP, identities, device
sessions, trusted devices, recovery
codes
notifications.routes.js (3) /auth/me/devices*, notifications/*
mobile.routes.js + /auth/mobile/* — native bearer login
mobileSso.routes.js (5)
@@ -121,9 +129,6 @@ server/
player/ index.js owns the shared `noindex, requireAuth` gate
(authenticated, ANY role — staff are a superset
of players) and the mount table
account.router.js (8) /player/account — credentials, TOTP,
linked identities; handlers shared
with /admin/account and /auth/me
appeals.router.js (4) /player/appeals
appeals.controller.js
(/player/shard is module-uo's)
@@ -145,7 +150,6 @@ server/
own prefixes; owns the shared
`noindex, isLoggedIn, staffOnly` gate and
declares no routes itself
account.router.js (6) /admin/account — self-service, no adminOnly
users.router.js (9) /admin/users — adminOnly. The six
/users/:id/shard/* routes are a
MODULE's, reached through the
@@ -786,21 +790,29 @@ their own router level, and `/sso/:provider/link` carries `requireAuth` per rout
| GET · PUT | `/me/notifications/subscriptions` | cookie / bearer | `{streams:[id]}` on PUT | get / replace own opted-in streams (unknown ids dropped) |
| GET · PUT | `/me/notifications/teams` | cookie / bearer | `{teams:[{teamId,muted,emailMode}]}` on PUT | get / replace own **per-Team** preferences (phase 6, [`TEAMS.md`](TEAMS.md) §6.3). One entry per Team the caller could be notified about — active membership or an active forum grant — plus any Team they already hold a preference for; server-side defaults applied. An entry naming a Team the caller has no access to is **dropped, not refused**: a Team left between loading the screen and saving it is a race, not a client bug. The array is required even when empty (`../android/PLAN.md` §11) |
**Role-agnostic self-service (`/auth/me/*`).** The canonical "me" surface for **every** authenticated
role. It reuses the exact `account.controller` handlers as `/player/account/*` and `/admin/account/*`
(no logic duplication) behind `requireAuth` **only** — any active account, never a specific role. This
lets a client (the Android app) manage its own account through one surface without ever touching
`/admin` (docs/android/PLAN.md §6.4). The older `/player/account/*` + `/admin/account/*` routes stay
for web back-compat.
**Role-agnostic self-service (`/auth/me/*`).** The **only** self-service account surface, for every
authenticated role, behind `requireAuth` **only** — any active account, never a specific role. A
client (the Android app) manages its own account through it without ever touching `/admin`
(docs/android/PLAN.md §6.4).
It used to be the third of three URL surfaces onto `account.controller`, beside `/player/account/*`
and `/admin/account/*`. **Those 14 routes were deleted.** Both were strictly *smaller* than this one —
neither carried recovery codes, and `/admin/account` carried no username or password change — so the
web client already reached in here for part of a single screen. Gating was equivalent where it
overlapped (`/player` and `/auth/me` are byte-identical `noindex, requireAuth`; `staffOnly` on
`/admin/account` was strictly narrower and bought nothing, since every handler is self-scoped to
`req.user.id`). The controller moved to `router/v1/auth/account.controller.js` beside its one
remaining router. **New self-service fields go here and only here.**
**The `/player/*` group is self-service, not player-only.** Staff are a **superset** of players — every
player ability plus their staff tools on top — so the whole group (`account.router.js`,
`appeals.router.js`, mounted by `player/index.js`, plus whatever a module mounts here) sits behind
player ability plus their staff tools on top — so the whole group (`appeals.router.js`, mounted by
`player/index.js`, plus whatever a module mounts here) sits behind
the shared `noindex, requireAuth` gate **only**, never `requireRole('player')`. Every handler is self-scoped to the caller by `req.user.id`, so an admin/editor/
moderator using it sees only their **own** linked accounts and characters (with the pre-existing
`isAdmin` bypass still letting a genuine admin read *any* character). `module-uo` inherits the rule
and relies on it: its `/player/shard/*` handlers are the identical self-scoped ones it also serves
under `/admin/shard/*`, so the two are interchangeable. This is why a staff account with linked game characters gets its "My characters" and
under `/admin/shard/*`, so the two are interchangeable. (Core no longer does this for account
security — see `/auth/me/*` above — but the rule the module depends on is unchanged.) This is why a staff account with linked game characters gets its "My characters" and
personal notification streams on the mobile client — the group no longer 403s a non-`player` role.
`teams.router.js` joins the group in Teams phase 2, and relies on exactly that rule: a moderator is in

View File

@@ -182,10 +182,16 @@ duplicates Phase 1b must clean up, and it makes `email_verified` too weak a sign
duplicate wins (which is why §7.1 Q1's answer is oldest-wins, not verified-wins).
**4. There is no self-serve email flow at all.** No route lets a user set or change their own address
after signup — `router/v1/player/account.router.js` has none, and `users.model.js:72`'s `update()` is
reached only by admin user management. An address is captured once, at registration or SSO provisioning,
and is thereafter unchangeable by its owner. A verification gate presupposes a change-and-verify flow,
so Phase 1b builds one; it is not an add-on to an existing screen.
after signup — `router/v1/auth/me.routes.js`, the self-service surface, has none, and
`users.model.js:72`'s `update()` is reached only by admin user management. An address is captured once,
at registration or SSO provisioning, and is thereafter unchangeable by its owner. A verification gate
presupposes a change-and-verify flow, so Phase 1b builds one; it is not an add-on to an existing
screen.
> Read out of the tree on 2026-08-28, when self-service had *three* URL surfaces onto one controller
> (`/auth/me/account/*`, `/player/account/*`, `/admin/account/*`), so a new field meant writing it
> three times. **The two smaller ones were deleted on 2026-08-29** — see Phase 1a below. Phase 1b's
> change-and-verify routes are written once, on `/auth/me/account`.
**One consequence for the error surface.** A unique constraint needs a user-facing failure, and the
obvious wording (*"that email is already registered"*) makes account existence queryable — a step back
@@ -1204,6 +1210,7 @@ change is not complete until `docs/` reflects it" — is the floor; this table i
| Phase | `docs/` | Other repos |
| --- | --- | --- |
| **1** Remove Gmail OAuth2, SMTP | `website/BACKEND_DESIGN.md` §7 **rewritten** (not amended — it documents Gmail OAuth2 as *the* mechanism); route tables lose `/admin/email/connect/*` | `website/README.md` + `.env.example` wherever they point at Connect Gmail · **`runicgateway.com`**: `notifications-and-email.mdx` (its "There is no SMTP option" aside is now false), `configuration.mdx:62`, `troubleshooting.mdx:101`, `system-architecture.mdx:117` · a release note |
| **1a** One self surface | `website/BACKEND_DESIGN.md` — the `/auth/me` prose and the `/player`+`/admin` router trees · `api-route-inventory.json` regenerated · `website/ENGAGEMENT.md` this phase | `android/PLAN.md` §6.4/§8 — the "routes stay for web back-compat" note is now false · `website/API_V2_PLAN.md` historical tables get a pointer |
| **1b** Unique email | `website/BACKEND_DESIGN.md` — the `users` table (the "not unique" note is now false), the new change/verify routes, and the de-dupe migration as an operator-visible upgrade step | `website/README.md` upgrade notes · a release note naming the admin report and the verification-gate default |
| **2** Trigger registry | `website/MODULE_API.md` §1.1 (**1.7.0** + correct the stale "1.6.0 has only ever been on `edge`" paragraph), §2.3 (`ctx.events`, `ctx.inbox`), §2.4 (`registerEventTriggers`), §7.3's dormant-rule note · `website/ENGAGEMENT.md` §4.3 kept true | `Integration-kit`: `ci/core-ref.json` re-pinned (the equality check goes red **on purpose**) + chapter 2 gains a "registering a trigger" section · **`runicgateway.com`**: `platform.json.moduleApi` → 1.7.0 |
| **3** Channel preferences | `website/BACKEND_DESIGN.md` route table · `android/PLAN.md` §11 | — |
@@ -1299,6 +1306,45 @@ of a 500 that takes the contact form with it.
---
### Phase 1a — One self-service surface: `/auth/me/account` ✅
**Not in the original plan.** It was added on 2026-08-29, ahead of Phase 1b, when mapping Phase 1b's
ground truth turned up that self-service account security had **three URL surfaces onto one
controller** — `/auth/me/account/*`, `/player/account/*` and `/admin/account/*`, all mounting the same
`account.controller` handlers. Phase 1b adds a self-service field (email), Phase 3 adds another
(channel preferences), and each would otherwise have been written three times.
**`/auth/me/account` was already a strict superset**, which is what settled which one to keep: it was
the only surface carrying recovery codes, and `/admin/account` carried no username or password change
at all. The web client already reached into `/auth/me` for two calls on a screen it otherwise served
from `/admin/account` — the split was leaking before anyone touched it. Gating was equivalent where it
overlapped: `/player` and `/auth/me` apply byte-identical `noindex, requireAuth`, and `staffOnly` on
`/admin/account` was strictly narrower while buying nothing, since every handler is self-scoped to
`req.user.id`.
**14 routes deleted, 0 added, no handler changed.** `account.controller.js` moved from
`router/v1/admin/` to `router/v1/auth/`, beside the one router that still reaches it. The web client's
14 call sites moved onto the root-level `api.myAccount` / `api.changeUsername` / … group. The Android
app needed **nothing**`MeApi.kt` was already 100% `/auth/me/account/*`.
It is a **breaking change to the published OpenAPI surface**, accepted deliberately: both consumers are
in this org, and deprecating-then-deleting would have meant Phase 1b deciding whether to add its email
routes to surfaces already marked for removal.
**As built:** `routes.manifest.json` shows exactly 14 deletions and 0 additions; the OpenAPI spec loses
the same 14 paths with **zero surviving path definitions changed** and identical `components` (its large
textual diff is pure reordering — removing the first-mounted router shifts every later path). Two
swagger tags — `Admin · Account` and `Player` — were declared *only* by the deleted routes and went with
them. 1203 server tests and 288 client tests green.
**One thing this phase did not fix, on purpose:** `docs/website/api-route-inventory.json`, the docs
mirror of the route manifest, had drifted badly on its own (168 entries against the manifest's 203 —
missing every Teams route, and still listing the two Gmail `connect` routes Phase 1 deleted). It is a
generated mirror with no CI gate, so it was regenerated wholesale here rather than partially updated.
**A mirror nothing checks will drift again** — a gate for it belongs in a later phase.
---
### Phase 1b — Unique, changeable, verifiable email addresses *(decision 6)*
**Lands alone, between 1 and 2, and before any engagement mail exists.** It touches registration, SSO
@@ -1323,7 +1369,8 @@ uniqueness *after* a send log and a suppression list hold rows is strictly worse
`foo@x.com` are one mailbox everywhere that matters, and folding must happen in the index rather than
in application code that can be bypassed.
3. **A self-serve change-and-verify flow**, which does not exist today (§0.6 finding 4). Set/change
address, a signed time-boxed verification link, `email_verified` set only on link use. SSO's
address, a signed time-boxed verification link, `email_verified` set only on link use. It lands on
**`/auth/me/account` and nowhere else** — Phase 1a made that the single self-service surface. SSO's
`emailVerified: Boolean(profile.email)` is corrected at the same time to honour the IdP's actual
`email_verified` claim rather than the mere presence of an address.
4. **The verification gate as an admin setting**`on` for fresh installs, `off` for upgrades, so the

View File

@@ -17,30 +17,6 @@
"method": "GET",
"path": "/api/health"
},
{
"method": "GET",
"path": "/api/v1/admin/account"
},
{
"method": "GET",
"path": "/api/v1/admin/account/identities"
},
{
"method": "DELETE",
"path": "/api/v1/admin/account/identities/:provider"
},
{
"method": "POST",
"path": "/api/v1/admin/account/totp/disable"
},
{
"method": "POST",
"path": "/api/v1/admin/account/totp/enable"
},
{
"method": "POST",
"path": "/api/v1/admin/account/totp/setup"
},
{
"method": "GET",
"path": "/api/v1/admin/activity"
@@ -89,14 +65,6 @@
"method": "PUT",
"path": "/api/v1/admin/email/config"
},
{
"method": "GET",
"path": "/api/v1/admin/email/connect/callback"
},
{
"method": "GET",
"path": "/api/v1/admin/email/connect/start"
},
{
"method": "POST",
"path": "/api/v1/admin/email/disconnect"
@@ -145,6 +113,14 @@
"method": "GET",
"path": "/api/v1/admin/moderation/recent"
},
{
"method": "GET",
"path": "/api/v1/admin/moderation/reports"
},
{
"method": "POST",
"path": "/api/v1/admin/moderation/reports/:id/handle"
},
{
"method": "GET",
"path": "/api/v1/admin/moderation/search"
@@ -293,6 +269,98 @@
"method": "PUT",
"path": "/api/v1/admin/site-mode"
},
{
"method": "GET",
"path": "/api/v1/admin/teams"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/:id"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/:id/archive"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/:id/display-name"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/:id/forum/moderation"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/:id/grants"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/:id/hide"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/:id/leader-override"
},
{
"method": "DELETE",
"path": "/api/v1/admin/teams/:id/leader-override/:memberKey"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/:id/unhide"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/forum/settings"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/forum/uploads"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/integrations"
},
{
"method": "PUT",
"path": "/api/v1/admin/teams/integrations"
},
{
"method": "DELETE",
"path": "/api/v1/admin/teams/integrations/:teamId"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/requests"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/requests/:id/decide"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/resync"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/review"
},
{
"method": "GET",
"path": "/api/v1/admin/teams/voice"
},
{
"method": "PUT",
"path": "/api/v1/admin/teams/voice"
},
{
"method": "DELETE",
"path": "/api/v1/admin/teams/voice/:teamId"
},
{
"method": "POST",
"path": "/api/v1/admin/teams/voice/sync"
},
{
"method": "POST",
"path": "/api/v1/admin/uploads"
@@ -477,6 +545,14 @@
"method": "PUT",
"path": "/api/v1/auth/me/notifications/subscriptions"
},
{
"method": "GET",
"path": "/api/v1/auth/me/notifications/teams"
},
{
"method": "PUT",
"path": "/api/v1/auth/me/notifications/teams"
},
{
"method": "GET",
"path": "/api/v1/auth/me/sessions"
@@ -557,38 +633,6 @@
"method": "POST",
"path": "/api/v1/auth/sso/totp"
},
{
"method": "GET",
"path": "/api/v1/player/account"
},
{
"method": "GET",
"path": "/api/v1/player/account/identities"
},
{
"method": "DELETE",
"path": "/api/v1/player/account/identities/:provider"
},
{
"method": "PATCH",
"path": "/api/v1/player/account/password"
},
{
"method": "POST",
"path": "/api/v1/player/account/totp/disable"
},
{
"method": "POST",
"path": "/api/v1/player/account/totp/enable"
},
{
"method": "POST",
"path": "/api/v1/player/account/totp/setup"
},
{
"method": "PATCH",
"path": "/api/v1/player/account/username"
},
{
"method": "GET",
"path": "/api/v1/player/appeals"
@@ -605,6 +649,66 @@
"method": "GET",
"path": "/api/v1/player/appeals/eligible"
},
{
"method": "GET",
"path": "/api/v1/player/teams"
},
{
"method": "GET",
"path": "/api/v1/player/teams/:slug/access"
},
{
"method": "PATCH",
"path": "/api/v1/player/teams/:slug/forum/posts/:id"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/posts/:id/moderate"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/report"
},
{
"method": "GET",
"path": "/api/v1/player/teams/:slug/forum/threads"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/threads"
},
{
"method": "GET",
"path": "/api/v1/player/teams/:slug/forum/threads/:id"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/threads/:id/moderate"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/threads/:id/posts"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/forum/uploads"
},
{
"method": "DELETE",
"path": "/api/v1/player/teams/:slug/forum/uploads/:id"
},
{
"method": "GET",
"path": "/api/v1/player/teams/:slug/grants"
},
{
"method": "POST",
"path": "/api/v1/player/teams/:slug/grants"
},
{
"method": "DELETE",
"path": "/api/v1/player/teams/:slug/grants/:userId"
},
{
"method": "POST",
"path": "/api/v1/public/contact"
@@ -637,6 +741,34 @@
"method": "GET",
"path": "/api/v1/public/status"
},
{
"method": "GET",
"path": "/api/v1/public/teams"
},
{
"method": "GET",
"path": "/api/v1/public/teams/:slug"
},
{
"method": "GET",
"path": "/api/v1/public/teams/:slug/activity"
},
{
"method": "GET",
"path": "/api/v1/public/teams/:slug/members"
},
{
"method": "GET",
"path": "/api/v1/public/teams/by-external/:moduleId/:externalId"
},
{
"method": "GET",
"path": "/api/v1/public/teams/unsubscribe/:token"
},
{
"method": "POST",
"path": "/api/v1/public/teams/unsubscribe/:token"
},
{
"method": "GET",
"path": "/api/v1/public/version"
@@ -674,6 +806,14 @@
{
"method": "GET",
"path": "/internal/bot-config"
},
{
"method": "GET",
"path": "/internal/commands"
},
{
"method": "POST",
"path": "/internal/commands/dispatch"
}
]
}