OneSay documentation

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.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.

JavaScript
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.

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.