Element events
The checkout iframe communicates with the parent page via window.postMessage. This is the raw protocol; SDK users usually consume higher-level controller events instead.
This page documents the raw postMessage contract between
link.cimplify.io/elements/checkout and the parent page. The source of truth is
IframeToParentMessage and ParentToIframeMessage in @cimplify/sdk.
For agents: if you use <CimplifyCheckout> or createElements, the SDK
handles these messages for you. Only implement this page directly when embedding
the raw checkout iframe.
Origin and Routing
Iframe messages include a per-element nonce URL parameter. The parent should:
- Verify
event.originis the expected Link origin. - Verify
event.sourcematches the iframe'scontentWindow. - Verify
event.data.noncematches the iframe URL nonce.
The SDK accepts https://cimplify.io, https://*.cimplify.io, and localhost
for development. A raw production integration should normally pin to
https://link.cimplify.io.
const LINK_ORIGIN = "https://link.cimplify.io";
window.addEventListener("message", (event) => {
if (event.origin !== LINK_ORIGIN) return;
if (event.source !== iframe.contentWindow) return;
if (event.data?.nonce !== nonce) return;
// handle trusted message
});Iframe to Parent
ready
First message after iframe load. Reply with init.
{ type: "ready", height: 412, nonce: "ab12cd34" }height_change
Content resized. Apply it to the iframe height.
{ type: "height_change", height: 564, nonce: "ab12cd34" }auth_requested
The standalone auth element needs the parent to authenticate through
Cimplify OAuth. Checkout no longer sends this — its OTP runs in-panel and
identity arrives via session_established.
{
type: "auth_requested",
loginHint: "jane@example.com",
contactType: "email",
nonce: "ab12cd34"
}SDK behavior: silent-first OAuth with loginHint as login_hint;
prompt: "login" skips silent auth so the shopper can change accounts.
Raw iframe behavior: start OAuth yourself and send set_token when your
callback returns an access token.
session_established
Checkout verified the shopper's OTP in-panel and already hydrated their saved
details. The parent's job is durability: claim the storefront session (binds
cart and chat to the verified identity) and persist the refresh token
first-party, then broadcast set_token with the same token.
{
type: "session_established",
accessToken: "…",
refreshToken: "…",
nonce: "ab12cd34"
}Checkout does not wait for a reply — ignoring this message costs only cross-visit recognition, never the current checkout.
session_establishment_failed
Sent by the parent (SDK) back to checkout when the session claim conflicts — the identity was superseded elsewhere. Checkout drops the hydrated saved details and returns to contact entry.
{
type: "session_establishment_failed",
accessToken: "…",
code: "…",
message: "…"
}continue_without_saved_details
The shopper chose to proceed as a guest after recognition (or a challenge was abandoned). The parent clears any identity it holds for this storefront session.
{
type: "continue_without_saved_details",
contact: "jane@example.com",
contactType: "email"
}contact_provided
The shopper supplied a guest contact. The SDK stores it as checkout customer data, but it is not authenticated identity.
{
type: "contact_provided",
contact: "jane@example.com",
contactType: "email"
}address_changed / address_selected
{
type: "address_changed",
address: {
street_address: "12 Independence Ave",
city: "Accra",
region: "Greater Accra",
country: "GH"
},
saveToLink: true
}address_selected carries the same address shape and is emitted when a saved
address is chosen.
payment_method_selected
{
type: "payment_method_selected",
method: {
type: "mobile_money",
provider: "mtn",
phone_number: "+233200000001"
},
saveToLink: true
}order_type_changed
{ type: "order_type_changed", orderType: "delivery" }request_submit
The iframe Pay button was pressed. Respond with process_checkout or call
elements.processCheckout(...).
{ type: "request_submit" }checkout_status
Non-terminal checkout transition.
{
type: "checkout_status",
status: "awaiting_authorization",
context: {
display_text: "Approve the prompt on your phone",
authorization_type: "otp",
provider: "mtn"
}
}checkout_complete
Terminal checkout result.
{
type: "checkout_complete",
success: true,
order: {
id: "ord_…",
order_number: "10042",
status: "confirmed",
total: "29.99",
currency: "GHS"
},
enrolled_in_link: true
}{
type: "checkout_complete",
success: false,
error: {
code: "PAYMENT_DECLINED",
message: "The provider declined the payment.",
recoverable: true
}
}error
{ type: "error", code: "AUTH_CONFIG_REQUIRED", message: "OAuth clientId and redirectUri are required." }logout_complete
The iframe completed logout or cleared local auth state.
{ type: "logout_complete" }Parent to Iframe
init
Sent after ready.
{
type: "init",
businessId: "biz_…",
publicKey: "cpk_live_…",
prefillEmail: "jane@example.com",
appearance: { theme: "light" },
orderTypes: ["delivery", "pickup"],
defaultOrderType: "delivery",
renderSubmitButton: true,
submitLabel: "Pay now",
token: "eyJ…"
}set_token
Authenticates the iframe with the access token returned by the merchant OAuth
callback. This is how the parent completes an auth_requested flow.
{ type: "set_token", token: "eyJ…" }set_cart
{
type: "set_cart",
cart: {
items: [{ name: "Jollof bowl", quantity: 1, unit_price: "29.99", total_price: "29.99", line_type: "simple" }],
subtotal: "29.99",
tax_amount: "0.00",
total_discounts: "0.00",
service_charge: "0.00",
total: "29.99",
currency: "GHS"
}
}process_checkout
{
type: "process_checkout",
cart_id: "cart_…",
order_type: "delivery",
location_id: "loc_…",
enroll_in_link: true,
customer: { name: "Jane", email: "jane@example.com", phone: null },
address: { street_address: "12 Independence Ave", city: "Accra", region: "Greater Accra" }
}Other Commands
| Message | Purpose |
|---|---|
get_data | Ask the iframe to emit its current selected data where supported. |
logout | Clear iframe auth state. |
abort_checkout | Abort an in-flight checkout. |
SDK Events vs Raw Messages
The SDK re-emits a smaller event set from CimplifyElement:
| SDK event | Source raw message |
|---|---|
ready | ready |
authenticated | Emitted by SDK after OAuth succeeds and set_token is broadcast. |
change | address_changed, address_selected, payment_method_selected |
order_type_changed | order_type_changed |
request_submit | request_submit |
error | error or SDK auth startup/callback errors |
Checkout lifecycle
Every Cimplify checkout (embedded iframe, hosted Pay session, React `<CimplifyCheckout>`, or headless API flow) passes through the same ordered set of states. Whether you observe them via `onStatusChange`, the `checkout_status` postMessage event, or the `on_status_change` callback on `processCheckout()`, the names and order are identical.
Catalogue
Browse products, variants, categories, collections, bundles, composites, deals, and price quotes. Read-only on the public client; safe to call from any environment.