JavaScript APIs

Overview

The JavaScript API lets your own page drive a running meeting room: mute a participant, read the participants list, send chat messages, change devices, listen for events. It works whether you embed the room as an iFrame or use the Veeting Blocks web components.

Using this page with an AI assistant

If you are using an AI coding assistant, copy the text below into it and add a sentence describing what you want to build. AI assistants accessing this page receive a separate, token-optimized version of our full developer documentation, specifically tailored by our engineers for AI coding assistants.

Before writing any code, read this page:
https://www.veeting.com/en/developer-documentation/javascript-apis

It documents the JavaScript API of a Veeting Rooms meeting room.
Keep these five rules in mind:

1. Ask me first whether my code runs INSIDE the room (Veeting Blocks,
   a custom tool) or in the page around an embedded iFrame. The two
   reach the API completely differently and the answer changes
   everything you write.
2. Wait for the "joined" event before calling methods that need the
   participant to be in the meeting.
3. "connected" and "disconnected" refer to the websocket to our API,
   not to WebRTC media. Do not treat them as media state.
4. Only use methods listed on that page. Do not invent them and do not
   guess a signature.
5. From a parent page you talk to the room with postMessage:
   {action, payload} in, {event, payload} out. It is one-way, so no
   method that returns a value is usable there. Check message.origin
   on everything you receive.

What I want to build:

Where your code runs determines how you reach the API

This choice matters more than anything else on this page, so settle it first.

Your code runsHow you reach the API
Inside the meeting room, which means Veeting Blocks or a custom tool pageDirectly, through the handler. You get the full API, including return values.
In the page around an embedded iFrameThrough postMessage. You can call any method that returns nothing, and you receive every event.

If you embed the room in an iFrame, your page and the meeting room are two different documents, usually on two different origins. Your page cannot reach into the room's window, and the room cannot reach into yours. Everything crosses that boundary as a message.

Cross-origin is expected and supported. The room normally runs on your own white-label domain, such as meeting.example.com, while your application sits on app.example.com or somewhere else entirely. Neither case needs anything special: a different subdomain and a completely different domain behave the same way here.

Driving the room from the page around it

Receiving events

The meeting room posts every event to its parent window. Listen for those messages:

window.addEventListener("message", (message) => {
  // Always check the origin. The room posts to any parent, so this check is
  // yours to make, and without it any page could forge these events.
  if (message.origin !== "https://<DOMAIN-NAME>") {
    return;
  }

  const { event, payload } = message.data;

  if (event === "joined") {
    console.log("The meeting room is ready");
  }

  if (event === "participantsUpdated") {
    console.log(`${payload.length} participants in the room`);
  }
});

The event is one of the twelve names below, and payload is exactly what that event carries. You do not have to subscribe: every event is posted to the parent whether you listen or not.

Calling methods

Post a message to the iFrame that names the method and its arguments:

const room = document.getElementById("meeting-frame");

room.contentWindow.postMessage({
  action: "muteAudio",
  payload: [true]
}, "https://<DOMAIN-NAME>");

action is the method name, and payload holds its arguments in order, as an array. For a method that takes no arguments, pass payload: [] or leave it out.

Because messaging across the boundary is one-way, two limits apply:

  • You get no return value. Anything beginning with get is therefore not useful across the boundary. Read the participants from the participantsUpdated event instead of calling getParticipantsList(), and take the room configuration from meetingRoomConfigUpdated.
  • You get no error either. If you misspell the method name, nothing happens and nothing is reported. Check the name against the tables below.

A complete example

A mute button and participant counter in your own page, outside the iFrame:

<button id="mute">Mute</button>
<span id="count">0</span> participants

<iframe id="meeting-frame"
  src="https://<DOMAIN-NAME>/meeting/<MEETING-ID>"
  allow="microphone;camera;encrypted-media;fullscreen;autoplay;display-capture;layout-animations;">
</iframe>

<script>
  const ROOM_ORIGIN = "https://<DOMAIN-NAME>";
  const room = document.getElementById("meeting-frame");
  let muted = false;

  window.addEventListener("message", (message) => {
    if (message.origin !== ROOM_ORIGIN) {
      return;
    }
    if (message.data.event === "participantsUpdated") {
      document.getElementById("count").textContent = message.data.payload.length;
    }
  });

  document.getElementById("mute").addEventListener("click", () => {
    muted = !muted;
    room.contentWindow.postMessage({ action: "muteAudio", payload: [muted] }, ROOM_ORIGIN);
  });
