Updated

Chat relay

Relaying in-game chat into the site, injecting the site's chat back into the game, and telling the site which console commands your server accepts.

Without a relay, a party’s conversation sits in a room none of the players in the match can see. These two endpoints close that loop.

In-game chat → the site

POST/api/integration/v1/chatIntegrationCHAT_WRITE

Relay a line of in-game chat.

{
  "partyId": "4471",
  "player": { "name": "Ashley", "steamId": "76561198000000000" },
  "body": "nice shot",
  "ts": 1753822850
}

Omit partyId to post into the server’s own in-game relay room instead. That form requires a server-scoped credential.

An author we cannot match keeps their in-game name and links nowhere. A message is never rendered as though a site member said it unless we actually resolved one.

The site → in-game

GET/api/integration/v1/chat/outboundIntegrationCHAT_READ

Lines typed on the site that should be injected into the game.

GET /api/integration/v1/chat/outbound?since=8813&limit=50
{
  "ok": true,
  "messages": [
    {
      "id": "8814",
      "at": "2026-07-29T20:41:02.000Z",
      "author": "Ashley",
      "authorId": "clx…",
      "body": "rotating to B",
      "muted": false
    }
  ],
  "newest": "8814"
}

Server-scoped credentials only. It serves the server’s own relay room plus the room of any live party on that server.

The cursor is an id, not a timestamp

Poll with the highest id you have already injected. Two messages can share a millisecond, and a timestamp cursor would either skip or re-deliver a line.

Calling it with no since returns the most recent page, so a freshly restarted mod does not replay the whole history into the game.

What comes back

Only site-authored messages. Lines that came from the game are excluded — echoing them would loop — and so are system lines, which are our own bookkeeping and mean nothing in-game. Deleted and hidden messages are excluded too.

author is the writer’s site name, so you can label the line in-game as coming from the website rather than from a player standing next to them.

muted: true means the author was not in the party when they wrote it. The site renders those dimmed, and you may want to skip or de-emphasise them too.

Telling the site what commands you accept

POST/api/integration/v1/chat/commandsIntegrationCHAT_WRITE

What this server accepts as console commands, so the site’s chat composer can offer them when a member types /. Server-scoped credentials only.

The list comes from the server because only the server has it. A member typing / into a relayed game chat is typing at a machine we do not control, whose command table depends on which game is loaded and which modules an operator installed — and a hard-coded list here would be stale the first time either changed.

commandsarrayrequired

Up to 256. Each has a name (one word, no prefix on it), and optionally usage (<map_id>, [reason]), a one-line description, chatAllowed, and permission.

prefixstring

The prefix members type in front of them — for the menu’s label only. The site’s own trigger is / and stays / whatever this says: it is what a member’s keyboard does, not what your server parses. A server whose prefix is ! still gets a / menu here and receives the line it expects, because the relay writes the prefix on the way in.

The payload is absolute, not incremental: it replaces what we hold. A command an operator removed has to disappear from the menu, and a merge has no way to say so. An empty list is how a server turns the menu off.

Two things happen on our side that are worth knowing. Names are deduplicated case-insensitively before the write, so a payload naming map and Map does not fail the whole batch — it would otherwise read to an operator as “the site refuses my command list” rather than “you listed one twice”. And the replace is one transaction, so a server reporting while a member has the menu open can never show them an empty list followed by a full one.

An app-scoped credential is refused with a 403: it covers every server the app has, and a command list posted under one would have no server to belong to.

Retention

Relayed chat has no per-report ceiling, and that is not an oversight: relaying is what the endpoint is for, so the only honest bound on it is your request rate.

It is bounded from the other end instead — a pruning job trims relayed lines on a retention window, 30 days by default.