Quickstart
Get the Expedition Insure quote widget rendering on your page and reacting to its events — the fastest path from a publishable key to a working embed. This page is for operator tech teams wiring up their first integration. For the full contract, see the embed integration guide; for the raw event payloads see /reference/events/; for sequencing your own checkout off the widget see /guides/checkout-sequencing/.
1. Prerequisites
Section titled “1. Prerequisites”You need two things before the widget will render, both tied to your operator account:
- A publishable key (
pk_op_...). It authenticates every embed request and ships in your page’s HTML — it’s public by design, like a Stripepk_. You pass it asconfig.pk. - An allowlisted origin. We add the exact origins your widget loads from (scheme + host + port, e.g.
https://www.youroperator.com) to your per-operator allowlist. A request from a non-allowlisted origin gets a fail-closed, blank frame — never data.
New here? Start at the partner registration page — it walks you through the setup steps, then signs you in and provisions your operator account (and your first pk_op_...) in one go. After that, manage your key and origins in the operator portal. Send us every origin you’ll embed from (production, staging, and any sandbox); up to 20 per operator.
The key is public; the origin is the boundary. A copied
pk_op_...is useless from an origin we haven’t allowlisted. The trust boundary is the server-side origin allowlist plus a per-key rate limit, not key secrecy.
2. Add the script and mount
Section titled “2. Add the script and mount”Add one script tag, then call ExpeditionInsure.mount(target, config):
<div id="quote"></div>
<script src="https://expedition.insure/widget.js"></script><script> 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, // total trip cost, whole dollars travelers: 2, });</script>widget.js is a tiny, React-free loader. On mount() it injects a sandboxed <iframe> into your target element, pointing at https://expedition.insure/embed/quote?<params>. The quote form and options render on our origin — your page never handles insurance data directly.
target— a CSS selector string ("#quote") or anHTMLElement. If it matches nothing,mount()throws.config—pkis required (missingpkthrows); everything else is optional and pre-fills the form. See the config params table for the full list.mount()returns the createdHTMLIFrameElement.- The iframe auto-sizes to its content (no inner scrollbars) via an
ei:resizerelay — you don’t set a height.
The global is window.ExpeditionInsure with { mount, on, embedOrigin }. There is no unmount() or update() today — to change config, clear the target element and call mount() again. [needs-product-work]
3. Wire the events
Section titled “3. Wire the events”Subscribe with ExpeditionInsure.on(eventName, handler). It returns an unsubscribe function. Unknown event names throw. Handlers run isolated — a throw in your handler is caught and logged, never breaking the bridge. Multiple handlers per event run in subscribe order.
The two events you’ll wire first:
| Event | Payload shape | Fires when |
|---|---|---|
quote.ready | { quoteId?: string, plansCount?: number } | The quote form renders and options are ready. |
quote.selected | { planId: string, premiumCents: number, premium: number, currency?: string } | A traveler clicks a plan card. |
ExpeditionInsure.on("quote.ready", (e) => { console.log("ready", e.quoteId, e.plansCount);});
ExpeditionInsure.on("quote.selected", (e) => { // premiumCents is canonical — integer minor units (cents). const dollars = e.premiumCents / 100; console.log("picked", e.planId, dollars, e.currency);});Read
premiumCents(integer minor units).premiumCentsis the premium field — divide by 100 yourself for display.
The full event catalog — quote.error, plus the embedded-checkout payment.succeeded / payment.failed events — is at /reference/events/.
4. Minimal working example
Section titled “4. Minimal working example”A complete, copy-pasteable page. Drop in your pk_op_..., serve it from an allowlisted origin, and open it:
<!doctype html><html lang="en"> <head> <meta charset="utf-8" /> <title>Travel insurance quote</title> </head> <body> <h1>Add travel insurance to your trip</h1> <div id="quote"></div>
<script src="https://expedition.insure/widget.js"></script> <script> 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, // total trip cost, whole dollars travelers: 2, });
ExpeditionInsure.on("quote.ready", (e) => { console.log("quote ready:", e.quoteId, "plans:", e.plansCount); });
ExpeditionInsure.on("quote.selected", (e) => { // Canonical premium is premiumCents (integer minor units). const display = (e.premiumCents / 100).toLocaleString("en-US", { style: "currency", currency: e.currency || "USD", }); console.log("plan selected:", e.planId, display); }); </script> </body></html>If the iframe stays blank, your origin probably isn’t allowlisted yet — check the console for a frame-ancestors CSP violation. If quote.error fires immediately, verify the pk_op_... value and that your page origin is on the allowlist.
Next steps
Section titled “Next steps”- /guides/checkout-sequencing/ — sequence your own trip-charge checkout off the widget’s events.
payment.succeededis a UX-only signal that can be missed; reconcile sales from the authoritativepolicy.issuedwebhook, and keep your trip charge on a separate rail. - /reference/events/ — every event and its exact payload, including
quote.error,payment.succeeded, andpayment.failed. - embed integration guide — config params, the key/origin exchange, CSP and sandbox requirements, and troubleshooting in full.