Documentation
Multi-party voice Room
Preview: features and APIs may change.
PDF uses your browser’s print dialog. It is not a hosted export service.
Three or four people can talk in one voice Room, each from their own browser. A Room holds up to four people. LiveKit carries the audio, and OneSay decides who may join, speak and listen: the server checks every step, whatever the client sends. The Console has a ready-made Voice room panel that walks you through these steps. This page explains them, so that you can build the same flow in your own application.
This is a preview with a fixed group. Read the current limits at the end of this page before you invite people.
What you need#
- A Project Environment on Staging, and an Application in it.
- For each person: their own OneSay sign-in through your application, which gives a short-lived Human token (never put an API key in a browser), and a microphone Source.
- For each person, these Grants in that Project Environment:
room.read,participant.read,room.join,source.use,signal.audio.publish,rtc.subscription.configure,signal.audio.subscribe,signal.audio.end,app.session.createandapp.session.close. The person who creates the Room also needsroom.createandroom.close.
Steps#
Each person walks these steps in order. Join one at a time: wait until the previous person appears in the participant list (listRoomParticipants; in the Console, under Who is here) before you join; people who join or start listening at the same moment can fail to connect or stay unheard.
- Create or join the Room. One person creates it with
POST /rooms(createRoom), withmax_participantsset to the group size (3 or 4), and shares itsroom_id. The others read it withGET /rooms/{room_id}(getRoom) to check that it is open and has not started. - Start an app session with
POST /app-sessions(createAppSession) for your Application. - Connect with the SDK's
client.rtc.connect(createRTCConnection). Nobody is heard yet. - Publish your microphone with
session.audio.publishMicrophone(createRTCPublication) and your microphone Source. - Let the next person join. The next person starts step 3 only when you appear in the participant list (step 6).
- Read the roster.
GET /rooms/{room_id}/participants(listRoomParticipants) lists who is in the Room, andGET /rooms/{room_id}/signals(listRoomSignals) lists their audio. A person is speaking when one of their signals has the profileaudio.rtc.v1and the statusactive. Name onlyactivesignals: apendingone is not published yet. - Listen, once. When everyone is speaking, each person sets one subscription
with
session.audio.setSubscriptions(updateRTCSubscriptions): thepub_ids from thepublication_idfield of the others'activesignals, sorted, withexpected_policy_version0. Start listening one person at a time, a few seconds apart, and do not change the set afterwards. - Turn on sound with
session.audio.enablePlayback, called from the person's own click, as browsers require. - Leave. End the microphone publication (
endRTCPublication), then close the session, which revokes the connection (revokeRTCConnection), and close the app session (closeAppSession). The creator may also close the Room (closeRoom).
JavaScript#
This sketch uses only the public JavaScript SDK. humanToken, the ids and the
idempotency keys come from your application; keep each key, so that a retry of the
same step sends the same key.
import { OneSayClient } from "@onesay/sdk";
// A short-lived Human token from your own sign-in; never an API key.
const client = new OneSayClient({ environment: "staging", tokenProvider: async () => humanToken });
// Step 3: connect with your app session.
const session = await client.rtc.connect({ roomId, appSessionId, idempotencyKey: connectKey });
// Step 4: publish your microphone Source.
const microphone = await session.audio.publishMicrophone({ sourceId, idempotencyKey: publishKey });
// Step 7: when everyone speaks, listen once to the others' active publications.
await session.audio.setSubscriptions({ publicationIds: otherPublicationIds, expectedPolicyVersion: 0, idempotencyKey: listenKey });
// Step 8: sound starts from the person's own click.
soundButton.onclick = () => session.audio.enablePlayback();
// Step 9: leave.
await microphone.end({ idempotencyKey: endKey });
await session.close({ idempotencyKey: closeKey });The Room and the app session are created through the Control API
(createRoom, createAppSession), as in step 1 and step 2.
Consent#
On Staging, every step that creates, changes or ends something asks the person for
confirmation first: creating the Room, starting the app session, connecting,
publishing, listening and each part of leaving. Your application asks OneSay for a
confirmation of that exact step (createConsentRequest), shows it, records the
person's own Allow or Deny (decideConsentRequest) and then runs the step with the
same input. Nothing is allowed by default, and reads such as the roster need no
confirmation. The Console's Voice room panel shows this pattern.
Current limits (preview)#
- A fixed group. The creator picks three or four people. Everyone joins and speaks before anyone listens, and each person sets their subscription once.
- Someone leaving no longer ends the others' connections, but the group cannot change. Nobody can be added or removed in this preview: do not change your subscription set after someone leaves. To talk again as a different group, leave and start a new Room.
- One at a time. Join one at a time: wait until the previous person appears in the participant list (
listRoomParticipants; in the Console, under Who is here) before you join; people who join or start listening at the same moment can fail to connect or stay unheard. If a step fails because the service is busy, try again with the same key. If you still cannot hear someone, leave and start a new Room. - At most four people in one Room.
- Voice only. Video, screen sharing, recording, moderation, guest links and dial-in are not part of this preview.
View original Markdown
# Multi-party voice Room
Three or four people can talk in one voice Room, each from their own browser. A Room holds
up to four people. LiveKit carries the
audio, and OneSay decides who may join, speak and listen: the server checks every
step, whatever the client sends. The [Console](../developer/CONSOLE.md) has a ready-made Voice room
panel that walks you through these steps. This page explains them, so that you can
build the same flow in your own application.
This is a preview with a fixed group. Read the current limits at the end of this
page before you invite people.
## What you need
- A Project Environment on Staging, and an Application in it.
- For each person: their own OneSay sign-in through your application, which gives a
short-lived Human token (never put an API key in a browser), and a microphone
Source.
- For each person, these Grants in that Project Environment: `room.read`,
`participant.read`, `room.join`, `source.use`, `signal.audio.publish`,
`rtc.subscription.configure`, `signal.audio.subscribe`, `signal.audio.end`,
`app.session.create` and `app.session.close`. The person who creates the Room
also needs `room.create` and `room.close`.
## Steps
Each person walks these steps in order. Join one at a time: wait until the previous person appears in the participant list (`listRoomParticipants`; in the Console, under Who is here) before you join; people who join or start listening at the same moment can fail to connect or stay unheard.
1. **Create or join the Room.** One person creates it with `POST /rooms`
(`createRoom`), with `max_participants` set to the group size (3 or 4), and
shares its `room_` id. The others read it with `GET /rooms/{room_id}`
(`getRoom`) to check that it is open and has not started.
2. **Start an app session** with `POST /app-sessions` (`createAppSession`) for your
Application.
3. **Connect** with the SDK's `client.rtc.connect` (`createRTCConnection`). Nobody
is heard yet.
4. **Publish your microphone** with `session.audio.publishMicrophone`
(`createRTCPublication`) and your microphone Source.
5. **Let the next person join.** The next person starts step 3 only when you appear
in the participant list (step 6).
6. **Read the roster.** `GET /rooms/{room_id}/participants`
(`listRoomParticipants`) lists who is in the Room, and
`GET /rooms/{room_id}/signals` (`listRoomSignals`) lists their audio. A person is
speaking when one of their signals has the profile `audio.rtc.v1` and the status
`active`. Name only `active` signals: a `pending` one is not published yet.
7. **Listen, once.** When everyone is speaking, each person sets one subscription
with `session.audio.setSubscriptions` (`updateRTCSubscriptions`): the `pub_`
ids from the `publication_id` field of the others' `active` signals, sorted,
with `expected_policy_version` 0. Start listening one person at a time, a few
seconds apart, and do not change the set afterwards.
8. **Turn on sound** with `session.audio.enablePlayback`, called from the person's
own click, as browsers require.
9. **Leave.** End the microphone publication (`endRTCPublication`), then close the
session, which revokes the connection (`revokeRTCConnection`), and close the app
session (`closeAppSession`). The creator may also close the Room (`closeRoom`).
## JavaScript
This sketch uses only the public JavaScript SDK. `humanToken`, the ids and the
idempotency keys come from your application; keep each key, so that a retry of the
same step sends the same key.
```js
import { OneSayClient } from "@onesay/sdk";
// A short-lived Human token from your own sign-in; never an API key.
const client = new OneSayClient({ environment: "staging", tokenProvider: async () => humanToken });
// Step 3: connect with your app session.
const session = await client.rtc.connect({ roomId, appSessionId, idempotencyKey: connectKey });
// Step 4: publish your microphone Source.
const microphone = await session.audio.publishMicrophone({ sourceId, idempotencyKey: publishKey });
// Step 7: when everyone speaks, listen once to the others' active publications.
await session.audio.setSubscriptions({ publicationIds: otherPublicationIds, expectedPolicyVersion: 0, idempotencyKey: listenKey });
// Step 8: sound starts from the person's own click.
soundButton.onclick = () => session.audio.enablePlayback();
// Step 9: leave.
await microphone.end({ idempotencyKey: endKey });
await session.close({ idempotencyKey: closeKey });
```
The Room and the app session are created through the Control API
(`createRoom`, `createAppSession`), as in step 1 and step 2.
## Consent
On Staging, every step that creates, changes or ends something asks the person for
confirmation first: creating the Room, starting the app session, connecting,
publishing, listening and each part of leaving. Your application asks OneSay for a
confirmation of that exact step (`createConsentRequest`), shows it, records the
person's own Allow or Deny (`decideConsentRequest`) and then runs the step with the
same input. Nothing is allowed by default, and reads such as the roster need no
confirmation. The Console's Voice room panel shows this pattern.
## Current limits (preview)
- **A fixed group.** The creator picks three or four people. Everyone joins and
speaks before anyone listens, and each person sets their subscription once.
- **Someone leaving no longer ends the others' connections, but the group cannot change.**
Nobody can be added or removed in this preview: do not change your subscription set after someone leaves. To talk again as a different group, leave and start a new Room.
- **One at a time.** Join one at a time: wait until the previous person appears in the participant list (`listRoomParticipants`; in the Console, under Who is here) before you join; people who join or start listening at the same moment can fail to connect or stay unheard.
If a step fails because the service is busy, try again with the same key. If
you still cannot hear someone, leave and start a new Room.
- **At most four people** in one Room.
- **Voice only.** Video, screen sharing, recording, moderation, guest links and
dial-in are not part of this preview.