> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.coinflow.cash/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.coinflow.cash/_mcp/server.

# Embed Events

The onboarding and information request (RFI) iframes post messages to the page
that frames them with `window.postMessage`. Use them to size the iframe and to
react in your page while the sub-merchant works. For server-side updates, use
[onboarding webhooks](/guides/embedded-onboarding/onboarding-webhooks).

## How messages are sent

* Messages are only posted to the `parentOrigin` the embed session was minted with, never to `'*'`.
* Nothing is posted until the iframe has confirmed it's running on an approved site.
* Nothing is posted for link-only sessions, since there's no parent page.
* Payloads never contain form values or personal data.

Every message has the same envelope:

```typescript
{
  source: 'coinflow';
  version: 1;
  type: string;    // one of the types below
  payload: object; // depends on type
}
```

## Listening safely

Check three things before you trust a message: that it comes from the Coinflow
origin, from your own iframe, and has the Coinflow envelope.

**`Browser`**

```typescript Browser
const COINFLOW_ORIGIN = 'https://sandbox-merchant.coinflow.cash'; // https://merchant.coinflow.cash in production
const iframe = document.getElementById('coinflow-onboarding') as HTMLIFrameElement;

window.addEventListener('message', event => {
  if (event.origin !== COINFLOW_ORIGIN) return;
  if (event.source !== iframe.contentWindow) return;
  const message = event.data;
  if (message?.source !== 'coinflow' || message.version !== 1) return;

  handleCoinflowMessage(message);
});
```

Ignore `type`s you don't recognize. New types and new payload fields can be
added within the same `version`.

## Onboarding events

Posted by `/embed/onboarding`.

| `type`                     | `payload`                       | When                                                                                                                                                                                                |
| :------------------------- | :------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `onboarding.heightChanged` | `{height}`                      | The content height changed. `height` is in CSS pixels; set the iframe's height to it.                                                                                                               |
| `onboarding.ready`         | `{status, stepKey?}`            | Once each time onboarding loads. `stepKey` is the step it opened on. After the application is submitted, it's the first step still needing action, such as `wallet`, and absent when there is none. |
| `onboarding.stepChanged`   | `{status, stepKey, stepStatus}` | The sub-merchant moved to another step, or finished one (`stepStatus: 'complete'`). The iframe grows instead of scrolling, so scroll it into view here.                                             |
| `onboarding.submitted`     | `{status}`                      | The sub-merchant submitted its application during this visit.                                                                                                                                       |
| `onboarding.error`         | `{code, message}`               | Something failed. `message` is a fixed, human-readable string.                                                                                                                                      |

* `status` is the onboarding status, the same value [Get Onboarding](/api-reference/api-reference/onboarding/get-onboarding) returns: `not_started`, `in_progress`, `submitted`, `action_required`, `approved` or `declined`.
* `stepKey` is a step key from the same endpoint, such as `profile`, `verification` or `wallet`. `stepStatus` is that step's status: `blocked`, `action_required`, `in_review` or `complete`.

## Information request events

Posted by `/embed/rfi/{rfiId}`. See [Information Requests](/guides/embedded-onboarding/information-requests).

| `type`              | `payload`                | When                                                                                  |
| :------------------ | :----------------------- | :------------------------------------------------------------------------------------ |
| `rfi.heightChanged` | `{height}`               | The content height changed. `height` is in CSS pixels; set the iframe's height to it. |
| `rfi.ready`         | `{rfiId, status}`        | Once each time the RFI loads.                                                         |
| `rfi.submitted`     | `{rfiId, status}`        | The sub-merchant submitted its answers during this visit.                             |
| `rfi.error`         | `{rfiId, code, message}` | Something failed. `message` is a fixed, human-readable string.                        |

`status` is the RFI's status, the same value
[List RFIs](/api-reference/api-reference/rfi/list-rfis) returns: `open`,
`reopened`, `submitted`, `completed` or `cancelled`.

## Error codes

| `code`            | Meaning                                                                              | What to do                                                     |
| :---------------- | :----------------------------------------------------------------------------------- | :------------------------------------------------------------- |
| `session_expired` | The embed session reached its 8-hour limit or was revoked                            | Mint a new session and reload the iframe                       |
| `load_failed`     | The page couldn't load its data                                                      | Offer a retry that reloads the iframe                          |
| `save_failed`     | The sub-merchant's changes couldn't be saved                                         | The iframe shows the error; no action needed unless it repeats |
| `not_found`       | RFI events only: the RFI doesn't exist, isn't this sub-merchant's, or was never sent | Check the `rfiId`                                              |
| `unexpected`      | Any other failure                                                                    | Offer a retry that reloads the iframe                          |

Treat any other `code` like `unexpected`.