Fermion allows you to embed live calls externally using a simple integration. This is useful if you wish to add realtime calls on your own website and do not wish to use Fermion managed LMS. There are a few steps you need to do in order to embed a session externally.Documentation Index
Fetch the complete documentation index at: https://docs.fermion.app/llms.txt
Use this file to discover all available pages before exploring further.
Step 1 - Creating a new session
The first step to embedding a session externally is to create it programmatically on Fermion.- You must call this API endpoint: https://docs.fermion.app/api-reference/live/create-live-session
- Once you call this endpoint, you will get
liveEventSessionIdin response. - You must store this
liveEventSessionIdin your database somewhere. liveEventSessionIdallows you to control the session completely using APIs, as we will see now below.
Step 2 - Embedding the session on your website
Once you have obtainedliveEventSessionId, embedding the session on your website involves two steps.
Generating a signed JWT
A signed JWT is required for any user with any permission (host or student) to be able to participate. This token must be generated by your backend. Here is an example of how to generate this in Node.js:JWT Payload Fields
Required fields:liveEventSessionId- The same ID that you got when you created session over API.userId- A unique ID for a user that you’re creating the playback session for. Note that it should be a stable ID. It can be anything like a database identifier or even an email address of the user. However two sessions must not be sharing same stable ID, otherwise they will be considered as a single user.
name- Optional default name to use for the user. If provided, this value will override the existing name.profilePicUrl- Optional default profile photo URL to use for the user. If provided, this value will override the existing profile photo.
playbackOptions object:
The playbackOptions object is optional and has the following default values if not provided:
shouldDisallowRecordedPlaybackIfNotLive(default:false) - If you pass this astrue, once the livestream ends, the same JWT that is used to embed the stream would not be able to playback the recorded version of the video.shouldPreferOnlyHighDefinitionPlayback(default:false) - If you pass this astrue, system would enforce only 1080p and 720p modes of playback. Note: Passing this astruemight result in buffering on user side if their internet is not fast (as this disables low quality playback versions).shouldHideSeekControls(default:false) - If you pass this astrue, it will hide the seek controls (seekbar and jump forward/backward buttons) from the UI.customBrandingData(optional) - If you pass this branding information, it would be used to show logo / brand name instead of your primary school brand name. This object contains:name(required) - The custom brand name to display.logoUrl(optional) - URL to the custom logo image.
Embedding the live call session (Fermion SDK)
In your frontend JavaScript app, install the fermion SDK. Run one of the following depending on your package manager:Embedding the live call session (Manually)
Once you have created JWT from backend, you can also embed the iframe session manually. Here is what the iframe code would be:your-website.com: Domain you have with fermion:your-session-id:: Live event session ID you got from API:your-jwt-token:: Signing token you created in last step
Step 3 - Assigning a primary host
When you open the webpage in browser, you’ll only see a black screen and no video playing or livestream controls. This is expected because every session, by default, is embedded as a student-view (view-only). You need to add a host to the call who will be responsible for starting the session, switching students within stage/audience, conducting polls, whiteboards, etc. You must do that using this endpoint: https://docs.fermion.app/api-reference/live/modify-live-session-user-state with the type ofupdate-permissions
The API above expects you to pass:
- User ID (same ID you created at the time of generating the JWT)
- Updated permission level
window.postMessage events
When you embed the live session on your domain, the iframe will emit events in real-time through thewindow.postMessage API. You can read more about window.postMessage API here.
These events are emitted from both live WebRTC sessions and recorded video playback. This allows you to track user engagement, respond to session lifecycle changes, and build interactive experiences around your embedded live sessions.
Note that you must not fully rely on events emitted by window.postMessage because of security reasons. Please do a server-side validation with our real API endpoint to be sure of the final result. You can use messages from window.postMessage to optimistically display results, however, since it is a client-side API, it is possible to hijack the event data by the untrusted user.
You can listen to every postMessage like this:
event.data with zod if you can. Our schemas are strictly typed and are zod-parseable.
Here are the events we emit through window.postMessage API:
webrtc:livestream-ended
This event fires when a live WebRTC session ends (typically when the host ends the session for everyone). Here’s a TypeScript type for this event:webrtc:left-stage
This event fires when someone leaves the stage during a live WebRTC session. Here’s a TypeScript type for this event:video:play
This event fires when a user plays the recorded video playback. Note that this event only fires during recorded playback, not during live sessions. Here’s a TypeScript type for this event:durationAtInSeconds field indicates the playback position when the user pressed play. Use this event to track playback engagement and viewing patterns.
video:pause
This event fires when a user pauses the recorded video playback. Note that this event only fires during recorded playback, not during live sessions. Here’s a TypeScript type for this event:durationAtInSeconds field indicates the playback position when the user pressed pause. Use this event to track viewing patterns and engagement metrics.
video:ended
This event fires when the recorded video playback reaches the end. Note that this event only fires during recorded playback, not during live sessions. Here’s a TypeScript type for this event:video:livestream-ended
This event fires when a live stream transitions to VOD (video-on-demand / recorded) mode. Here’s a TypeScript type for this event:webrtc:livestream-ended as it relates to the HLS stream transitioning from live to recorded mode.
video:time-updated
This event fires periodically during recorded video playback (every few seconds). Note that this event only fires during recorded playback, not during live sessions. Here’s a TypeScript type for this event:currentTimeInSeconds field indicates the current playback position. Use this event to track detailed viewing progress, create bookmarks, or show custom overlays. Note that this event fires frequently, so consider throttling or debouncing on the receiving end if you’re performing expensive operations.
Complete TypeScript Type
Here’s a complete TypeScript discriminated union type for all live event postMessage events:Important Notes
- Video events (
video:*) only fire during recorded video playback. If a user is watching the live session in real-time, these events will not be emitted. - WebRTC events (
webrtc:*) only fire during live sessions when users are connected via WebRTC. - The
playbackOptionsobject in the JWT token (mentioned in Step 2) controls playback behavior, which affects which events users might see. - Some sessions may have both live and recorded phases. During the live phase, you’ll receive WebRTC events. After the session ends and transitions to recorded playback, you’ll receive video events.
Using the Fermion SDK for event handling
If you’re using the Fermion SDK (installed via@fermion-app/sdk), you don’t need to manually set up window.addEventListener for postMessage events. The SDK provides a convenient setupEventListenersOnVideo() method that handles all the complexity for you.
Benefits of using the SDK
- Type-safe callbacks: TypeScript definitions ensure you’re handling events correctly
- Automatic validation: The SDK validates incoming events automatically
- Cleaner API: More intuitive methods instead of manual postMessage parsing
- Built-in cleanup: Easy disposal of event listeners to prevent memory leaks