</script>

Getting the handler

The rest of this section applies to code running inside the meeting room: Veeting Blocks or a custom tool. If you are embedding an iFrame, use the messages above instead.

The API only exists once the meeting room has finished loading, so you cannot call it from a script that runs at page load. Instead, define a wlvmrApiReady function on window. The meeting room calls that function when it is ready and passes the name of the object it registered itself under.

window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }

  // Every API method lives on this object
  window[handler].muteAudio(true);
};

Two rules follow, and both trip people up:

  1. Define wlvmrApiReady before the meeting room loads. If you assign it afterward, the call has already happened and you never receive the handler.
  2. Do not hard-code the handler name. The meeting room passes it to you as an argument. Always use window[handler], never a literal.

A worked example

Waiting for the room, subscribing to events, then calling methods:

window["wlvmrApiReady"] = (handler) => {
  if (!window[handler]) {
    console.error(`Handler window.${handler} not found!`);
    return;
  }

  const api = window[handler];

  api.on("joined", () => {
    console.log("The meeting room is ready to use");
  });

  api.on("participantsUpdated", (participants) => {
    console.log(`${participants.length} participants in the room`);
  });

  api.on("chatMessage", (message) => {
    console.log(`${message.fromParticipantName}: ${message.message}`);
  });

  api.setAudioInputDeviceId("default", false);
  api.setVideoInputDeviceId("default", true);
};

Events

Subscribe with on(event, callback). There are twelve events.

EventFires whenPayload
beforeConnectingJust before the room connects its medianone
connectedThe websocket to the Veeting API is established and in usenone
joinedThe meeting room is ready to usenone
disconnectedThe websocket to the Veeting API is disconnected or unusednone
leaveThe participant leaves the meetingnone
participantsUpdatedSomebody joins, leaves, or changes stateIApiParticipant[]
chatMessageA group chat message arrivesIApiChatMessage
privateChatMessageA private chat message arrivesIApiChatMessage
meetingDurationUpdatedThe remaining meeting time changesIApiRemainingTimeUpdate
meetingRoomConfigUpdatedThe room configuration changesIMeetingRoomConfig
customMessageAnother participant sends a custom messageICustomMessage
screenshareStateChangeScreen sharing starts or stopsIScreenshareState

Note: connected and disconnected describe the websocket to our API, not the WebRTC media. A participant can be disconnected from media and still use the whiteboard.

Methods

Meeting and participants

MethodDescription
getVersion(): stringThe version string of the running meeting room.
getParticipantsList(): IApiParticipant[]The current participants list.
leaveMeeting(): voidDisconnects from the server and leaves the meeting room.
enableFollowMe(enabled: boolean): voidEnables Follow Me. Requires moderator rights.

Audio and video

MethodDescription
muteAudio(muted: boolean): voidMutes or unmutes audio.
toggleMuteAudio(): voidToggles audio mute.
muteVideo(muted: boolean): voidMutes or unmutes video.
toggleMuteVideo(): voidToggles video mute.
setVolume(volume: number, meetingParticipantId?: string): voidSets the playback volume for one participant or for everyone.
connect(mediaConfig?: { audio: boolean, video: boolean }): voidConnects media. Ignored if already connected. Participants connect automatically when they join.
disconnect(): voidDisconnects media. Ignored if already disconnected.
restartMediaConnections(): voidRestarts the media connections. Useful after a device change.
enterVideoFullscreen(): voidEnters fullscreen. Fails in most browsers other than Chrome because fullscreen requires user interaction.

Devices

MethodDescription
getMediaDeviceSettings(): IMediaDeviceSettingsThe currently selected devices and resolution.
getMediaDeviceAudioInputList(audioOnly: boolean): MediaDeviceInfo[]The available microphones. audioOnly limits the permission prompt to audio.
getMediaDeviceAudioOutputList(audioOnly: boolean): MediaDeviceInfo[]The available speakers. audioOnly limits the permission prompt to audio.
getMediaDeviceVideoInputList(): MediaDeviceInfo[]The available cameras.
setAudioInputDeviceId(deviceId: string, reconnect: boolean): voidSets the microphone. The API does not check that the device ID is valid.
setAudioOutputDeviceId(deviceId: string, reconnect: boolean): voidSets the speaker. Only Chrome and Edge support this today.
setVideoInputDeviceId(deviceId: string, reconnect: boolean): voidSets the camera. The API does not check that the device ID is valid.
setVideoResolution(resolution: MeetingRoomVideoResolution, reconnect: boolean): voidSets the outgoing video resolution.
setMediaStreamConstraints(constraints: MediaStreamConstraints, merge?: boolean): voidConstraints for getUserMedia. merge defaults to true, which combines your constraints with ours.
setDisplayMediaConstraints(constraints: MediaStreamConstraints, merge?: boolean): voidConstraints for getDisplayMedia. merge defaults to true.

