Meet API Docs Dashboard
On this page

Embed postMessage Protocol

What the embed SDK sends and receives, for anyone who builds the iframe by hand.

The embed SDK wraps this protocol. Use the protocol directly if you cannot add a script, for example in a native web view that frames Meet from your own page.

Before you start#

  1. Your page's origin must be in the project's Embed origins. Meet answers every room page with Content-Security-Policy: frame-ancestors 'self' <your origins>, and the room speaks postMessage only to a parent whose origin is on the list, to that origin and never to *. It takes commands only from the window that framed it. https://*.acme.com matches every subdomain, http://localhost:3000 is for development, and * is refused for a project with live keys.
  2. The iframe needs allow="camera; microphone; display-capture; autoplay; fullscreen; clipboard-write; speaker-selection".
  3. Screen sharing started by a command needs a click inside the frame first, because browsers require a user gesture there.

Address options#

Option Meaning
?token= The meeting token
?key= An invite key
?theme=dark or light Colour theme
?lang= Interface language
?hide=chat,people,screen,pip,leave,header Remove those controls
?parent=<origin> A hint for browsers without location.ancestorOrigins (the SDK adds it)

Envelope#

Every message is { source: "meet", v: 1, kind: "command" | "response" | "event", type, data, id }.

js
const frame = document.querySelector('iframe');
const send = (type, data = {}) => frame.contentWindow.postMessage(
  { source: 'meet', v: 1, kind: 'command', type, data, id: crypto.randomUUID() }, 'https://meet.respiresoft.com');

window.addEventListener('message', (e) => {
  if (e.origin !== 'https://meet.respiresoft.com' || e.data?.source !== 'meet') return;
  console.log(e.data.kind, e.data.type, e.data.data ?? e.data.error);
});

send('join', { name: 'Ann', audio: true, video: true });

Commands#

Command (data) Does Errors
join {name?, key?, lang?, audio?, video?} Enter the room and the call. Answers {pending} when the host must admit you. already_joined, name_required, join_failed
leave Leave the call
setAudio {on}, setVideo {on} Microphone, camera not_in_call, bad_argument
startScreenShare, stopScreenShare Share the screen not_in_call, unsupported
setTheme {theme} dark or light bad_argument
setCaptions {on} Captions on or off. on also starts caption events. bad_argument
setLanguage {language} The language you listen in bad_argument
sendChat {text} A chat message, up to 2,000 characters not_in_room, bad_argument

An unknown command answers unknown_command. A command with no handler answers not_available.

Events#

Event (data) When
ready {room: {slug, name}, state: "door" or "blocked"} The room page is up and your origin is allowed. Sent once.
joined {participantId, name, role} You entered the call
left {reason} left, kicked, revoked or ended
participant-joined, participant-updated {id, name, audio, video, screen, hand} Someone joined, or a mic, camera, screen or hand changed (also you, for updates)
participant-left {id, name} Someone left the call
active-speaker {participantId} The loudest speaker changed. null when quiet.
chat-message {id, participantId, name, text, ts, own} A new message. History is not replayed.
caption {participantId, text, final, lang} Only after setCaptions with on: true
error {code, message} room_closed, room_locked, room_notFound and other refused rooms, or call_error
recording-started, recording-stopped Reserved. Not sent yet.