REST API Reference

The UrsaMU REST API runs on the same port as the WebSocket hub (default 4203).


Authentication

All endpoints require a Bearer JWT in the Authorization header unless marked None.

Obtain a token via POST /api/v1/auth/login. Tokens are signed with JWT_SECRET — set this in production.

Authorization: Bearer <token>

Error responses

Status Meaning
400 Bad request — missing or invalid body fields
401 Missing or invalid token
403 Valid token, insufficient permissions (flag check failed)
404 Resource not found
429 Rate limited
500 Internal server error

All error bodies follow the same shape:

{ "error": "Human-readable message" }

WebSocket Connection

The WebSocket hub is on the same port as HTTP (4203). Two connection modes are supported:

JWT pre-auth (web clients)

Attach a token as a query parameter. The player is authenticated immediately — no
connect name password prompt needed.

ws://localhost:4203?token=<jwt>&client=web

Classic connect (Telnet-style)

Connect without a token. The server sends the connect screen. Authenticate with:

connect <name> <password>

Rate limiting

Each WebSocket connection is limited to 10 commands per second. Excess commands
are silently dropped (a warning is logged server-side). This cannot be changed per
connection — if you need higher throughput, batch commands or use the REST API.


Auth Endpoints

POST /api/v1/auth/register

Create a new character account. Returns a JWT.

curl -X POST http://localhost:4203/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "password": "hunter2"}'
{ "token": "<jwt>" }

POST /api/v1/auth/login

Authenticate and receive a JWT.

curl -X POST http://localhost:4203/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"name": "Alice", "password": "hunter2"}'
{ "token": "<jwt>" }

POST /api/v1/auth/reset-password

Consume a one-time reset token and set a new password.

curl -X POST http://localhost:4203/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{"token": "<one-time-token>", "password": "newpassword"}'
{ "message": "Password reset successfully." }

Players & Channels

GET /api/v1/me

Current player profile (requires auth).

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/me
{
  "id": "p-1",
  "name": "Alice",
  "flags": ["connected", "player"],
  "data": { "description": "A mysterious figure.", "avatar": "/avatars/alice.png" }
}

GET /api/v1/players/online

List connected players. No auth required.

curl http://localhost:4203/api/v1/players/online
[
  { "id": "p-1", "name": "Alice", "location": "The Lobby" },
  { "id": "p-2", "name": "Bob",   "location": "The Library" }
]

GET /api/v1/channels

List all channels.

curl http://localhost:4203/api/v1/channels
[
  { "name": "Public", "header": "[Public]", "members": 4 },
  { "name": "Staff",  "header": "[Staff]",  "members": 2 }
]

GET /api/v1/channels/:name/history

Recent message history for a channel (requires auth).

curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:4203/api/v1/channels/Public/history?limit=20"
[
  { "sender": "Alice", "message": "Hello everyone!", "timestamp": 1750536000000 },
  { "sender": "Bob",   "message": "Hey Alice.",       "timestamp": 1750536010000 }
]

Database Objects

GET /api/v1/dbos

List accessible objects. Optional ?flags= filter.

curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:4203/api/v1/dbos?flags=room"

GET /api/v1/dbobj/:id

Fetch a single game object by its DB id.

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/dbobj/p-1
{
  "id": "p-1",
  "name": "Alice",
  "location": "room-1",
  "flags": ["connected", "player"],
  "data": { "description": "…", "state": {} },
  "contents": []
}

PATCH /api/v1/dbobj/:id

Update object data, name, or description (requires ownership or admin).

curl -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"data": {"description": "A tall figure in a green cloak."}}' \
  http://localhost:4203/api/v1/dbobj/p-1

Scenes

GET /api/v1/scenes

List active scenes (requires auth).

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/scenes
[
  {
    "id": "sc-1",
    "name": "The Heist",
    "status": "active",
    "type": "action",
    "roomId": "room-5",
    "participants": ["Alice", "Bob"],
    "createdAt": 1750536000000
  }
]

POST /api/v1/scenes

Create a new scene (requires auth).

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "The Heist", "type": "action", "roomId": "room-5"}' \
  http://localhost:4203/api/v1/scenes

GET /api/v1/scenes/:id

Get scene details with pose log and participants.

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/scenes/sc-1

PATCH /api/v1/scenes/:id

Update scene metadata (owner or admin).

