HTTP API Reference

All API endpoints require an authenticated session unless noted otherwise. Authentication is performed by the /login route, which sets a signed session cookie. Subsequent requests include this cookie automatically.

All JSON responses use Content-Type: application/json.

Authentication

GET /login

Render the login page.

Response Headers:
POST /login

Authenticate a user and establish a session.

Form Parameters:
  • username – Account username.

  • password – Account password.

Status Codes:
  • 302 Found – Redirect to / on success.

  • 200 OK – Re-render login page with error message on failure.

Response Headers:
  • Set-Cookiesession=<signed_value> on success.

Note

Failed login attempts are deliberately delayed by 3 seconds to slow brute-force attacks. Repeated failures additionally trigger account lockout: after max_login_attempts consecutive failures an account is locked for lockout_duration_minutes (both configurable in settings.json).

GET /logout

Log out the current user, set presence to offline, and clear the session.

Requires authentication.

Status Codes:
GET /signup

Render the registration page.

Response Headers:
POST /signup

Register a new account.

Form Parameters:
  • username – 1–32 characters; letters, numbers, hyphens, underscores. The reserved names minimost, everyone, and deleteduser are rejected (case-insensitively).

  • password – Minimum 8 characters; must include uppercase, digit, and special character.

  • confirm_password – Must match password.

Status Codes:
  • 302 Found – Redirect to / on success.

  • 200 OK – Re-render signup page with error on validation failure.

Chat Interface

GET /

Serve the main chat SPA.

Requires authentication.

Response Headers:

Channels

GET /channels

Return the list of public channel names.

Requires authentication.

Response JSON Object:
  • channels (array) – Ordered list of channel name strings.

Example response:

["general", "software", "firmware", "off-topic"]
GET /channel_unreads

Return unread message counts for every public channel.

Requires authentication.

Response JSON Object:
  • object – Mapping of channel name to unread count.

Example response:

{"general": 3, "software": 0, "off-topic": 1}

Messages

GET /messages/(channel)

Fetch messages for a channel since a given timestamp.

Requires authentication.

Parameters:
  • channel – Public channel name or DM identifier (e.g. dm:alice:bob).

Query Parameters:
  • after (float) – Only return messages modified after this Unix timestamp. Defaults to 0 (return all messages). Pass NaN to also trigger a full load.

Response JSON Object:
  • array – Array of message objects.

Message object fields:

Field

Type

Description

id

integer

Database primary key (in the current user’s database).

channel

string

Channel or DM identifier.

sender

string

Username of the author.

content

string or null

Message text body. null for image-only messages.

filename

string or null

UUID-named image filename, served from /files/<filename>.

ts

float

Unix timestamp (seconds).

edited

integer (0/1)

Whether the message has been edited.

edited_ts

float or null

Timestamp of the most recent edit.

deleted

integer (0/1)

Soft-delete flag.

deleted_ts

float or null

Timestamp of deletion.

reply_to_id

integer or null

ID of the parent message (in the same user’s database).

reactions

string (JSON) or null

JSON-encoded object mapping emoji names to lists of reactor usernames, e.g. "{\"thumbs_up\": [\"alice\", \"bob\"]}"

reactions_ts

float or null

Timestamp of the most recent reaction change.

mentions

string (JSON) or null

JSON-encoded array of the channel members @-mentioned in the message, e.g. "[\"alice\"]". The sentinel "@everyone" marks a channel-wide mention. null when the message mentions no one. The client highlights the message and notifies the viewer when their username (or @everyone) appears here.

POST /send/(channel)

Send a message and/or image attachment(s) to a channel.

Requires authentication.

Any @username tokens in text that resolve to real channel members are extracted (case-insensitively) and stored in the message’s mentions column; @everyone is stored as the sentinel "@everyone". Tokens inside emails (foo@bar) and URLs are ignored.

Parameters:
  • channel – Target channel or DM identifier.

Form Parameters:
  • text – Message text body (optional if files are provided).

  • reply_to_id – Integer ID of the parent message (optional).

  • files – One or more image files (multipart). Accepted extensions: .jpg, .jpeg, .png, .gif, .webp.

Status Codes:
GET /message/(msg_id)

Fetch a single message by ID.

Requires authentication.

Parameters:
  • msg_id (int) – Message primary key in the current user’s database.

Response JSON Object:
  • id (integer) – Message ID.

  • sender (string) – Author’s username.

  • content (string) – Message text.

  • filename (string) – Image filename or null.

  • deleted (integer) – Soft-delete flag.

Status Codes:
POST /edit/(msg_id)

Edit the text content of a message.

Requires authentication. Only the original sender can edit.

Parameters:
  • msg_id (int) – Message ID in the current user’s database.

Form Parameters:
  • text – Replacement message text. Mentions are re-extracted from the new text and the mentions column is updated accordingly.

Status Codes:
POST /delete/(msg_id)

Soft-delete a message.

Requires authentication. Only the original sender can delete.

Parameters:
  • msg_id (int) – Message ID in the current user’s database.

Status Codes:
GET /search_messages

Search message history by keyword.

Requires authentication.

Query Parameters:
  • q (string) – Search term (substring match).

