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

# Embedded checkout

> Put the AgentaOS checkout on your own page, inline or as an overlay, with a script tag, npm, or React.

The embedded checkout is the AgentaOS checkout on your own page: **inline** in a spot you choose, or as an **overlay** over the page. Card, bank transfer, trials, discount codes, VAT and 3D Secure work exactly as on the hosted checkout. AgentaOS stays the merchant of record.

It runs in the browser only. For server work, such as creating checkouts or reading subscriptions, use [`@agentaos/pay`](/sdk/pay-overview).

## Before you start

<Steps>
  <Step title="Approve your site">
    In the dashboard, **Settings → Developers → Approved sites**, add each site you'll embed on, e.g. `https://acme.com`. The browser refuses to show your checkout anywhere else. Test products also work on `localhost` without adding it.
  </Step>

  <Step title="Set your look">
    **Settings → Business → Checkout appearance**: button color, font, corners, background. It applies to the hosted and the embedded checkout alike.
  </Step>

  <Step title="Get the product's buyer link">
    The `checkoutUrl` from `agenta products create`, the SDK, or the product page. **Embed on your site** on the product page has a ready snippet.
  </Step>
</Steps>

## Script tag

No build step:

```html theme={"system"}
<script src="https://app.agentaos.ai/v1/agentaos.js"></script>
<div id="checkout"></div>
<script>
  const checkout = AgentaOS.checkout.open({
    link: 'https://app.agentaos.ai/pay/UID95sZlBqXrVKlUHKmLhQ',
    target: '#checkout', // leave out for the overlay
    onEvent: (e) => console.log(e.name, e.data),
  });
</script>
```

## npm

```bash theme={"system"}
pnpm add @agentaos/checkout
```

```ts theme={"system"}
import { loadAgentaOS } from '@agentaos/checkout';

const agentaos = await loadAgentaOS(); // null on the server
agentaos?.checkout.open({ link, target: '#checkout', onEvent });
```

`loadAgentaOS()` adds the script once, however many times it's called (the first call's `origin` wins). While developing against a local AgentaOS, pass `loadAgentaOS({ origin: 'http://localhost:3000' })`. If the script can't load, the promise rejects with a plain sentence and a later call tries again. `<AgentaOSCheckout>` reports it to `onEvent` as `checkout.error` `not_loaded`.

## React

```tsx theme={"system"}
import { AgentaOSCheckout, useAgentaOSCheckout } from '@agentaos/checkout/react';

// Inline: switching `link` (e.g. a Monthly/Yearly toggle) updates the open checkout.
<AgentaOSCheckout link={yearly ? YEARLY_LINK : MONTHLY_LINK} email={user.email} onEvent={onEvent} />

// Overlay, from a button: it lives as long as this component (unmounting closes it)
const { open } = useAgentaOSCheckout();
<button onClick={() => open({ link: STARTER_LINK })}>Buy</button>
```

## Options

<ParamField body="link" type="string">
  The product's buyer link.
</ParamField>

<ParamField body="session" type="string">
  Instead of `link`: the `sessionId` of a checkout your server created with [`checkouts.create`](/sdk/pay-checkouts). Use it for logged-in users, with your own `metadata`. Create the checkout when the buyer clicks Buy, not on every page view: checkout creation is [rate-limited](/api-reference/introduction#rate-limits) to 60 per minute from your server's IP address, shared by all your buyers.
</ParamField>

<ParamField body="target" type="string | HTMLElement">
  Selector or element for inline. Leave it out for the overlay.
</ParamField>

<ParamField body="email" type="string">
  Prefill the buyer's email. Sent to the checkout directly, never in a URL.
</ParamField>

<ParamField body="country" type="string">
  Prefill the buyer's country, ISO code. Sent to the checkout directly, never in a URL.
</ParamField>

<ParamField body="successUrl" type="string | false">
  Where the whole page goes after payment, with `sessionId` added (https only). Defaults to the checkout's success URL: the one given to `checkouts.create`, else the product's. `false` keeps the page where it is.
</ParamField>

<ParamField body="onEvent" type="(event) => void">
  Receives the [events](#events) below.
</ParamField>

`open()` returns `{ update({ link | session }), close() }`. Prefill and `successUrl` are read when the checkout opens; `update()` switches only the product or checkout.

## Events

| `name` | `data` |
| - | - |
| `checkout.loaded` | `{ product: { name, type, billingInterval, trialDays }, currency, testMode }` |
| `checkout.completed` | `{ sessionId, kind: 'payment' \| 'trial' \| 'bank_transfer', email, amountMinor, currency, successUrl }` |
| `checkout.closed` | `{}`: the overlay closed, by the buyer, by `close()`, or because the React component that opened it unmounted |
| `checkout.error` | `{ code: 'not_loaded' \| 'not_found' \| 'unavailable', message }` |

`amountMinor` is what was charged today in minor units (0 for a free trial), or what the buyer was asked to send for a bank transfer. A bank transfer never moves the page: the buyer needs the bank details on screen.

<Warning>
  **An event is never proof of payment.** Unlock access from the [webhook](/payments/webhooks) or `checkouts.retrieve(sessionId)`, and handle `checkout.completed` once per `sessionId`: a reload of a finished checkout sends it again.
</Warning>

`not_loaded` after 10 seconds usually means the page's site is not on the business's approved sites.

## Payment methods in a frame

Cards with 3D Secure work everywhere. On EUR sales the checkout also offers bank transfer. Apple Pay and Google Pay appear on devices that support them.

## Next steps

<Columns cols={2}>
  <Card title="Checkouts" icon="credit-card" href="/payments/checkouts">
    Create a checkout on your server for a logged-in user.
  </Card>

  <Card title="Webhooks" icon="webhook" href="/payments/webhooks">
    Unlock access when the payment is confirmed.
  </Card>
</Columns>
