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 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

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 after OAuth succeeds and set_token is broadcast.
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