Loader API reference
The widget.js loader is the small, React-free script that injects the Expedition Insure quote iframe into your page. This page is the precise contract for its global, window.ExpeditionInsure: every method signature, return value, throw condition, and config param. It’s for the engineer wiring the widget in and anyone reviewing what the loader does to the page.
For the event payloads you receive through on(), see /reference/events/; for the HTTP endpoints the iframe calls, see /reference/embed-api/; for a narrative walkthrough start at /embedded-insurance/integration-guide/.
1. The window.ExpeditionInsure global
Section titled “1. The window.ExpeditionInsure global”After widget.js loads, the global has exactly three members — no more:
window.ExpeditionInsure = { mount, // (target, config) => HTMLIFrameElement on, // (eventName, handler) => () => void (unsubscribe) embedOrigin, // string — the embed origin this build trusts};mount(target, config)— inject a sandboxed iframe and return it. See §2.on(eventName, handler)— subscribe to a lifecycle event; returns an unsubscribe function. See §3.embedOrigin— a read-only string: the origin this build ofwidget.jstrusts and frames. It is resolved at runtime from the origin that servedwidget.js, so<script src="https://expedition.insure/widget.js">makesembedOriginequalhttps://expedition.insure. It exists for debugging — there is no setter. The loader uses it to lock the postMessage channel (it only ever trusts inbound messages whoseevent.originequalsembedOrigin, never"*").
There is no unmount() and no update() — see §5 for the remount workaround.
2. mount(target, config)
Section titled “2. mount(target, config)”Inject the quote iframe into a target element.
mount(target: string | HTMLElement, config: MountConfig): HTMLIFrameElementtarget— either a CSS selector string (resolved withdocument.querySelector) or a directHTMLElement.config— see the params table in §4.pkis required; everything else is optional and pre-fills the quote form.- Returns the created
HTMLIFrameElement, already appended into the target element. Keep the reference if you want to style its wrapper or read it back later.
<div id="quote"></div>
<script src="https://expedition.insure/widget.js"></script><script> const iframe = ExpeditionInsure.mount("#quote", { pk: "pk_op_...", // your publishable key destination: "antarctica", startDate: "2026-12-01", endDate: "2026-12-14", ages: [45, 47], residence: "US", tripCost: 18000, // whole dollars, total trip cost travelers: 2, });</script>Throw conditions
Section titled “Throw conditions”mount() throws synchronously in exactly two cases:
| Condition | Error message |
|---|---|
| Target not found — the selector matches nothing, or the passed element is null. | [ExpeditionInsure] mount target not found: <target> |
Missing or invalid pk — config is absent, or config.pk is not a non-empty string. | [ExpeditionInsure] mount requires a `pk` (publishable key). |
What mount() does, in order
Section titled “What mount() does, in order”- Resolve the target element; throw if missing.
- Validate
pk; throw if missing or invalid. - Install the shared message bridge (idempotent — one
windowmessage listener is registered the first time and reused by all instances). - Create an
<iframe>and setiframe.srcto${embedOrigin}/embed/quote?<serialized config params>&host=<your-origin>(see §4 for serialization). - Set the
sandboxattribute (§5),title="ExpeditionInsure quote", andloading="lazy". - Apply first-paint inline styles:
width:100%,border:0,display:block,height:520px. The height is replaced as soon as the iframe reports its real content height through anei:resizerelay, so you never set a height and never see an inner scrollbar. - Append the iframe into the target and track the instance.
- Return the iframe.
Multiple mount() calls are supported — each is tracked independently, and inbound messages are matched back to the right iframe by its contentWindow. The host param the loader appends is your page origin; it is advisory and spoofable, used by the iframe as its postMessage targetOrigin. The real trust boundary is the server-side pk_op_ Origin allowlist, not this param.
3. on(eventName, handler)
Section titled “3. on(eventName, handler)”Subscribe to a lifecycle event emitted by the widget.
on(eventName: PublicEventName, handler: (payload: unknown) => void): () => void-
Returns an unsubscribe function. Call it to remove your handler; calling it more than once is a safe no-op.
-
Handlers are isolated. Each handler runs inside a
try/catch; a throw in your handler is caught and logged ([ExpeditionInsure] listener error:), never breaking the bridge or stopping other listeners. -
Multiple handlers per event are allowed and run in subscribe order.
-
Unknown event names throw synchronously:
[ExpeditionInsure] unknown event "<name>".Valid: quote.ready, quote.selected, quote.error, payment.succeeded, payment.failed
The five valid event names are quote.ready, quote.selected, quote.error, payment.succeeded, and payment.failed. The widget bridges a namespaced ei:* postMessage channel, origin-locked in both directions; the ei: prefix is stripped before your handler sees the payload.
const off = ExpeditionInsure.on("quote.selected", (e) => { // premiumCents is integer minor units (cents); divide by 100 for display. console.log(e.planId, e.premiumCents, e.currency);});
// later, to stop listening:off();Read
premiumCents(integer minor units). Onquote.selectedandpayment.succeeded,premiumCentsis the premium field — an integer in minor units (cents); divide by 100 yourself for display.
payment.succeededis a UX signal, not the source of truth. It reports only the insurance charge confirmed inside our iframe, and it can be missed (the user closes the tab, the bridge drops). The authoritative post-sale signal is thepolicy.issuedwebhook, fired server-side — that is also what writes the commission. Usepayment.succeededto sequence your own checkout UX; reconcile sales and attribution frompolicy.issued. See /reference/events/.
Full payload shapes for each event are documented at /reference/events/.
4. MountConfig params
Section titled “4. MountConfig params”Every param except pk is optional. Empty or undefined values are dropped from the iframe URL; arrays are serialized to CSV ([45, 47] becomes "45,47").
| Param | Type | Example | Notes |
|---|---|---|---|
pk | string (required) | "pk_op_..." | Your publishable key. Public by design (like a Stripe pk_); the trust boundary is the server-side Origin allowlist, not key secrecy. Missing or invalid → mount() throws. |
destination | string | "antarctica" | Primary destination slug or name. |
startDate | string (YYYY-MM-DD) | "2026-12-01" | Trip departure date. |
endDate | string (YYYY-MM-DD) | "2026-12-14" | Trip return date. Duration is derived from startDate→endDate. |
ages | number[] or CSV string | [45, 47] / "45,47" | One age per traveler. Strongly recommended — without ages, estimate quality drops. |
residence | string | "US" | Traveler residence (country code). Drives carrier eligibility. |
tripCost | number or string | 18000 | Total trip cost in whole dollars — not per-traveler, not cents. |
travelers | number or string | 2 | Number of travelers. |
ref | string | "booking-9281" | Your attribution/reference tag, echoed back for reconciliation. |
logoUrl | string (URL) | "https://cdn.you/logo.svg" | Co-brand logo shown in the widget header. |
accentColor | string (CSS color) | "#00B4A0" | Accent color for buttons and links inside the widget. |
The loader also appends a host=<your-origin> param automatically. It is not part of MountConfig, it is advisory, and it is spoofable — the real trust boundary stays the server-side pk_op_ allowlist.
5. The iframe sandbox
Section titled “5. The iframe sandbox”mount() sets this exact sandbox attribute on the iframe. You don’t configure it — it’s listed so your security review knows what to expect:
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-top-navigation-by-user-activation"| Token | Why it’s set |
|---|---|
allow-scripts | Runs the quote app and Stripe.js inside the iframe. |
allow-same-origin | The framed document is our origin (embedOrigin), so same-origin is safe here. Required for Stripe.js and for the same-origin checkout handoff. |
allow-forms | The quote and checkout forms submit. |
allow-popups | Provider and hosted flows that open a tab (e.g. Stripe / 3DS). |
allow-top-navigation-by-user-activation | Lets the Buy→hosted-checkout handoff navigate the top window — but only on a user click (the Buy button). The iframe can never silently redirect your page. If your own outer sandbox strips this token, the loader covers it with an origin-validated ei:navigate fallback that only ever navigates to a URL on our embed origin. |
The iframe also carries title="ExpeditionInsure quote" and loading="lazy".
6. No unmount() or update() — the remount workaround
Section titled “6. No unmount() or update() — the remount workaround”There is no
unmount()and noupdate()today. [needs-product-work] The global exposes onlymount,on, andembedOrigin. There is no API to tear down a mounted iframe instance or to push new config into a live one.
To change config — for example, when the traveler edits their trip dates or destination on your page — remount: clear the target element and call mount() again with the new config.
const target = document.querySelector("#quote");target.innerHTML = ""; // remove the old iframeExpeditionInsure.mount(target, nextConfig);Notes on remounting:
- Each
mount()call appends a new tracked instance; clearing the target’s DOM first removes the previous iframe from the page. - The shared message bridge is installed once and reused, so remounting does not stack listeners on the
window— re-mounting is cheap. - Subscriptions you registered with
on()are global to the widget, not bound to a single iframe; they keep firing for the new iframe and don’t need re-registering after a remount.
Next steps
Section titled “Next steps”- /reference/events/ — full
on()event payloads and thepolicy.issuedwebhook. - /reference/embed-api/ — the HTTP endpoints the iframe calls, status codes, and rate limit.
- /embedded-insurance/integration-guide/ — the narrative install + security walkthrough.