Response JSON Object:
  • array – Up to 50 matching message objects (fields: id, channel, sender, content, ts), newest first.

Reactions

POST /react/(msg_id)

Toggle an emoji reaction on a message.

Requires authentication.

Parameters:
  • msg_id (int) – Message ID in the current user’s database.

Form Parameters:
  • reaction – Emoji name (e.g. thumbsup, heart). Must be a valid reaction name from the VALID_REACTIONS set.

Response JSON Object:
  • object – Current reactions map after the toggle, e.g. {"thumbs_up": ["alice", "bob"]}

Status Codes:

Users and Presence

GET /users

Return all registered users except the current user.

Requires authentication.

Response JSON Object:
  • array – List of username strings.

GET /channel_members/(channel)

Return the members of a channel that the current user may @-mention, excluding the current user. For public channels this is every other registered user; for private channels the other members; for DMs the other participants. Used to populate the @-mention autocomplete dropdown.

Requires authentication.

Parameters:
  • channel – Channel name, DM identifier, or private:<id>.

Response JSON Object:
  • array – List of mentionable username strings.

Status Codes:
GET /user_colors

Return the display name colour for every user that has set one.

Requires authentication.

Response JSON Object:
  • object – Mapping of username to CSS hex colour string.

Example response:

{"alice": "#e06c75", "bob": "#61afef"}
GET /user_avatars

Return the set of usernames that have a custom avatar.

Requires authentication.

Response JSON Object:
  • array – List of username strings that have uploaded an avatar.

GET /online_users

Return presence states for recently active users.

Requires authentication.

Response JSON Object:
  • object – Mapping of username to presence state string ("active", "idle", "hidden", "offline").

Example response:

{"alice": "active", "bob": "idle", "charlie": "offline"}
POST /presence

Update the current user’s presence state.

Requires authentication.

Request JSON Object:
  • state (string) – One of "active", "idle", "hidden", "offline".

Status Codes:
GET /typing/(channel)

Return users currently typing in a channel.

Requires authentication.

Parameters:
  • channel – Channel or DM identifier.

Response JSON Object:
  • array – List of username strings. Excludes the current user.

POST /typing/(channel)

Report that the current user is typing.

Session is checked silently (not @login_required).

Parameters:
  • channel – Channel or DM identifier.

Status Codes:

Direct Messages

GET /dms

Return a summary of all visible DM conversations.

Requires authentication.

Hidden conversations (closed by the user) are excluded unless a new message has arrived after the conversation was hidden.

Response JSON Object:
  • array

    List of conversation objects, sorted by most recent activity. Each object has:

    • channel (string): DM channel identifier.

    • users (array): Other participant usernames.

    • unread (integer): Unread message count.

POST /dms/close

Hide a DM conversation from the sidebar.

Requires authentication.

The conversation is not deleted. It reappears automatically when a new message is received after the hidden timestamp.

Request JSON Object:
  • channel (string) – DM channel identifier to hide.

Status Codes:
GET /unread_count

Return the total number of unread DMs.

Requires authentication.

Response JSON Object:
  • count (integer) – Total unread DM message count.

Read Receipts

POST /mark_read/(channel)

Mark all messages in a channel as read.

Requires authentication.

Parameters:
  • channel – Channel or DM identifier.

Status Codes:
GET /read_receipts/(channel)

Return read receipts for all messages in a channel.

Requires authentication.

Parameters:
  • channel – Channel or DM identifier.

Response JSON Object:
  • object – Mapping of message timestamp strings to lists of reader usernames.

Example response:

{
    "1716000000.123": ["alice", "bob"],
    "1716000001.456": ["alice"]
}

User Settings

GET /settings

Return the current user’s settings.

Requires authentication.

Response JSON Object:
  • name_color (string) – CSS hex colour string, or null if not set.

  • avatar_file (string) – Avatar filename, or null if not set.

POST /settings

Update the current user’s settings.

Requires authentication.

Form Parameters:
  • name_color – CSS hex colour in #rrggbb format (optional). Pass an empty string to clear the colour.

Status Codes:

Avatars

GET /avatar/(username)

Serve a user’s profile avatar image.

Requires authentication.

Parameters:
  • username – Account username.

Response Headers:
Status Codes:
POST /avatar

Upload or replace the current user’s avatar.

Requires authentication.

The image should be pre-resized to 128 × 128 px by the client before upload (the frontend uses the Canvas API for this). The server stores the file as-is.

Form Parameters:
  • avatar – Image file (multipart/form-data). Accepted extensions: .jpg, .jpeg, .png, .gif, .webp.

Status Codes:
DELETE /avatar

Delete the current user’s avatar. The default initials avatar is shown to other users after removal.

Requires authentication.

Status Codes:

Private Channels

POST /private_channels/(channel_id)/leave

Leave a private channel.

Requires authentication.

A system message is posted to the channel notifying remaining members. If the leaving user is the last member, the channel is not automatically deleted — it becomes an empty room.

Parameters:
  • channel_id – Private channel identifier (private:<name> form).

Status Codes:

Files

GET /files/(filename)

Serve an uploaded image file.

Requires authentication.

Parameters:
  • filename – UUID-based image filename (as stored in the database).

Response Headers:
Status Codes: