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

# Script reference

> Onstacks.open, Onstacks.mount, callbacks, errors, messages and security.

## Install

```html HTML theme={"system"}
<script
  src="https://js.onstacks.io/embed/1.1.0.js"
  integrity="sha384-cNfWGj5hpTIPM/rc1j0u5sh46v+otoZGpoIbtDf4G3HLU7S+v5xsYAImGeSCmsow"
  crossorigin="anonymous"
></script>
```

The same script serves Sandbox and Live links. Versioned files never change; `/embed/v1.js` tracks the latest 1.x and can't be pinned with `integrity`. Hashes for every version are at [`/embed/versions.json`](https://js.onstacks.io/embed/versions.json). To self-host, copy the file into your build; it has no dependencies.

## `Onstacks.open(target, callbacks)`

Opens the widget in a modal window and returns a handle.

`target` is one of:

* an options object `{ widgetId, environment, type, asset, chain, currency, amount, wallet, ref, redirect }` (see [Add the widget](/ramps/widget/quickstart)). The script builds the link and adds `origin`, your page's origin, so the widget can post messages back to it. During local development, `host` can point the link at a widget running on `localhost`.
* a [signed link](/ramps/widget/signed-links).

| Callback | Called with | When |
| - | - | - |
| `onReady` | – | The widget has loaded. |
| `onCompleted` | `{ ref }` | The customer finished and the payment was recorded. |
| `onFailed` | `{ ref, reason }` | The payment failed, was cancelled or expired. `reason` may be absent. A retry that succeeds calls `onCompleted`. |
| `onClose` | – | The window was closed (close button, Escape or `close()`). A payment the customer already funded continues. |
| `onResize` | `{ height }` | The widget changed height. The frame is resized for you. |
| `onError` | `{ reason }` | `expired`: the link's `exp` has passed, nothing was opened. `timeout`: the widget didn't load within 20 seconds. |

`ref` is the value you passed when opening the widget. Callbacks report what the widget said in the customer's browser; they're not proof of payment. See [Track payments](/ramps/widget/track-payments).

| Handle | Does |
| - | - |
| `close()` | Closes the window. |
| `reopen()` | Opens the same link again while it's valid. It's a fresh start, not a resume; never ask a customer who already paid to pay again. Returns `false` if the link expired. |

## `Onstacks.mount(element, target, callbacks)`

Same `target` and callbacks, rendered inline inside `element` instead of a window. The frame grows with the widget and the page scrolls. Returns `{ destroy() }`, which removes the frame. If the link has already expired, `onError` runs with `expired` and nothing is rendered.

## Errors

`open` and `mount` throw before anything is shown when the call itself is wrong:

| Message | Cause |
| - | - |
| `Onstacks: widgetId is required` | `widgetId` missing, or not 3 to 40 lowercase letters, digits and hyphens. |
| `Onstacks: host must be an absolute URL` | `host` isn't a full URL such as `http://localhost:3000`. |
| `Onstacks: pass a signed link or { widgetId, ... }` | `target` is neither an options object nor a URL. |
| `Onstacks: not an Onstacks widget link` | The link's origin isn't `ramp.onstacks.io`, `ramp-sandbox.onstacks.io` or `localhost`. |
| `Onstacks: mount needs an element` | The first argument to `mount` isn't a DOM element. |

Problems after the window opens, such as a wrong widget ID or an unlisted website, show inside the widget as a not-found screen; see [If it doesn't work](/ramps/widget/quickstart#if-it-doesnt-work).

## Content Security Policy

```
script-src 'nonce-…' https://js.onstacks.io; frame-src https://ramp.onstacks.io https://ramp-sandbox.onstacks.io
```

`script-src` covers the embed script; your `Onstacks.open(...)` call needs a nonce or to live in your bundle. The script adds no stylesheets, so a strict `style-src` is fine.

## What the script does

It creates a `<dialog>` or `<iframe>` for the link, listens for `message` events, forwards the ones that pass these checks to your callbacks, and removes everything on close:

* `event.origin` is the widget's origin, and that origin is `ramp.onstacks.io` or `ramp-sandbox.onstacks.io` (or `localhost` in development).
* `event.source` is the frame it opened.
* The data is `{ source: "onstacks-widget", version: 1, message }` and the payload has the documented shape.

It makes no network requests, stores nothing, evaluates nothing and exposes one frozen global, `window.Onstacks`. The file is served unminified so you can read it.

## Messages

If you embed the widget yourself, these are the messages it posts to the parent page, each wrapped as `{ source: "onstacks-widget", version: 1, message: { type, payload } }`. The widget posts only to the website in your settings (or the signed link's `origin`).

| Type | Payload | When |
| - | - | - |
| `widget.ready` | – | Loaded. |
| `widget.resize` | `{ height }` | Content height changed. |
| `transaction.completed` | `{ ref }` | The customer finished and the payment was recorded. |
| `transaction.failed` | `{ ref, reason? }` | Payment failed, cancelled or expired. |
| `widget.close` | – | Reserved; not sent yet. |

<Accordion title="Embedding without the script">
  Open `https://ramp-sandbox.onstacks.io/w/<widget-id>?type=offramp&ref=…` (or a signed link) in your own iframe with `allow="camera; clipboard-write"` and apply the same checks before trusting a message:

  ```js JavaScript theme={"system"}
  const widgetOrigin = new URL(widgetUrl).origin;
  if (!["https://ramp.onstacks.io", "https://ramp-sandbox.onstacks.io"].includes(widgetOrigin)) throw new Error("not a widget link");

  const frame = document.createElement("iframe");
  frame.src = widgetUrl;
  frame.allow = "camera; clipboard-write";
  document.body.append(frame);

  window.addEventListener("message", (event) => {
    if (event.origin !== widgetOrigin || event.source !== frame.contentWindow) return;
    const data = event.data;
    if (!data || data.source !== "onstacks-widget" || data.version !== 1) return;
    const { type, payload } = data.message;
    if (type === "widget.resize") frame.style.height = `${payload.height}px`;
    if (type === "transaction.completed") document.getElementById("status").textContent = `Payment ${payload.ref} received.`;
  });
  ```
</Accordion>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.