The <conference-room> widget

A distributable web component that wraps a live confcall room in an iframe and re-emits its lifecycle as DOM CustomEvents — the same custom-element developer experience as @confcall/agent-widget, but iframe-isolated so it survives CRM sandboxes (Salesforce Lightning Locker, ServiceNow OpenFrame, Dynamics CIF).

Install

Script tag:

<script src="https://widget.confcall.app/conference-widget.umd.js"></script>

or as an npm package:

npm install @confcall/conference-widget
import '@confcall/conference-widget';

Quick start

<conference-room
  room-id="room-abc123"
  base-url="https://meet.confcall.app"
  display-name="Ada Lovelace"
></conference-room>

Without an auth-token, the embedded page shows a "Sign in for host access" affordance that opens the AuthService popup flow; a visitor can still join and participate as a guest without signing in.

In practice, set room-id and auth-token from the response of POST /api/embed/sessions — see the two-call smoke sequence on the overview page.

Attributes

AttributeRequiredDescription
room-idYesThe confcall room to join.
base-urlYes Origin of the confcall deployment — origin only (e.g. https://meet.confcall.app), no path, no trailing slash. The element appends /embed/room/{room-id} itself; a base-url that already includes a path produces a broken, doubled URL.
auth-tokenNo A JWT (popup-flow token, or a control-plane embed session token from POST /api/embed/sessions). When set, the embed page skips the popup sign-in and joins with the token's role immediately.
display-nameNoPre-fills the guest display name.
layoutNo mobile (default) or desktop. The embed always renders the mobile in-call layout regardless — this only affects the query param forwarded to the embed page.

Changing any attribute updates the iframe's src in place — no element remount.

Known pitfall: base-url must be a bare origin. A prior widget (agent-widget) once double-appended a URL suffix because a server-side snippet generator emitted a value that already contained the path it appended again. If you generate this attribute from server-side config, assert it's a bare origin before interpolating it.

Events

All events bubble and are composed, so they cross the iframe/element boundary and can be listened for anywhere in the host page's DOM.

EventdetailFires when
conference-joined{ roomId, participantCount }The local participant joins.
conference-left{ reason: 'leave' | 'kicked' | 'ended' | 'denied' }The local participant leaves, for any reason.
conference-participant-joined{ userId, name, isAiAgent }A remote participant (including the AI agent) joins.
conference-participant-left{ userId, name, isAiAgent }A remote participant leaves.
conference-meeting-ended{ roomId }The host ends the meeting for everyone.
conference-auth-required{ action }A guest attempts a host-only action (e.g. inviting the AI agent) without an auth-token.
conference-error{ code, message }A recoverable embed-page error.
const room = document.querySelector('conference-room');

room.addEventListener('conference-joined', (e) => {
  console.log('joined room', e.detail.roomId, 'participants:', e.detail.participantCount);
});

room.addEventListener('conference-meeting-ended', () => {
  room.remove();
});

Messages are only accepted from the element's own iframe at the base-url origin — anything else is silently ignored.

Sizing

The element has no intrinsic size; size its container the same way you would any iframe:

<div style="width: 400px; height: 700px;">
  <conference-room room-id="room-abc123" base-url="https://meet.confcall.app"></conference-room>
</div>

Scope exclusions (v1)

Full source and build instructions: conference-widget/README.md in the confcall repo. Design rationale: specs/RFC-Conference-Room-Embed.md (Phase 3).