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
| Field | Type | Description |
|---|---|---|
type | string | Event group, such as session, course, screen, or back. |
event | string (optional) | Event name within the group, such as ready, progress, or complete. |
data | object | Some 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.
| Field | Type | Description |
|---|---|---|
percentage | number | Score as a percent. |
passed | boolean | Whether the attempt met the passing grade. |
attemptsUsed | number | On failed and exhausted. How many attempts have been used, including this one. |
attemptsAllowed | number | On failed and exhausted. Total attempts the learner may take, which is authored retries plus one. -1 means unlimited. |
retriesAvailable | number | On failed and exhausted. Derived remaining retries. Existing integrations can keep using this field. Unlimited classic quizzes may still send Infinity. |
raw / max | number | Classic 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.originis as expected before handling data. - Verify
event.sourceis your iframe to ignore unrelated messages. - Use the
typeandeventfields to route logic in your app. - Remove listeners on unmount / cleanup to avoid duplicate handlers.