Skip to content

Connector protocol

A connector is a program (game plugin, addon or mod) that keeps Inari informed of where players are. It talks to Inari over the same WebSocket endpoint as browsers: /ws.

Every message is a JSON text frame with the same envelope:

{
"kind": "auth",
"data": {},
"transaction_uuid": "6f1c0a9e-3a8b-4c53-a9a1-5e2c0f2d1b77"
}
Field Description
kind Message type, in snake_case.
data Payload, depends on kind.
transaction_uuid Chosen by you. The response to a request carries the same value, so you can match them.

Invalid JSON or unknown messages are silently ignored.

Must be the first message. It can only be sent once per connection.

Request

{
"kind": "auth",
"data": {
"is_connector": true,
"auth_key": "<API_KEY>",
"channel": "default"
},
"transaction_uuid": "..."
}
Field Type Description
is_connector boolean true for a connector.
auth_key string The server’s API_KEY.
channel string Channel to manage.

Response

{
"kind": "auth",
"data": { "max_distance": 20, "app_version": "1.0.0" },
"transaction_uuid": "..."
}

Send the full list of players of your game server. Call it regularly, about every 300 ms: it drives who hears whom. Players of the channel that are not in the list are not linked to a character.

Request

{
"kind": "sync_connector_state",
"data": {
"clients": [
{
"code": "a1b2c3d4",
"display_name": "Steve",
"position": [120.5, 64.0, -30.2],
"head": [1, 0, 0],
"is_force_muted": false,
"group": "team-red",
"actions": { "toggle_muted": false, "toggle_deafened": false }
}
]
},
"transaction_uuid": "..."
}
Field Type Required Description
code string yes The code the player used to join.
position [x, y, z] yes Position of the character, in your game’s units. Compared with MAX_DISTANCE.
head [x, y, z] yes Direction the player is looking at, used to place the voices around the listener. Must be a unit vector (length 1, each component between -1 and 1), like Minecraft Bedrock’s getViewDirection(). For example [1, 0, 0] looks along the X axis.
is_force_muted boolean yes When true, nobody hears this player.
display_name string no Name shown on the web page.
group string no Only players of the same group hear each other. Defaults to default.
actions.toggle_muted boolean no true toggles the player’s muted state.
actions.toggle_deafened boolean no true toggles the player’s deafened state.

Response

{
"kind": "sync_connector_state",
"data": {
"connections": [
{
"code": "a1b2c3d4",
"display_name": "Steve",
"is_connector": false,
"is_force_muted": false,
"is_muted": false,
"is_deafened": false
}
]
},
"transaction_uuid": "..."
}

connections lists everyone connected to the channel, which lets you show a player’s muted/deafened status in game.

Errors use the error kind and the transaction_uuid of the failed request.

{
"kind": "error",
"data": {
"message": "UNAUTHORIZED",
"details": "The API key provided is invalid.",
"code": 401
},
"transaction_uuid": "..."
}
Code message Cause
400 BAD_REQUEST Invalid client code (shorter than 6 characters or already used).
401 UNAUTHORIZED Invalid API key, or a non-connector sent a connector message.
409 ALREADY_CONNECTED auth was already sent on this connection.
import { WebSocket } from "ws";
import { v4 as uuid } from "uuid";
const ws = new WebSocket("wss://inari.example.com/ws");
const send = (kind: string, data: unknown) =>
ws.send(JSON.stringify({ kind, data, transaction_uuid: uuid() }));
ws.on("open", () => {
send("auth", { is_connector: true, auth_key: process.env.INARI_API_KEY, channel: "default" });
setInterval(() => {
send("sync_connector_state", { clients: getPlayers() }); // your game data
}, 300);
});