Integration GuideAPI Reference
Integration Guide

Iframe Embedding

Before you start

⚠️

Origin allow-list required

By default the checkout responds with Unauthorized — this page cannot be embedded from this origin when loaded inside an <iframe>. Send us the origin (or list of origins) you plan to embed from and we'll allow them.

⚠️

Referrer required

The checkout resolves where to post events from the Referer header of the first iframe load. If your page sets Referrer-Policy: no-referrer (or referrerpolicy="no-referrer" on the iframe tag), the form works but no events are posted at all — silently.

📘

Same customization as the standalone checkout

Form appearance, locale, payment methods and branding are configured the same way as the standalone checkout — see Hosted Checkout customization.

Embedding

<iframe
  src="https://checkout.8B.world/s/{sessionId}"
  width="100%"
  height="720">
</iframe>

Inside an iframe:

  • All outbound links open in a new tab.
  • The Back to store button emits a postMessage (SESSION_REDIRECT with your back-to-store URL) instead of navigating — your page performs the actual navigation.

Listening to events

The iframe posts messages to the parent window. Every message shares the same envelope; only payload varies between events:

{
  type: string;       // event identifier — see the Events section
  sessionId: string;  // checkout session the form is rendering
  intentId: string;   // intent the session belongs to
  payload?: object;   // event-specific, see each event
}

sessionId carries its entity prefix (IS…), while intentId is the bare number — the same intent is addressed as I{intentId} in the REST API.

Delivery is at-least-once: the same event can arrive several times, and SESSION_PROCESSING repeats periodically while the payment is pending. Deduplicate by type + sessionId and keep handlers idempotent.

PaymentInfo is used as the payload by several events:

{
  paymentId: string;
  paymentMethod: FormPaymentMethodType;  // e.g. "CARD"
  paymentStatus: PaymentStatus;          // e.g. "ACCEPTED"
}

Events

Every event carries sessionId and intentId at the top level. Only payload varies between events.

typesessionIdintentIdpayloadFires when
SESSION_METADATA_SENTThe iframe is ready. Sent in some flows only — do not rely on it in every session.
CHECKOUT_LOADEDThe payment-method screen rendered. Not emitted when the session opens in any other state (processing, finished, expired).
📘

Which failure event will I get?

PAYMENT_ATTEMPT_FAILED — the pay request was rejected up front: nothing was charged and no payment exists. An issuer decline never arrives through it. PAYMENT_FAILED — a payment failed but the session stays open, so the payer can retry in the same form. SESSION_FAILED — the session finished with a failure and is closed; an issuer decline typically surfaces here. Handle all three, and fetch the decline reason via the REST API — the events themselves don't carry it.

Examples

Success

{
  "type": "PAYMENT_ATTEMPT_SUCCESS",
  "sessionId": "IS313605683903180800",
  "intentId": "313605683324366848",
  "payload": {
    "paymentId": "313631993211891712",
    "paymentMethod": "CARD",
    "paymentStatus": "ACCEPTED"
  }
}

Failure

{
  "type": "SESSION_FAILED",
  "sessionId": "IS313644775239876608",
  "intentId": "313644775160184832"
}
{
  "type": "PAYMENT_ATTEMPT_FAILED",
  "sessionId": "IS313639612777435136",
  "intentId": "313639612693549056",
  "payload": {
    "paymentMethod": "CARD",
    "reason": "FX rate expired or not found for payment method BankCardKZ in session IS313639612777435136"
  }
}

Did this page help you?