Rooms made on the website (hosted_room, migration 0005), the REST routes in docs/hosted-rooms.md, and the realtime room.* protocol: joining with the capacity, adult and linked-account rules, seats, numbered messages with forged senders dropped and a rate limit, closing. The Rooms page lists them, with a form to make one and the owner's edit and close. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6.5 KiB
The hub's API
apps/api (Elysia on Bun). The route definitions in apps/api/src/app.ts are the reference: their schemas validate
requests and type the website's client (Eden). This is the overview.
/api/auth/*: Better Auth (accounts, sessions, device sign-in, JWT keys)./api/v1/*: the hub's own routes.
The website reaches the API through its own origin (Next.js rewrites /api to it), so sessions are first-party
cookies. T5DE's proxy calls it directly with Authorization: Bearer <session token>.
Errors are {"error": "<code>", "message": "<text for people>"} with a 4xx/5xx status; message can be shown as
is. Codes: invalid, signed_out, forbidden, not_found, no_such_imvu_account, already_linked,
code_missing, expired, link_cap, rate_limited, imvu_unavailable.
Accounts (Better Auth)
POST /api/auth/sign-up/email |
{email, password, name, username}. Usernames 3-32 of A-Za-z0-9_; passwords 10-256 characters. The first account is an admin. |
POST /api/auth/sign-in/username |
{username, password} |
POST /api/auth/sign-out |
|
GET /api/v1/me |
{person} |
Proxies
POST /api/auth/device/code |
{client_id: "t5de-proxy"} → {device_code, user_code, verification_uri, verification_uri_complete, interval, expires_in} (RFC 8628). verification_uri is the website's /device. |
POST /api/auth/device/token |
{grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code, client_id} → {access_token} (a session token, used as a bearer token) or {error: authorization_pending | slow_down | expired_token | access_denied} |
POST /api/v1/device/approve |
(website, signed in) {code}: signs the waiting proxy in as this person |
POST /api/v1/badges |
(bearer or website session) {imvuIds} (at most 200 IMVU account IDs) → {badges}: those linked to a T5DE account that turned on showBadge for them and isn't banned. Nothing else about them. 120 a minute per person. |
GET /api/v1/realtime |
WebSocket, bearer token: presence, outfits, the room channel and hosted rooms (room.*, docs/hosted-rooms.md). Protocol in apps/api/src/realtime.ts. |
GET /.well-known/jwks.json |
the Ed25519 keys JWTs are signed with |
Website routes (signed in unless noted)
GET /api/v1/home |
(anyone) {onlineCount, links?} |
GET /api/v1/online |
(anyone) {online}: accounts showing they're online (moderators see all, with hidden) |
GET /api/v1/online/events |
(anyone) server-sent events: online with {online} now and on each change |
GET /api/v1/rooms |
(anyone) {rooms}: listed IMVU rooms of T5DE users, with the T5DE users in each now (AP rooms for adults only) |
GET /api/v1/account |
{person, links, challenges, computers, canLink} |
POST /api/v1/account/links |
{username} → 201 {challenge} (the tagline code) |
POST /api/v1/account/links/:imvu/check |
{link, moved} |
DELETE /api/v1/account/challenges/:imvu |
cancel linking |
PATCH /api/v1/account/links/:imvu |
{showBadge?, showPresence?, listRooms?} → {link} (each off by default; showBadge: others see the T5 badge on this account in the client) |
POST /api/v1/account/links/:imvu/recheck |
{link} (age checked with IMVU again) |
DELETE /api/v1/account/links/:imvu |
unlink |
POST /api/v1/account/signout-computers |
signs T5DE out on all their computers |
Hosted rooms
Rooms the hub hosts (docs/hosted-rooms.md, which has the details and the realtime part). Bearer or website session;
the GETs also signed out.
POST /api/v1/hosted-rooms |
{name, description?, roomPid, rating, maxUsers?, imvuId} → 201 {room}. imvuId: one of the person's linked accounts, shown as owner. Name 1-64 characters, description up to 512 (plain text), maxUsers 2-30 (default 10), rating general or adult (adult people only). Up to 10 open rooms a person (room_cap), 20 new ones an hour. |
GET /api/v1/hosted-rooms |
{rooms}: open rooms, newest first (at most 200); adult ones for adults only. ?mine=1: the person's own. |
GET /api/v1/hosted-rooms/:n |
{room, scene: {contents, hash}}; 404, or 410 closed |
PATCH /api/v1/hosted-rooms/:n |
(owner) {name?, description?, rating?, maxUsers?, roomPid?} → {room}. Made adult: non-adults in it are sent out. |
PUT /api/v1/hosted-rooms/:n/scene |
(owner) {contents, roomPid?} → {room, scene}. contents: up to 500 [furniture ID, product ID (integer or hub ID), placement (up to 200 characters)]. 60 a minute. |
DELETE /api/v1/hosted-rooms/:n |
(owner or moderator) closes it for good; everyone in it gets room.closed |
A room is {n, instanceId, chatId, name, description, roomPid, rating, maxUsers, ownerImvuId, ownerName, occupancy, createdAt}; instanceId is 2100000000-<n>, chatId 2100000000 + n, occupancy the people in it now.
Products, catalogs and libraries
The routes and tokens are in docs/products.md. Products are {id, name, description, rating, latest, creator, creatorUsername, parent, image, categories, createdAt, updatedAt}; parent, image and categories come from the
latest revision (its IMVU parent's public info). GET /api/v1/products takes q, creator, category (an IMVU
category ID) and limit (1-200, default 60).
Age verification
Vendor-neutral (docs/design.md, "Age"): IMVU's answer is recorded when an account is linked or checked again; person-level providers (phone wallets, document checks) are started here.
GET /api/v1/age |
{providers: [{id, label, scope}], checks}: the providers in use and the person's own checks |
POST /api/v1/age/:provider/start |
{returnURL} → {kind: "done"}, {kind: "redirect", url, ref} (send the person there) or {kind: "browser", request, ref} (hand request to the browser's Digital Credentials API) |
POST /api/v1/age/:provider/complete |
{ref, response}: the browser's answer, for wallet providers |
POST /api/v1/age/webhook/:provider |
the provider's webhook; the provider checks its signature |
Console (moderators; roles: admins)
GET /api/v1/console |
{counts, connections, online, audit} |
GET /api/v1/console/people?q= |
{people} |
GET /api/v1/console/people/:id |
{person, links, computers} |
POST /api/v1/console/people/:id/link-cap |
{linkCap} (0-1000) |
POST /api/v1/console/people/:id/ban |
{reason}; only admins ban moderators |
POST /api/v1/console/people/:id/unban |
|
POST /api/v1/console/people/:id/role |
(admins) {role} |