> 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.

# Add the embed

> How to add the HumanApproved embed to an insurance quote journey, protect the step before payment and send the quote for the customer's approval.

The embed is a small script that runs on your quote pages. When someone continues to payment, it asks HumanApproved whether the purchase can proceed. Customers buying for themselves proceed as they would normally. When an AI agent is buying, the step pauses and the agent is shown a link to pass to the customer, who approves the purchase.

> **Info**
>
> You need a published product, a test publishable key (`pk_test_…`) and your test website on the product's allowed websites. See [Set up a product](/au/portal/set-up-a-product) and [Keys and environments](/au/concepts/keys-and-environments).

#### Load the script on every page of the journey

Put the embed in the shared layout of your quote journey, so it's on **every page**, not only the payment step. The checks look at how the whole journey was completed, from the first page the script is on: a script loaded only on the payment page has little to measure.

```html
<!-- On every page of the quote journey, in your shared layout -->
<script src="https://cdn.beopen.com/approvals-embed/1.5.0/embed.js"
        integrity="sha384-…" crossorigin="anonymous" async></script>

<!-- On the page with the step that continues to payment: your own file, with the code below -->
<script src="/js/checkout-approvals.js" defer></script>
```

Copy the tag from the **Embed** page in the portal: it has the current version and its `integrity` hash filled in.

* **Pinned with SRI.** The version is in the address and the browser checks the file against the hash, so your pages only run the file you tested. There's no "latest" address: to upgrade, change the version and the hash together.
* **Keep `crossorigin="anonymous"`.** The browser only checks the hash on a script loaded that way.
* **`async`** means the script never holds up your page.

If your site sends a Content Security Policy, allow the CDN and the API: see [Content Security Policy](/au/guides/content-security-policy).

#### Protect the step before payment

In your own file (here `/js/checkout-approvals.js`, served from your site like your other scripts), call `protect`:

**`checkout-approvals.js`**

```js title="checkout-approvals.js"
window.HumanApproved = window.HumanApproved || { q: [] };
HumanApproved.q.push(["protect", {
  key: "pk_test_…",               // your publishable key
  quote: "HTA-4471920",           // your quote reference: 1–128 characters, not personal data
  before: "#continue-to-payment", // the button, link or form that continues to payment
  quoteDetails: () => ({
    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",    // the customer's own mobile: the code goes here
  }),
}]);
```

The queue works whichever loads first, your file or the embed. Keep the call in a file rather than an inline `<script>`, so a strict CSP doesn't block it.

`before` is a CSS selector for the step to protect:

| Element          | What's protected | Example                                               |
| ---------------- | ---------------- | ----------------------------------------------------- |
| A button or link | Its click        | `#continue-to-payment`, `a[href="/checkout/payment"]` |
| A form           | Its submit       | `form#payment-details`                                |

Protection keeps working when the element is re-rendered. In a single-page app, calling `protect` again with the same `before` updates its options.

#### Give the quote and the customer's mobile

`quoteDetails` returns this quote's values as `{ quote, mobile }`. It's only called when the customer needs to approve (and on later presses after an approval, to detect a changed quote), so an ordinary check sends no personal data.

* **`quote`** holds the product's published fields, nested as their keys say. You'll find them on the product's **Fields** page in the portal. Dates are `YYYY-MM-DD`, money is `{ amount, currency }` and lists are arrays. A missing required field or a value of the wrong type is refused; fields the product doesn't define are dropped.
* **`mobile`** is the customer's own mobile number, where the approval code is sent.
* **Keep it fast and free of side effects.** It's called when a review starts, with up to 10 seconds to return. After an approval, it's called each time the step is used, to check the quote hasn't changed, and the step waits up to 2.5 seconds for it. Read the values already on your page rather than fetching them, and don't change anything when it's called.

The quote is what the customer sees and approves on the approval page, so check every value matches what they'll pay for. See [After an approval](/au/embed/sdk/after-an-approval) for how a changed quote is handled.

#### Add your own ID or signal (optional)

Two optional settings add context to each check:

```js
HumanApproved.q.push(["protect", {
  // …key, quote, before, quoteDetails as above
  userId: "CUST-1001",                         // your own ID for this customer
  signal: { source: "cloudflare", score: 2 },  // your own bot detection's result
}]);
```

* **`userId`** is your own opaque ID for the customer, such as a customer or account number, so you can find their checks and approvals in the portal and your server gets it back with the review. Don't use an email address, phone number or name. If the value looks like an email address or phone number, it isn't stored.
* **`signal`** passes the result of your own bot detection, for a product set to use your own signal on its **Agent detection** page. See [Agent detection](/au/concepts/agent-detection).

All options, including `onResult` for your own logging, are in [protect() options](/au/embed/sdk/options).

#### Check what happens on the page

When someone uses the protected step, the embed holds the click or submit and asks HumanApproved whether to continue:

| Response                                     | What happens                                                                                                                                                              |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Continue** (not an agent)                  | Nothing visible. The original click or submit proceeds unchanged.                                                                                                         |
| **Review** (an agent)                        | The embed sends the quote and shows the agent a pause panel with a link to pass to the customer. It waits for the customer's answer, and the wait resumes after a reload. |
| **Approved**                                 | The panel closes and the step proceeds automatically.                                                                                                                     |
| **Declined or expired**                      | The panel tells the agent nothing was paid, and to ask the customer what they want to do. The step stays paused, and the next try runs a new check.                       |
| **No response in 2.5 seconds, or any error** | The step proceeds, and the browser console shows a warning with the reason.                                                                                               |

You can change the panel's wording and look on the product's **Agent popup** page: see [The pause panel](/au/embed/sdk/pause-panel). Whether the step pauses only for agents, for every purchase or never depends on the product's **When to ask for approval** setting: see [Approval methods](/au/concepts/approval-methods).

#### Verify the approval on your server

The embed runs in the browser, and an agent driving the checkout controls that browser. Before you bind the policy, have your server confirm the approval with your secret key. See [Verify on your server](/au/guides/verify-on-your-server).

## Next steps

#### [Verify on your server](/au/guides/verify-on-your-server)

Confirm the approval on your server before you bind.

#### [Test and go live](/au/guides/test-and-go-live)

Trigger a test review, then switch to live keys.