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

# postMessage events

> The raw protocol of the embedded iframe, for integrating without the SDK.

The embedded iframe reports what happens inside it through `postMessage` messages to the window that contains it:

```js theme={null}
window.parent.postMessage({ type: "TAPIPAY_DOMICILIATION_BANK_SELECTED", payload }, "*");
```

The [SDK](/en/payment-portal/autopay-sdk) does nothing magic with that: it listens to those same messages and re-emits them under friendlier names. This page documents the layer underneath, for when you mount the iframe by hand.

<Tip>
  If you can use the SDK, use it: it saves you the listener, the origin validation and the iframe height. This page is for when you cannot install a package, or you already have your own iframe in place.
</Tip>

## Mounting the view by hand

The widget has two views and each one emits its own set of messages. They are the same ones the SDK mounts, and the URL tells them apart: the autopay one ends in `/autopay`.

<CodeGroup>
  ```html Autopay theme={null}
  <iframe
    src="https://app.tapipay.la/s/acme-corp/embed/CLI-00042/autopay"
    width="100%"
    height="700"
    style="border: none;"
    allow="payment"
    title="Autopay"
  ></iframe>
  ```

  ```html Payment methods theme={null}
  <iframe
    src="https://app.tapipay.la/s/acme-corp/embed/CLI-00042"
    width="100%"
    height="700"
    style="border: none;"
    allow="payment"
    title="Payment methods"
  ></iframe>
  ```
</CodeGroup>

You can identify your company with `companyCode` instead of the alias:

```text theme={null}
https://app.tapipay.la/c/MX-S-12345/embed/CLI-00042/autopay
```

<Note>
  This is different from [embedding the full portal](/en/payment-portal/embed-portal), which shows the debt list. Here you mount only the widget.
</Note>

## Listening to the messages

```js theme={null}
window.addEventListener("message", function (event) {
  // 1. Validate the origin before reading anything
  if (event.origin !== "https://app.tapipay.la") return;

  // 2. Route on the message type
  switch (event.data?.type) {
    case "TAPIPAY_DOMICILIATION_BANK_SELECTED":
      console.log("Bank picked:", event.data.payload.name);
      break;
    case "TAPIPAY_DOMICILIATION_CONFIRMED":
      showProcessingScreen();
      break;
    case "TAPIPAY_AUTOPAY_ACTIVATED":
      showSuccess(event.data.payload.lastFour);
      break;
    case "TAPIPAY_DOMICILIATION_ACCOUNT_CHECK_FAILED":
      track(event.data.payload.reason);
      break;
  }
});
```

<Warning>
  **Always validate `event.origin`.** The messages are emitted with `targetOrigin: "*"`, so your listener also receives whatever any other iframe or script on your page sends. Without that check, anyone can fake a TapiPay event from the user's browser.
</Warning>

In the sandbox environment the origin is `https://homo.tapipay.la`.

<Warning>
  **The shape of the data is not the same as in the SDK.** The raw message is `{ type, payload }`: the content travels **inside `payload`**. The SDK hands that `payload` unwrapped to your handler, but here you have to read `event.data.payload`.
</Warning>

## Lifecycle signals

Both views emit these three signals while they run inside an iframe, no matter what your user does.

| Message            | When it arrives                                     | Data                |
| ------------------ | --------------------------------------------------- | ------------------- |
| `TAPIPAY_JS_READY` | The widget finished mounting and can be shown.      | None                |
| `TAPIPAY_READY`    | The data loaded and the real screen is visible.     | None                |
| `TAPIPAY_RESIZE`   | On mount and every time the content height changes. | `height`, in pixels |

<Warning>
  `TAPIPAY_RESIZE` is the exception to the `payload` rule: the height travels at the root of the message, in `event.data.height`.
</Warning>

None of them has a matching SDK event, because the SDK handles them internally: it uses `TAPIPAY_JS_READY` to remove its loading screen and `TAPIPAY_RESIZE` to adjust the iframe height. If you mount the iframe by hand, that is on you.

```js theme={null}
const frame = document.querySelector("#tapipay");

window.addEventListener("message", function (event) {
  if (event.origin !== "https://app.tapipay.la") return;

  switch (event.data?.type) {
    case "TAPIPAY_JS_READY":
      hideLoadingScreen();
      break;
    case "TAPIPAY_RESIZE":
      frame.style.height = event.data.height + "px";
      break;
  }
});
```

## The messages

Every message has its matching SDK event. The `payload` is identical in both cases, with the same field names.

### Enrollment flow

One message per step your user completes during enrollment. The first four arrive once each, in order.

| Message                                        | SDK event                            |
| ---------------------------------------------- | ------------------------------------ |
| `TAPIPAY_DOMICILIATION_BANK_SELECTED`          | `domiciliationBankSelected`          |
| `TAPIPAY_DOMICILIATION_FORM_COMPLETED`         | `domiciliationFormCompleted`         |
| `TAPIPAY_DOMICILIATION_VERIFICATION_COMPLETED` | `domiciliationVerificationCompleted` |
| `TAPIPAY_DOMICILIATION_CONFIRMED`              | `domiciliationConfirmed`             |
| `TAPIPAY_DOMICILIATION_ACCOUNT_CHECK_FAILED`   | `domiciliationAccountCheckFailed`    |

<Warning>
  `TAPIPAY_DOMICILIATION_CONFIRMED` means your user **pressed confirm**, not that the enrollment became active. The backend processes it asynchronously. The real confirmation is `TAPIPAY_AUTOPAY_ACTIVATED`.
</Warning>

### Enrollment management

| Message                     | SDK event          |
| --------------------------- | ------------------ |
| `TAPIPAY_AUTOPAY_LOADED`    | `autopayLoaded`    |
| `TAPIPAY_AUTOPAY_ACTIVATED` | `autopayActivated` |
| `TAPIPAY_AUTOPAY_REMOVED`   | `autopayRemoved`   |
| `TAPIPAY_AUTOPAY_ERROR`     | `autopayError`     |

### Card payment

These come from the **payment methods view**, when your user pays by card inside the widget.

| Message                         | SDK event             |
| ------------------------------- | --------------------- |
| `TAPIPAY_PAYMENT_WINDOW_OPENED` | `paymentWindowOpened` |
| `TAPIPAY_PAYMENT_SUBMITTED`     | `paymentSubmitted`    |
| `TAPIPAY_PAYMENT_CANCELLED`     | `paymentCancelled`    |

`TAPIPAY_PAYMENT_WINDOW_OPENED` and `TAPIPAY_PAYMENT_CANCELLED` arrive with no data: they tell you the payment window opened and that your user closed it without finishing.

<Warning>
  `TAPIPAY_PAYMENT_SUBMITTED` means **submitted, not settled**. Never mark a debt as paid from this message: the real status arrives through the [payment notification](/en/webhooks/payment-notification) to your backend.
</Warning>

<CardGroup cols={2}>
  <Card title="Autopay payloads" icon="code" href="/en/payment-portal/autopay-sdk#events">
    The fields of the enrollment and autopay management messages, with their types.
  </Card>

  <Card title="Payment payloads" icon="credit-card" href="/en/payment-portal/payment-methods-sdk#events">
    The fields of the card payment messages, with their types.
  </Card>
</CardGroup>