reconnect defaults to false. Pass true to apply the change immediately by reconnecting media.

The three list methods can prompt the participant. They ask the browser for media permission to read device labels, so calling one can open a permission dialog. Call them when the participant expects it, not on page load.

Screen sharing and recording

MethodDescription
setScreensharingInterceptor(callback: () => void): voidLets an Electron application intercept screen-sharing requests and pre-select a source.
startScreensharing(sourceId?: string): voidStarts screen sharing, optionally from a given source. Does nothing if the participant is not allowed to share.
stopScreensharing(): voidStops screen sharing.
forceStopScreensharing(): voidStops another participant's screen sharing. Moderators only; the server blocks it otherwise.
startRecording(): voidStarts recording. Only works if the meeting is configured for partial recording.
stopRecording(): voidStops recording. Subject to the same condition.

Messaging

MethodDescription
sendChatMessage(message: string): voidSends a group chat message.
sendPrivateChatMessage(message: string, participantId: string): voidSends a private chat message to one participant.
sendCustomMessage(message: ICustomMessage): voidSends a custom message to all participants, delivered as a customMessage event.

Layout and events

MethodDescription
setVideoDisplayCalculator(calculator: IVideoDisplayCalculator): voidTakes over how videos are arranged. See video display calculator.
on(event: MeetingRoomApiEvent, callback: ApiEventCallback): voidSubscribes to an event.

Additional methods in Veeting Blocks

If you are using Veeting Blocks rather than an embedded room, the API offers four more methods for driving the join flow yourself:

MethodDescription
isBrowserSupported(): booleanWhether the current browser can run a meeting.
loadMeeting(meetingId: string): Promise<IMeetingRoomConfig>Loads a meeting's configuration.
joinMeeting(config: IMeetingConnectionConfig): Promise<void>Joins the meeting.
can(meetingPermission: string): Promise<boolean>Whether the current participant has the given permission.

Type definitions

interface IApiParticipant {
  id: string;
  name: string;
  muted?: boolean;
  handRaised?: boolean;
  fromPSTN?: boolean;
  hasVideo?: boolean;
  hadVideo?: boolean;
  joinedAt: number;
}

interface IApiChatMessage {
  fromParticipantId: string;
  fromParticipantName: string;
  message: string;
}

interface IApiRemainingTimeUpdate {
  remainingSeconds: number;
}

interface IScreenshareState {
  active: boolean;
}

interface IMediaDeviceSettings {
  videoResolution: string;
  audioInputDeviceId: string;
  audioOutputDeviceId: string;
  videoInputDeviceId: string;
}

interface IMeetingConnectionConfig {
  meetingId: string;
  participantName: string;
  participantEmail?: string;
  audio: boolean;
  video: boolean;
  moderatorToken?: string;
  interpreterToken?: string;
  invisibleToken?: string;
  speakerToken?: string;
}

type MediaDirection = "receiveonly" | "sendonly" | "sendreceive";
type JoinMode = "audio-only" | "audio-video" | "video-only" | "no-media";

type ApiEventCallback = (payload: void | IApiParticipant[] | IApiChatMessage
  | IApiRemainingTimeUpdate | IMeetingRoomConfig | ICustomMessage | IScreenshareState) => void;

Common mistakes

  • Assigning wlvmrApiReady after the meeting room has loaded, so it is never called.
  • Hard-coding the handler name instead of using the one passed to the callback.
  • Calling a method before the joined event, when the room is loaded but not yet in the meeting.
  • Expecting connected to mean media is flowing. It refers to the websocket to our API.
  • Expecting startScreensharing to report a problem. If the participant is not allowed to share, it returns silently.
  • Calling startRecording on a meeting that is not configured for partial recording.
  • Calling forceStopScreensharing as a non-moderator. The server blocks it.
  • Passing a device ID without checking that it exists. The API does not validate it.

Not sure how to best implement your project?

Contact our team to discuss the details.