curl -X PATCH \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "closed"}' \
  http://localhost:4203/api/v1/scenes/sc-1

GET /api/v1/scenes/:id/export

Export a scene as Markdown or JSON.

# Markdown (for copy-paste into a wiki or Google Doc)
curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:4203/api/v1/scenes/sc-1/export?format=markdown"

# JSON (for external tools)
curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:4203/api/v1/scenes/sc-1/export?format=json"

POST /api/v1/scenes/:id/pose

Add a pose, OOC comment, or scene-set to a scene.

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"msg": "Alice steps through the door.", "type": "pose"}' \
  http://localhost:4203/api/v1/scenes/sc-1/pose

type values: "pose" (default), "ooc", "set" (scene description).

POST /api/v1/scenes/:id/join

Join a scene (requires auth).

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:4203/api/v1/scenes/sc-1/join

POST /api/v1/scenes/:id/invite

Invite a player to a scene (owner only).

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"playerId": "p-3"}' \
  http://localhost:4203/api/v1/scenes/sc-1/invite

Mail

Provided by the mail-plugin.

GET /api/v1/mail

Inbox (requires auth).

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/mail
[
  {
    "id": "ml-1",
    "from": "Bob",
    "subject": "Meeting tonight",
    "read": false,
    "receivedAt": 1750536000000
  }
]

POST /api/v1/mail

Send a new message.

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"to": "Bob", "subject": "Re: Meeting", "body": "I will be there!"}' \
  http://localhost:4203/api/v1/mail

GET /api/v1/mail/:id

Read a message (marks as read).

curl -H "Authorization: Bearer $TOKEN" http://localhost:4203/api/v1/mail/ml-1

DELETE /api/v1/mail/:id

Delete a message.

curl -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:4203/api/v1/mail/ml-1

Help

Provided by the help-plugin.

GET /api/v1/help

List all help sections and topics. No auth required.

curl http://localhost:4203/api/v1/help
{
  "sections": {
    "building": ["dig", "open", "link", "describe", "examine"],
    "mail":     ["send", "reply", "delete", "folders"],
    "social":   ["say", "pose", "page"]
  }
}

GET /api/v1/help/:topic

Fetch a single topic. Use ?format=md for raw Markdown.

# Rendered (MUSH color codes stripped)
curl http://localhost:4203/api/v1/help/dig

# Raw Markdown
curl "http://localhost:4203/api/v1/help/dig?format=md"

POST /api/v1/help/:topic

Create or update a help entry (admin).

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"content": "# DIG\n\nCreates a new room.", "section": "building"}' \
  http://localhost:4203/api/v1/help/dig

DELETE /api/v1/help/:topic

Delete a help entry (admin). Restores the underlying file entry if one exists.

curl -X DELETE \
  -H "Authorization: Bearer $TOKEN" \
  http://localhost:4203/api/v1/help/dig

Building

Provided by the builder-plugin. Requires builder+ flag.

POST /api/v1/building/room

Create a new room.

curl -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "The Library", "description": "Shelves of ancient tomes."}' \
  http://localhost:4203/api/v1/building/room

Wiki

Provided by the wiki-plugin.

GET /api/v1/wiki

List all wiki topics. No auth required.

curl http://localhost:4203/api/v1/wiki

GET /api/v1/wiki/:topic

Retrieve a wiki page. No auth required.

curl http://localhost:4203/api/v1/wiki/lore/history

Config & Text

GET /api/v1/config

Server config (name, version, ports, theme). No auth required.

curl http://localhost:4203/api/v1/config
{
  "game": { "name": "My Game", "version": "0.0.1" },
  "server": { "http": 4203, "telnet": 4201 }
}

GET /api/v1/connect

Connect-screen text (raw, suitable for display in a login screen).

curl http://localhost:4203/api/v1/connect

GET /api/v1/welcome

Welcome text shown after login.

curl http://localhost:4203/api/v1/welcome

Health

GET /health

No auth required. Returns immediately; useful for load balancer health checks.

curl http://localhost:4203/health
{ "status": "ok", "engine": "UrsaMU" }

Plugin Endpoints

Official plugins add versioned routes on install:

Plugin Base path Full docs
jobs /api/v1/jobs UrsaMU/jobs-plugin
events /api/v1/events Events plugin
bbs /api/v1/bbs UrsaMU/bbs-plugin

Custom plugins register routes via registerPluginRoute(path, handler) — see Plugin Development.