> ## Documentation Index
> Fetch the complete documentation index at: https://docs.technified.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Server Manager

> The API-key command queue that powers live server control from the dashboard.

[Server Manager](/dashboard/roblox/server-manager) lets staff control live Roblox servers from the dashboard: see who is online, kick, ban, mute, message, broadcast, or shut a server down. It works as a **command queue**, and your game server speaks to it with a guild API key.

<Info>
  The queue endpoints (`servers/heartbeat`, `servers/:serverId/commands`, and the command `ack`) require the `server:write` scope. Quick-installed plugin keys have it automatically. See [Scopes](/developer-api/introduction#scopes).
</Info>

## How the queue works

```
Dashboard (staff clicks Kick)        Your Roblox server (API key)
        |                                     |
        |  enqueues a command                 |  1. heartbeat: report state + players
        v                                     |  2. read pending commands from the
   server_commands  <------------ heartbeat --+     heartbeat response
        |                                     |  3. run each command in-game
        +------------------ ack --------------+  4. ack: report the result
```

<Note>
  Only these three endpoints accept a guild API key. The dashboard side, listing servers and enqueuing a command (`/guilds/:guildId/servers...`), requires a logged-in dashboard session with the `server_manager` permission and is not callable with an API key.
</Note>

These endpoints use camelCase field names, like the rest of the [plugin protocol](/developer-api/plugin-protocol).

## Heartbeat

Report a live server and its current player list. Send this on a short loop, for example every 10 seconds (a server counts as stale after 30 seconds without a heartbeat). It keeps the server marked online, feeds the player list shown in the dashboard, and its response carries any pending commands for this server, so no separate poll loop is needed.

```http theme={null}
POST /roblox/servers/heartbeat
```

<ParamField body="guildId" type="string" required>
  Must match your API key's guild.
</ParamField>

<ParamField body="serverId" type="string" required>
  The Roblox `game.JobId`.
</ParamField>

<ParamField body="placeId" type="string" required />

<ParamField body="universeId" type="string" />

<ParamField body="gameName" type="string" />

<ParamField body="playerCount" type="number" required />

<ParamField body="maxPlayers" type="number" required />

<ParamField body="serverAge" type="number">
  Seconds since the server started.
</ParamField>

<ParamField body="fps" type="number" />

<ParamField body="players" type="object[]" required>
  The current players.

  <Expandable title="player object">
    <ParamField body="robloxId" type="string" required />

    <ParamField body="username" type="string" required />

    <ParamField body="displayName" type="string" />

    <ParamField body="joinTime" type="number" required>
      Unix timestamp when the player joined.
    </ParamField>

    <ParamField body="team" type="string" />
  </Expandable>
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl "https://api.technified.xyz/api/v1/roblox/servers/heartbeat" \
    -H "X-API-Key: $TECHNIFIED_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "guildId": "'$GUILD_ID'",
      "serverId": "abc123-...",
      "placeId": "987654321",
      "gameName": "My Game",
      "playerCount": 2,
      "maxPlayers": 50,
      "serverAge": 3600,
      "players": [
        { "robloxId": "261", "username": "Shedletsky", "joinTime": 1746712900 }
      ]
    }'
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "status": "success",
    "code": 200,
    "data": {
      "success": true,
      "commands": [
        {
          "id": 42,
          "type": "kick",
          "targetRobloxId": "261",
          "targetUsername": "Shedletsky",
          "parameters": { "reason": "AFK" },
          "issuedByUsername": "Febreeze"
        }
      ]
    }
  }
  ```
</ResponseExample>

Players missing from a heartbeat are treated as having left and are removed from the server's live list. `data.commands` holds up to 20 pending commands (oldest first, including broadcast commands targeted at every server); they are marked as delivered on return, so execute and [acknowledge](#acknowledge-a-command) each one.

## Poll for commands

Alternative to heartbeat-delivered commands: fetch the pending commands queued for this server on a dedicated loop. The response also includes broadcast commands targeted at every server. Polled commands are marked as delivered. If you already send heartbeats, you do not need this endpoint.

```http theme={null}
GET /roblox/servers/:serverId/commands
```

<ParamField path="serverId" type="string" required>
  The same `game.JobId` you heartbeat with.
</ParamField>

<ResponseField name="data.commands" type="object[]">
  Up to 20 pending commands, oldest first.

  <Expandable title="command object">
    <ResponseField name="id" type="number">
      The command ID, needed to acknowledge it.
    </ResponseField>

    <ResponseField name="type" type="string">
      One of `kick`, `ban`, `mute`, `message`, `teleport`, `broadcast`, `shutdown`.
    </ResponseField>

    <ResponseField name="targetRobloxId" type="string | null">
      The target player, for player commands.
    </ResponseField>

    <ResponseField name="targetUsername" type="string | null" />

    <ResponseField name="parameters" type="object">
      Command-specific data, for example `reason`, `duration`, or `message`.
    </ResponseField>

    <ResponseField name="issuedByDiscordId" type="string" />

    <ResponseField name="issuedByUsername" type="string" />
  </Expandable>
</ResponseField>

<ResponseExample>
  ```json 200 theme={null}
  {
    "status": "success",
    "code": 200,
    "data": {
      "commands": [
        {
          "id": 17,
          "type": "kick",
          "targetRobloxId": "261",
          "targetUsername": "Shedletsky",
          "parameters": { "reason": "Breaking rules" },
          "issuedByDiscordId": "123456789012345678",
          "issuedByUsername": "Builderman"
        }
      ]
    }
  }
  ```
</ResponseExample>

### Command types

| Type        | Scope  | Common parameters    |
| ----------- | ------ | -------------------- |
| `kick`      | Player | `reason`             |
| `ban`       | Player | `reason`, `duration` |
| `mute`      | Player | `reason`, `duration` |
| `message`   | Player | `message`            |
| `teleport`  | Player | destination data     |
| `broadcast` | Server | `message`            |
| `shutdown`  | Server | none                 |

Player commands carry a `targetRobloxId`. Server-level commands like `broadcast` and `shutdown` do not. A broadcast is queued for every server at once.

## Acknowledge a command

After running a command, report the outcome. This moves the command to `acknowledged` so it is not retried, and surfaces the result in the dashboard.

```http theme={null}
POST /roblox/servers/:serverId/commands/:commandId/ack
```

<ParamField path="serverId" type="string" required />

<ParamField path="commandId" type="number" required>
  The `id` from the poll response.
</ParamField>

<ParamField body="success" type="boolean" required />

<ParamField body="result" type="string">
  A short human-readable result, for example "Kicked Shedletsky".
</ParamField>

<ParamField body="error" type="string">
  An error message when `success` is `false`.
</ParamField>

<RequestExample>
  ```bash cURL theme={null}
  curl "https://api.technified.xyz/api/v1/roblox/servers/abc123-.../commands/17/ack" \
    -H "X-API-Key: $TECHNIFIED_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "success": true, "result": "Kicked Shedletsky" }'
  ```
</RequestExample>

<Tip>
  If you run the Roblox integration or Technified Admin, this whole loop is already implemented. You only need these endpoints when building a custom admin system.
</Tip>
