Direct iframe integration
Use the hosted quote flow without the SDK, in your own iframe or a mobile webview.
Everything the SDK does is built on one hosted page:
https://buy.coverport.io/embed/quote?pk=<publishable key>&host=<your origin>&product=<slug>You can load that URL in any iframe or webview and talk to it with postMessage. This is the supported path for iOS and Android apps today; native SDKs are planned.
URL parameters
Prop
Type
Embedding
<iframe
src="https://buy.coverport.io/embed/quote?pk=pk_test_…&host=https%3A%2F%2Fshop.example.com"
title="CoverPort insurance quote"
allow="payment"
style="width:100%;max-width:480px;height:min(720px,92vh);border:0;border-radius:12px"
></iframe>allow="payment"is required for wallet payments (Apple Pay, Google Pay, Link) to work inside the frame.- The flow is responsive down to 320px wide. 480 by 720 is the layout the SDK uses.
- The page sets
X-Frame-Options/CSP so it can only be framed when thepkis valid. There is no allowlist of framing origins; the key is the credential.
Messages from the flow
The flow posts messages to window.parent. Every message is an object with __coverport: true and a type.
type | Payload | When |
|---|---|---|
ready | none | The flow has loaded and rendered its first screen. Use it to hide a spinner. |
complete | sale: CoverportSale | Payment succeeded and the policy was issued. Fires once. |
close | none | The buyer asked to leave the flow from inside it. Remove the iframe. |
CoverportSale is documented in the Reference.
Listening safely
Always check event.origin and event.source before trusting a message. The SDK does exactly this:
const iframe = document.querySelector('#coverport-frame');
const EMBED_ORIGIN = 'https://buy.coverport.io';
window.addEventListener('message', (event) => {
if (event.origin !== EMBED_ORIGIN) return;
if (event.source !== iframe.contentWindow) return;
const data = event.data;
if (!data || data.__coverport !== true) return;
switch (data.type) {
case 'ready':
spinner.hidden = true;
break;
case 'complete':
recordSale(data.sale);
break;
case 'close':
iframe.remove();
break;
}
});Mobile webviews
Load the same URL in a WKWebView (iOS) or WebView (Android) and bridge messages back to native code.
The flow calls window.parent.postMessage, and in a top-level webview window.parent === window, so the flow does not post. Inject a shim that forwards to a WKScriptMessageHandler instead:
let shim = """
window.parent = { postMessage: function (msg) {
window.webkit.messageHandlers.coverport.postMessage(msg);
}};
"""
let script = WKUserScript(source: shim, injectionTime: .atDocumentStart, forMainFrameOnly: true)
config.userContentController.addUserScript(script)
config.userContentController.add(self, name: "coverport")
webView.load(URLRequest(url: URL(string:
"https://buy.coverport.io/embed/quote?pk=pk_live_…&host=app://coverport-host")!))func userContentController(_ c: WKUserContentController, didReceive m: WKScriptMessage) {
guard let body = m.body as? [String: Any], body["__coverport"] as? Bool == true else { return }
switch body["type"] as? String {
case "complete": handleSale(body["sale"])
case "close": dismiss()
default: break
}
}Card entry in webviews
Payment is collected by Stripe Checkout inside the flow. Both platforms support this in a standard webview; do not disable JavaScript or third-party cookies for the CoverPort origin.
Why there is no cookie
The flow deliberately does not rely on cookies. Third-party cookies are blocked inside iframes by every major browser, so the buyer's identity step runs server-side on CoverPort and the resulting session lives in the frame's own memory. Reloading the frame starts a fresh session.