Docs

Embeddable Events Build and above

Embeddables emit browser events via window.postMessage. You can listen for these messages to track learner progress, react to authoring actions, and handle lifecycle events such as ready or error states.

How it works

Coassemble sends messages from inside the embedded iframe to your host application. Each embeddable page in this docs site includes an Emitted Events table listing the events that can be sent for that embeddable.

In your host app, add one message listener on window, then filter events by iframe source and origin before handling the payload.

Listener example

const iframe = document.getElementById('your-iframe-id');
const expectedOrigin = new URL(iframe.src).origin;

function onEmbedMessage(event) {
    if (event.origin !== expectedOrigin) return;
    if (event.source !== iframe?.contentWindow) return;
    if (!event.data || typeof event.data !== 'object') return;

    const payload = event.data;

    if (payload.type === 'session') {
        if (payload.event === 'ready') {
            console.log('Embeddable ready');
        }
    } else if (payload.type === 'course') {
        if (payload.event === 'progress') {
            console.log('Progress update', payload);
        }
    } else if (payload.type === 'back') {
        console.log('Back clicked');
    }
}

window.addEventListener('message', onEmbedMessage);

Payload shape

FieldTypeDescription
typestringEvent group, such as session, course, screen, or back.
eventstring (optional)Event name within the group, such as ready, progress, or complete.
dataobjectSome events include extra metadata. Handle payloads defensively and check fields before use.

Quiz score

A scored quiz pass complete includes data.score.percentage and data.score.passed. A scored quiz failed (and exhausted) score also includes absolute attempt counts from the quiz run.

FieldTypeDescription
percentagenumberScore as a percent.
passedbooleanWhether the attempt met the passing grade.
attemptsUsednumberOn failed and exhausted. How many attempts have been used, including this one.
attemptsAllowednumberOn failed and exhausted. Total attempts the learner may take, which is authored retries plus one. -1 means unlimited.
retriesAvailablenumberOn failed and exhausted. Derived remaining retries. Existing integrations can keep using this field. Unlimited classic quizzes may still send Infinity.
raw / maxnumberClassic quizzes only: correct count and question count when those values are present.

When the learner finishes, fails, and cannot retry, the player also emits type: 'screen' / event: 'exhausted' with the same score object. end still means the screen was navigated away from; exhausted is the terminal fail-with-no-retries-left event and fires in addition to failed. Unlimited quizzes (attemptsAllowed: -1) never emit exhausted.

Best practices

  • Verify event.origin is as expected before handling data.
  • Verify event.source is your iframe to ignore unrelated messages.
  • Use the type and event fields to route logic in your app.
  • Remove listeners on unmount / cleanup to avoid duplicate handlers.