PostMessage API
The full event and command contract for the PersoScan iframe: message envelope, zone-selected and zone-confirmed payloads, and the commands you can send.
Use this page as the source-of-truth contract for parent-window integration.
Message Envelope
All outgoing widget events use this envelope:
| Field | Type | Value |
|---|---|---|
version | string | "v1" |
source | string | "persoscan-widget" |
timestamp | number | Unix epoch in milliseconds |
type | string | Event discriminator |
data | object | Event payload |
Outgoing Events, Widget to Parent
| Event | Runtime status | Trigger |
|---|---|---|
ready | Emitted | Widget is loaded and has no initialization error |
zone-selected | Emitted | User clicks a zone |
zone-confirmed | Emitted conditionally | User confirms selected zone in modal |
zone-cleared | Emitted conditionally | Existing selection is cleared |
model-changed | Emitted conditionally | User changes male/female model in widget UI |
pain-submitted | Emitted conditionally | User submits pain form |
error | Schema-defined, not currently emitted by runtime sender paths | Reserved for widget error reporting |
ready
data payload: {}
zone-selected
Required data keys:
| Key | Type |
|---|---|
zoneId | string |
zoneName | string |
sex | `“male" |
hitPoint | { x: number; y: number; z: number } |
Optional data keys:
| Key | Type | Condition |
|---|---|---|
region | string | Included when license features enable detailed zone metadata extraction |
side | `“left" | "right”` |
aspect | `“anterior" | "posterior" |
zone-confirmed
Payload shape is identical to zone-selected.
Emitted only when:
confirmModalOnClick=truein embed configuration, and- user presses confirm in the modal.
zone-selected still fires on click even when the confirmation modal is enabled.
zone-cleared
data payload: {}
Emitted when a prior selection exists and is then cleared.
model-changed
data payload:
| Key | Type |
|---|---|
sex | `“male" |
Emitted when user changes model from widget controls. It is not emitted for passive initial config hydration.
pain-submitted
Required data keys:
| Key | Type |
|---|---|
zoneId | string |
zoneName | string |
sex | `“male" |
painIntensity | number (0..10, integer) |
painType | `“sharp" |
Optional data keys:
| Key | Type | Condition |
|---|---|---|
painNotes | string | Present when notes are submitted |
region | string | Same metadata gate as zone-selected |
side | `“left" | "right”` |
aspect | `“anterior" | "posterior" |
error
Schema payload shape:
| Key | Type |
|---|---|
code | string |
message | string |
Runtime note: sender helper exists in code, but current runtime paths do not call it.
Incoming Commands, Parent to Widget
update-config
Use this command to patch runtime config after iframe loads.
Supported data keys:
| Key | Type |
|---|---|
devMode | boolean |
theme | `“light" |
accentColor | #RRGGBB string |
highlightColor | #RRGGBB string |
bodyColor | #RRGGBB string |
elementsLightColor | #RRGGBB string |
elementsDarkColor | #RRGGBB string |
bgColor | #RRGGBB string |
lang | string |
sex | `“male" |
mode | `“point" |
initialZone | string |
showForm | boolean |
hideSexSelect | boolean |
sexSelectPosition | one of top left, top center, top right, center left, center right, bottom left, bottom center, bottom right |
hideZoneSelection | boolean |
hidePainInput | boolean |
hidePainSlider | boolean |
hidePainType | boolean |
hidePainNotes | boolean |
hideSubmitButton | boolean |
hideExport | boolean |
transparentBackground | boolean |
select-zone
data payload:
| Key | Type |
|---|---|
zoneId | string |
clear-selection
data payload: {}
Copy-Ready Integration Snippets
Event listener
const WIDGET_ORIGIN = new URL('https://widget.persoscan.com').origin;
window.addEventListener('message', (event) => {
if (event.origin !== WIDGET_ORIGIN) return;
const msg = event.data;
if (!msg || msg.source !== 'persoscan-widget' || msg.version !== 'v1') return;
switch (msg.type) {
case 'ready':
// Widget initialized
break;
case 'zone-selected':
// Immediate click event
break;
case 'zone-confirmed':
// Emitted only when confirm modal is enabled and user confirms
break;
case 'zone-cleared':
// Selection was cleared
break;
case 'model-changed':
// User switched model
break;
case 'pain-submitted':
// User submitted pain form
break;
case 'error':
// Reserved in schema; see runtime notes below
break;
}
});Command sender
const iframe = document.getElementById('persoscan-widget');
const WIDGET_ORIGIN = new URL('https://widget.persoscan.com').origin;
function sendCommand(type, data) {
iframe.contentWindow.postMessage({ type, data }, WIDGET_ORIGIN);
}
sendCommand('update-config', { theme: 'dark', lang: 'en', showForm: true });
sendCommand('select-zone', { zoneId: 'lower-back' });
sendCommand('clear-selection', {});Common Mistakes
Treating schema-defined events as always emitted
Problem: You wait for an event that never arrives.
Cause: You do not account for runtime gates and currently inactive sender paths.
Solution:
- Use the runtime status table above.
- Keep event handling defensive.
- Implement fallbacks when optional metadata fields are absent.
Sending commands with unsupported keys
Problem: Some config updates appear ignored.
Cause: update-config validates keys against a strict schema.
Solution:
- Send only documented keys from this page.
- Configure other parameters via iframe URL query string when needed.
Always validate event.origin and event.data.source in production listener code.