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
Checkout needs the parent to authenticate the shopper through Cimplify OAuth.
Checkout after a valid contact:
{
type: "auth_requested",
loginHint: "jane@example.com",
contactType: "email",
mode: "checkout",
nonce: "ab12cd34"
}Checkout account change:
{
type: "auth_requested",
mode: "checkout",
prompt: "login",
nonce: "ab12cd34"
}SDK behavior:
mode: "checkout"enables silent-first OAuth.loginHintbecomes OAuthlogin_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.
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.