Skip to main content
A session comes back with two URLs, and they are not interchangeable:
Framing url produces a blank box and one console line (frame-ancestors 'none'), with no request reaching us and nothing in your logs. If an embed is blank, check which of the two you used first.
Embedding keeps the payer on your site. Doing that alone works fine — the page can connect its own wallet. To match the page to your brand, pass theme parameters on the URL — colours, font, and corner radius, no CSS.

Bridging a connected wallet

If your app has already connected a wallet, making the payer connect a second time inside the iframe is a real drop-off point. The parent bridge lends your existing connection to the frame: the iframe asks for signatures, your provider produces them, and the payer never reconnects.
Call bridge.stop() when the iframe unmounts, or the listener outlives it.
onPaymentComplete means the payer submitted a transaction — not that funds have settled. Cross-chain routes still have a bridge leg to finish. Release goods on the webhook plus a session read, never on this callback. It is a UI signal, not a settlement signal.

Keeping the bridge in sync

The bridge is constructed with an address and a chain id. If your user switches either, tear the bridge down and start a new one — otherwise the frame keeps signing against the wallet state you handed it at mount:

Security

Messages are origin-checked in both directions — the parent validates the frame’s origin against an allowlist before acting on anything it says, and the iframe does the same for the parent. That is why apiBase has a hard-coded default: a bridge pointed at an arbitrary origin is a phishing surface, so overriding it is deliberate rather than convenient. Never pass a secret key here. The bridge is browser code; sessions are created on your server.