Seats and speakers
The seat, speak request and invitation endpoints for audio rooms.
These routes apply to audio_room rooms. Paths start with https://stream.zavryn.online/api/v1/rooms/:room_name, and they take the user_token as a bearer.
Actions marked "host or admin" return 403 Only host or admin can perform this action otherwise. An admin can't act on a host.
Seat modes
free: anyone can take an open, unlocked seat.request: everyone else must request to speak and be approved, or be invited.
Read and configure
GET /seats returns the seats and pending requests:
{
"seats": [
{ "index": 0, "identity": "host-1", "name": "Host", "locked": false, "reserved_for": "host-1", "attributes": { "avatar": "" } },
{ "index": 1, "identity": null, "name": null, "locked": false, "reserved_for": null, "attributes": null }
],
"seat_count": 9,
"seat_mode": "request",
"mode_id": null,
"requests": [{ "id": 12, "identity": "user-7", "created_at": "2026-06-08T10:00:00.000Z" }]
}POST /seats/setup reconfigures them, for a host or admin:
{ "identity": "host-1", "seat_count": 6, "seat_mode": "request", "mode_id": "game-mode" }Changing mode_id rebuilds the seats, and non-host speakers lose their seats.
Seat actions
In the actions marked "actor", identity is the person doing it.
| Endpoint | Body | Who | Common errors |
|---|---|---|---|
POST /seats/:index/take | identity | Anyone, subject to the mode | 409 Already seated, 409 Seat is not available, 403 Seat is reserved, 403 Speaker request not approved. Request to speak first. |
POST /seats/leave | identity | The occupant | 404 Not seated |
POST /seats/move | identity, target_seat | The occupant | 409 Seat is locked, 409 Target seat was taken, 403 Seat is reserved |
POST /seats/:index/kick | identity (actor) | Host or admin | 404 Seat is empty, 403 Cannot kick from a reserved seat |
POST /seats/:index/lock | identity (actor) | Host or admin | 403 Cannot lock a reserved seat |
POST /seats/:index/unlock | identity (actor) | Host or admin | 404 Seat not found or not locked |
POST /seats/:index/mute | identity (actor) | Host or admin | |
POST /seats/:index/unmute | identity (actor) | Host or admin |
Force-mute someone
PUT /participants/:identity/media, where the path has the target and the body has the actor:
{ "identity": "host-1", "audio": true, "video": false, "muted": true }Speak requests
| Endpoint | Body | Who | Common errors |
|---|---|---|---|
POST /speakers/request | identity | Anyone | 400 Room is in free mode. Take a seat directly., 409 Request already pending |
DELETE /speakers/request | identity | The requester | 404 No pending request found |
GET /speakers/requests | Anyone | ||
POST /speakers/requests/:id/approve | identity (actor) | Host or admin | 404 Request not found or already resolved |
POST /speakers/requests/:id/reject | identity (actor) | Host or admin |
Invitations
| Endpoint | Body | Who | Common errors |
|---|---|---|---|
POST /speakers/invite | identity, target_identity, optional seat_index | Host or admin | 404 Target user not in room, 409 Invitation already pending |
POST /speakers/invitations/:id/accept | identity | The target | 409 Target seat is occupied |
POST /speakers/invitations/:id/decline | identity | The target |
Lock chat
POST /comments/lock
POST /comments/unlockSend { "identity": "host-1" }, for a host or admin. Both are safe to repeat. While chat is locked, only hosts and admins can post, and gifts and likes keep working.
The kits honor the lock. If you use the media client, your app must honor it too, because the lock applies through room state rather than permissions. Read chat_locked from the room, and listen for the _chat_lock data message.
Updates in the room
Clients receive seat changes in the room metadata, under _seats, and as data messages: _seat_update, _mode_change, _custom_command, _speaker_request, _speaker_request_approved, _speaker_request_rejected, _speaker_invitation, _speaker_invitation_accepted, _speaker_invitation_declined and _chat_lock.