Skip to content

Versioning & changelog

This page tells you how Expedition Insure ships changes to the embed surfaces you depend on — the widget.js loader, the embed HTTP API, the operator portal, and the policy.issued webhook — and how to track them. It’s for the engineers who keep your integration running after launch. If you’re still wiring things up, start with the embed integration guide and the MCP overview; come back here when you need to know how things change underneath you.

There is no global semantic version number you pin against today. Each surface evolves independently, and we treat its public contract — not a version string — as the thing we keep stable:

SurfacePublic contract you depend onHow you reference it
widget.js loaderwindow.ExpeditionInsure global (mount, on, embedOrigin), MountConfig params, the ei:* event payloadshttps://expedition.insure/widget.js (rolling) or https://expedition.insure/widget/<x.y.z>.js (pinned)
Embed HTTP APIPOST /api/embed/quote, GET /api/embed/options, status codes, response JSONPath + your pk_op_... key
policy.issued webhookThe PolicyIssuedPayload JSON body, X-EI-Signature HMAC schemeYour registered endpoint
Operator portalapi.operatorPortal.* read/write contractsSession-authenticated portal

The rules below describe how we change each contract without breaking you.

The loader is published on two channels:

<!-- Rolling: the latest loader; picks up additive, backward-compatible fixes
automatically (see §2). The right default for most integrations. -->
<script src="https://expedition.insure/widget.js"></script>
<!-- Pinned: this exact build. Immutable — it never changes under you.
Upgrade deliberately by bumping the version in the URL. -->
<script src="https://expedition.insure/widget/1.0.0.js"></script>
ChannelURLCachingUse it when
Rollinghttps://expedition.insure/widget.jsrevalidated on every loaddefault — you want additive fixes automatically, no redeploy
Pinnedhttps://expedition.insure/widget/<x.y.z>.jsimmutable, cached long-livedyou need change-control and want to review each loader upgrade yourself

A pinned URL is an immutable snapshot: widget/1.0.0.js always serves the same bytes. A new loader build publishes a new pinned URL and updates the rolling widget.js; existing pinned URLs are never changed or removed, so a build you pinned can’t shift under you. New pinned versions are announced in the changelog (§6). To upgrade, change the version in your <script> tag and re-test.

Pinning covers the loader only. The embed HTTP API and the policy.issued webhook are always served from their live contracts — there is no pinned version of those, so build them for forward compatibility (below).

We classify every change to a public surface as additive or breaking, and we treat them very differently.

These are backward-compatible and may go live at any time, including on the rolling widget.js (a pinned widget/<x.y.z>.js stays frozen until you upgrade it):

  • A new optional MountConfig param. Unknown params you don’t set are simply omitted from the iframe URL — adding one never affects an existing mount() call.
  • A new optional field on an existing event payload or API response. Your handlers and parsers must ignore fields they don’t recognize.
  • A new optional field on the policy.issued webhook body. Verify the signature over the raw body (see the webhook guide), then read only the fields you need.
  • A new ei:* internal message type. The loader silently ignores inbound message types it doesn’t know (default: return), so this can’t break you.
  • A new event name added to the public set. Subscribing is opt-in via on(); an event you never subscribe to costs you nothing.

Build for forward compatibility. Treat every event payload and API response as an open map: read the fields you use, ignore the rest. Never assert on an exact key set. This is the single most important thing you can do to stay compatible with the rolling loader and the always-live API and webhook surfaces.

A change is breaking if it could make a correctly-written integration stop working. Examples:

  • Removing or renaming a MountConfig param, an event name, an event payload field, an API field, or a webhook field.
  • Changing the type or units of an existing field (for example, treating a whole-dollar field as minor units).
  • Changing a status code, an auth requirement, or the rate limit in a way that rejects previously-valid traffic.
  • Changing the X-EI-Signature scheme or the bytes that are signed.

For breaking changes we follow the process in §4 — advance notice, a deprecation window, and a changelog entry — before anything is removed.

