cimplify
Concepts

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:

  1. Verify event.origin is the expected Link origin.
  2. Verify event.source matches the iframe's contentWindow.
  3. Verify event.data.nonce matches 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 identity with the session token and the access token your callback returns.

session_established

Checkout verified the shopper's OTP in-panel and already hydrated their saved details. Checkout has already bound the storefront session to the verified identity on the server (the session token rotated once, the cart folded under the customer). The parent's job is adoption: take the bound session token, the access token and the customer, and persist the refresh token first-party. The parent never claims or refreshes for the iframe.

{
  type: "session_established",
  sessionToken: "…",
  accessToken: "…",
  refreshToken: "…",
  customer: { id: "…", name: "…", email: null, phone: null }
}

Checkout does not wait for a reply — ignoring this message costs only cross-visit recognition, never the current checkout.

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…"
}

identity

Hands the iframe the identity the parent holds: the storefront session token and, when the shopper is signed in, the access token and customer. The SDK sends it on mount and whenever its identity changes; this is also how a hand-rolled parent completes an auth_requested flow.

{
  type: "identity",
  sessionToken: "…",
  accessToken: "eyJ…",
  customer: { id: "…", name: "…", email: null, phone: null }
}

An anonymous parent sends accessToken: null and customer: null.

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

MessagePurpose
get_dataAsk the iframe to emit its current selected data where supported.
logoutClear iframe auth state.
abort_checkoutAbort an in-flight checkout.

SDK Events vs Raw Messages

The SDK re-emits a smaller event set from CimplifyElement:

SDK eventSource raw message
readyready
authenticatedEmitted by SDK once it adopts an established session or restores one.
changeaddress_changed, address_selected, payment_method_selected
order_type_changedorder_type_changed
request_submitrequest_submit
errorerror or SDK auth startup/callback errors

On this page