> ## Documentation Index
> Fetch the complete documentation index at: https://docs.upside.win/llms.txt
> Use this file to discover all available pages before exploring further.

# Iframe Protocol

The exact postMessage contract between the Upside parent shell and an embedded game iframe.

# Iframe Protocol

Upside embeds approved games in an iframe and exchanges a small set of browser messages with that iframe. The protocol is intentionally narrow: parent-to-game messages provide player context, and game-to-parent messages request UI refreshes or parent-shell feedback.

**Origin checks are required**

Treat `postMessage` data as untrusted until `event.origin` matches an expected Upside origin. Never accept a player token from `*`, from localhost, or from another arbitrary site.

## Parent To Game

### Initial Context

When the game iframe is available, Upside sends a context object to the iframe's origin.

```ts
type UpsideContextMessage = {
  balance: string;
  token: string;
  locale: string;
};
```

| Field | Required | Description |
| --- | --- | --- |
| `balance` | Yes | Estimated WIN balance in atomic units. WIN uses 18 decimals. |
| `token` | Yes | Player JWT. Forward it to your backend as `Authorization: Bearer {token}`. |
| `locale` | Yes | Current locale. Upside also appends it to the iframe URL as `?locale=...`. |

### Locale Change

```ts
type LocaleChangeMessage = {
  type: "localeChange";
  locale: string;
};
```

Use this to update copy, number formatting, and any localized game assets without forcing a full iframe reload.

## Game To Parent

### Ready

```ts
window.parent.postMessage({ type: "ready" }, "https://upside.win");
```

Send this after your app has mounted and registered its `message` listener. Upside responds by sending the latest context snapshot.

### Win Modal

```ts
window.parent.postMessage(
  {
    type: "showWinModal",
    wins: "2000000000000000000",
  },
  "https://upside.win",
);
```

Opens the standard Upside win modal. The optional `htmlContent` field can customize modal body content.

### Loss Modal

```ts
window.parent.postMessage(
  {
    type: "showLossModal",
    loss: "1000000000000000000",
  },
  "https://upside.win",
);
```

Opens the standard Upside loss modal. Use this after the backend has settled the session with `processPayout`.

### Custom Modal

```ts
window.parent.postMessage(
  {
    type: "showCustomModal",
    htmlContent: "<p>Round complete</p>",
  },
  "https://upside.win",
);
```

`htmlContent` must be a string. Keep it generated from trusted server-rendered templates or safe static content.

### Toast

```ts
window.parent.postMessage(
  {
    type: "showToast",
    message: "Game saved",
    description: "You can resume this session later.",
    variant: "success",
    duration: 4000,
  },
  "https://upside.win",
);
```

Supported `variant` values are `success`, `error`, `warning`, and `info`. Unknown variants fall back to the default toast.

### Refresh Requests

```ts
window.parent.postMessage({ type: "refetchBalance" }, "https://upside.win");
window.parent.postMessage({ type: "refreshMatchHistory" }, "https://upside.win");
```

Call these after settlement, not before. If a backend payout fails, show an error instead of asking the parent to refresh stale data.

## Message Matrix

| Direction | Message | Required Fields | Typical Timing |
| --- | --- | --- | --- |
| Game -> parent | `ready` | `type` | App mounted and listener registered |
| Parent -> game | context snapshot | `balance`, `token`, `locale` | Initial load, balance/token updates |
| Parent -> game | `localeChange` | `type`, `locale` | Player changes locale |
| Game -> parent | `showWinModal` | `type`, `wins` | Backend settled a winning session |
| Game -> parent | `showLossModal` | `type`, `loss` | Backend settled a losing session |
| Game -> parent | `showCustomModal` | `type`, `htmlContent` | Trusted custom result or explainer |
| Game -> parent | `showToast` | `type`, `message` | Non-blocking status feedback |
| Game -> parent | `refetchBalance` | `type` | After `placeBet` or `processPayout` success |
| Game -> parent | `refreshMatchHistory` | `type` | After `processPayout` success |

## Safe Handler

```ts
const UPSIDE_ORIGINS = new Set(["https://upside.win", "https://www.upside.win"]);

window.addEventListener("message", event => {
  if (!UPSIDE_ORIGINS.has(event.origin)) return;

  const data = event.data ?? {};

  if (typeof data.token === "string") {
    // Store the token only in memory and send it only to your backend.
  }

  if (data.type === "localeChange" && typeof data.locale === "string") {
    // Update translations and number formatting.
  }
});
```

## Common Mistakes

**Using targetOrigin='*' for player token messages**

Do not use `*` when a message includes or requests authenticated player state. Send messages to `https://upside.win` and accept messages only from known Upside origins.
  **Posting the win modal before backend settlement**

The player-facing result should follow backend settlement. If `processPayout` fails, show an error toast and retry or reconcile the session.
  **Trusting client outcome data**

Client events can be modified. Treat the iframe as a display and input surface; final outcome generation and payout calculation belong on the backend.
  **Rendering untrusted htmlContent**

`htmlContent` is powerful and should be reserved for trusted templates. Never pass player-submitted HTML directly into parent-shell modals.