We grow the contract additively. New fields appear on event payloads, API responses, and the policy.issued webhook body without notice (see §2) — your handlers and parsers must ignore fields they don’t recognize. Money is always carried in integer minor units: read premiumCents and commissionCents and divide by 100 yourself for display.

ExpeditionInsure.on("quote.selected", (e) => {
// Integer minor units (cents):
const cents = e.premiumCents; // e.g. 41900
const dollars = (cents / 100).toFixed(2); // "419.00"
});

If we ever need to change the type or units of an existing field, or remove one, that is a breaking change and follows the process in §4 — advance notice, a migration window, and a changelog entry — before anything changes.

When a breaking change is unavoidable, the sequence is:

1. Announce → changelog entry (§6) + the affected guide updated,
describing the change, the migration, and the window.
2. Window → old + new behavior coexist for a deprecation window
so you can migrate without downtime.
3. Cut over → old behavior removed; a final changelog entry records
the removal and its date.

What you should do to stay ahead of it:

  • Watch the changelog (§6). It is the authoritative record of what changed and when.
  • Keep a real contact on file. For the operator portal and webhooks, make sure we have a technical contact who reads change notices.
  • Build forward-compatible per §2 — read the fields you use, ignore the rest.

Surfaces with no breaking-change shield yet

Section titled “Surfaces with no breaking-change shield yet”

Some capabilities don’t exist, so there’s nothing to deprecate — but you should know the current reality so you don’t build against a gap:

  • No policy.cancelled, policy.refunded, or commission.voided webhook. [needs-product-work] policy.issued is the only webhook event we emit. You are not notified when a commission is voided or clawed back. Reconcile commission state from the operator portal rather than assuming webhook coverage of the full lifecycle.
  • No operator-facing setter for webhookUrl, webhookSecret, or commissionRateBps. [needs-product-work] These are configured by Expedition Insure during onboarding, not self-served in the portal. The portal exposes commissionRateBps read-only.
  • No sk_op_ (secret-key) HTTP endpoint. [needs-product-work] Secret keys can be minted, but no server-to-server route consumes them today. Build against the publishable-key embed API (pk_op_...) for now.

One sequencing rule outlives any version change, so write your integration around it from day one:

  • payment.succeeded is a UX-only signal. It fires from the in-iframe checkout return so you can update your UI promptly, but it can be missed (closed tab, dropped frame, blocked message). Never treat it as proof a policy exists.
  • The policy.issued webhook is the authoritative post-sale signal. Policy issuance and the commission record are written server-side off the Stripe webhook, independent of whether the browser ever delivered payment.succeeded. Reconcile and fulfill on policy.issued, deduping on its eventId.

We will not break this contract: even as fields are added, policy.issued remains your source of truth and payment.succeeded remains advisory.

Newest first. This is the public record of contract-affecting changes. Additive changes are listed for awareness; breaking changes and removals are always listed.

  • widget.js loaderwindow.ExpeditionInsure with mount, on, and embedOrigin. Five public events: quote.ready, quote.selected, quote.error, payment.succeeded, payment.failed. Served rolling from https://expedition.insure/widget.js, or pinned from https://expedition.insure/widget/<x.y.z>.js. See §1.
  • premiumCents is the money field on quote.selected and payment.succeeded — an integer in minor units (cents). Divide by 100 for display.
  • Embed HTTP APIPOST /api/embed/quote and GET /api/embed/options, authenticated by a publishable pk_op_... key over a per-operator origin allowlist, rate-limited at 60 requests/minute per key.
  • policy.issued webhook — signed with X-EI-Signature (Stripe HMAC scheme, t=<unix-seconds>,v1=<hex>), delivered at-least-once with up to three attempts; dedupe on eventId. Body carries premiumCents and commissionCents (integer minor units). It is the only webhook event today.
  • Operator portal — self-serve provisioning, publishable-key rotation, and allowed-origin management via api.operatorPortal.*. Embedding starts disabled until enabled by Expedition Insure.

Keeping this current. When a contract-affecting change ships, it lands here first. If you spot drift between this changelog and observed behavior, reach a human at help@expedition.insure.