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:

FieldTypeValue
versionstring"v1"
sourcestring"persoscan-widget"
timestampnumberUnix epoch in milliseconds
typestringEvent discriminator
dataobjectEvent payload

Outgoing Events, Widget to Parent

EventRuntime statusTrigger
readyEmittedWidget is loaded and has no initialization error
zone-selectedEmittedUser clicks a zone
zone-confirmedEmitted conditionallyUser confirms selected zone in modal
zone-clearedEmitted conditionallyExisting selection is cleared
model-changedEmitted conditionallyUser changes male/female model in widget UI
pain-submittedEmitted conditionallyUser submits pain form
errorSchema-defined, not currently emitted by runtime sender pathsReserved for widget error reporting

ready

data payload: {}

zone-selected

Required data keys:

KeyType
zoneIdstring
zoneNamestring
sex`“male"
hitPoint{ x: number; y: number; z: number }

Optional data keys:

KeyTypeCondition
regionstringIncluded 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:

  1. confirmModalOnClick=true in embed configuration, and
  2. user presses confirm in the modal.

zone-cleared

data payload: {}

Emitted when a prior selection exists and is then cleared.

model-changed

data payload:

KeyType
sex`“male"

Emitted when user changes model from widget controls. It is not emitted for passive initial config hydration.

pain-submitted

Required data keys:

KeyType
zoneIdstring
zoneNamestring
sex`“male"
painIntensitynumber (0..10, integer)
painType`“sharp"

Optional data keys:

KeyTypeCondition
painNotesstringPresent when notes are submitted
regionstringSame metadata gate as zone-selected
side`“left""right”`
aspect`“anterior""posterior"

error

Schema payload shape:

KeyType
codestring
messagestring

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:

KeyType
devModeboolean
theme`“light"
accentColor#RRGGBB string
highlightColor#RRGGBB string
bodyColor#RRGGBB string
elementsLightColor#RRGGBB string
elementsDarkColor#RRGGBB string
bgColor#RRGGBB string
langstring
sex`“male"
mode`“point"
initialZonestring
showFormboolean
hideSexSelectboolean
sexSelectPositionone of top left, top center, top right, center left, center right, bottom left, bottom center, bottom right
hideZoneSelectionboolean
hidePainInputboolean
hidePainSliderboolean
hidePainTypeboolean
hidePainNotesboolean
hideSubmitButtonboolean
hideExportboolean
transparentBackgroundboolean

select-zone

data payload:

KeyType
zoneIdstring

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:

  1. Use the runtime status table above.
  2. Keep event handling defensive.
  3. 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:

  1. Send only documented keys from this page.
  2. Configure other parameters via iframe URL query string when needed.

Next Steps

Last updated: March 12, 2026