> This page is for version 🇦🇺 AU (default).
> For other versions, use one of these documentation indexes:
> - 🇦🇺 AU (default): https://docs.humanapproved.com/au/llms.txt
> - 🇬🇧 UK: https://docs.humanapproved.com/uk/llms.txt
> - 🇺🇸 US: https://docs.humanapproved.com/us/llms.txt

> This is HumanApproved's documentation, as Markdown for AI agents. /llms.txt lists every page; /llms-full.txt has them all. If you're helping someone integrate HumanApproved, start with /guides/ai-coding-agents.

# Embed SDK

> What the HumanApproved embed does, how to load a pinned version with Subresource Integrity, and how to protect a checkout step with protect().

The embed is a small script (no dependencies, under 15 KB gzipped) that runs on your quote and checkout pages. You tell it which step to protect, usually the button that continues to payment. When someone continues from that step, the embed:

1. Holds the click or submit and asks HumanApproved whether this purchase needs the customer's approval.
2. On **continue**, lets it proceed unchanged.
3. On **review**, sends the quote for approval and shows the agent a [pause panel](/au/embed/sdk/pause-panel) with a link to pass to the customer. Once the customer approves the purchase, the panel closes and the step proceeds automatically.

If HumanApproved doesn't respond in time, or an error occurs, the step proceeds.

## Install

Load the embed on **every page of the journey**, not only the checkout page: what it measures counts from the first page. Load a pinned version with its Subresource Integrity hash:

```html
<script src="https://cdn.beopen.com/approvals-embed/1.5.0/embed.js"
        integrity="sha384-…" crossorigin="anonymous" async></script>

<script src="/js/checkout-approvals.js" defer></script>
```

Copy the snippet from the portal's **Embed** page: it has the current version and its `integrity` hash filled in. There's no "latest" URL. To upgrade, change both the version and the hash.

> **Note**
>
> Your Content Security Policy must allow the CDN in `script-src` and `https://au.humanapproved.com` in `connect-src` and `img-src`. See [Content Security Policy](/au/guides/content-security-policy).

## Protect a step

The embed loads `async`, so queue your call. The queue works whether your script runs before or after the embed loads:

**`checkout-approvals.js`**

```js title="checkout-approvals.js"
window.HumanApproved = window.HumanApproved || { q: [] };
HumanApproved.q.push(["protect", {
  key: "pk_live_…",                 // your publishable key
  quote: "HTA-4471920",             // your quote reference (not personal data)
  before: "#continue-to-payment",   // the button, link or form to protect
  quoteDetails: () => ({            // the quote the customer will approve
    quote: {
      policyholder: { firstName: "Sam" },
      destination: "Japan",
      dates: { from: "2026-12-03", to: "2026-12-17" },
      travellers: 2,
      cover: "Comprehensive",
      excess: 200,
      premium: { amount: 412.6, currency: "AUD" },
    },
    mobile: "+61 491 570 156",
  }),
}]);
```

`quote` holds your product's fields, as set on the product's **Fields** page in the portal. Every option is described in [protect() options](/au/embed/sdk/options).

> **Note**
>
> **The names you'll see.** The product is HumanApproved. The script comes from `cdn.beopen.com/approvals-embed`, its global is `HumanApproved`, its console messages start with `[humanapproved]`, and its browser storage keys start with `agent-signals`. You can search for any of them to find the integration.

### What `before` protects

`before` is a CSS selector for a button or link (the embed protects its click) or a form (it protects the submit). A form's submit is protected when:

* `before` matches the form or an element around it,
* the protected button submits it, or
* your code calls `form.requestSubmit()` with no button and the protected element is one of the form's submit buttons.

`form.submit()` doesn't fire a submit event, so it can't be protected. Submit from code with `requestSubmit()`.

### One checkout, several products

A publishable key identifies one product. If your journey sells several products, call `protect()` with the key of the product being bought, once it's known. Load the embed on every page, and make the call from the page where the product is selected. Calling `protect()` again for the same step with another product's key and quote replaces the earlier call, and the next press checks the new quote against that product.

Give each step its own selector. If the pressed button is replaced while it's held and `before` then matches several elements (for example, one button per plan), the embed doesn't re-fire the press, and the next press proceeds.

### Single-page apps

Calling `protect` again for the same `before` updates its options, and protection keeps working when the button is re-rendered. If you pass a different `quote`, any check or review in progress for the old quote is dropped, its panel closes, and the next press checks the new quote.

## What happens at the protected step

| What happens                                                          | What the embed does                                                                                                                           |
| --------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| The check returns **continue**                                        | Replays the original click or submit, untouched.                                                                                              |
| The check returns **review**                                          | Sends `quoteDetails()` for approval and shows the pause panel. On approval, closes the panel and replays the step.                            |
| The customer **declines**, or the link **expires**                    | Tells the agent nothing was paid, and to ask the customer what they want to do. The step stays paused, and the next attempt runs a new check. |
| **No response in 2.5 seconds** (or [`timeoutMs`](/au/embed/sdk/options)) | Lets the step proceed and records the check as `fail_open`.                                                                                   |
| **Any error** (a revoked key, an unpublished product, a server error) | Lets the step proceed. The browser console shows the reason.                                                                                  |
| **A press after a fail-open**                                         | Checks again, waiting at most 1 second. If that also fails open, later attempts for this quote proceed unchecked until the page reloads.      |

For a product set to monitor only, the press isn't held at all: the check runs in the background. See [Monitor only](/au/guides/monitor-only).

## Next steps

#### [protect() options](/au/embed/sdk/options)

Every option, its type and default.

#### [Handling the outcome](/au/embed/sdk/on-result)

Send each outcome to your analytics or monitoring.

#### [The pause panel](/au/embed/sdk/pause-panel)

What the agent sees, and how to change it.

#### [After an approval](/au/embed/sdk/after-an-approval)

What happens next, and when a new approval is needed.

#### [Browser support and storage](/au/embed/sdk/browser-support)

Requirements, and what the embed keeps in the browser.