> This page is for version 🇬🇧 UK.
> 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.

# protect() options

> Reference for the HumanApproved embed's protect() options, including quoteDetails, userId, signal, onResult and timeoutMs.

```js
HumanApproved.q.push(["protect", {
  key: "pk_live_…",
  quote: "HTA-4471920",
  before: "#continue-to-payment",
  quoteDetails: () => ({ quote: { /* your product's fields */ }, mobile: "+44 7700 900482" }),
  userId: "CUST-1001",
  onResult: (result) => console.info("HumanApproved", result.outcome),
  timeoutMs: 2500,
}]);
```

## Options

| Option         | Type     | Required | Default | What it does                                                                                                                                                     |
| -------------- | -------- | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`          | string   | Yes      |         | Your publishable key, `pk_live_…` or `pk_test_…`. It identifies the product and its environment.                                                                 |
| `quote`        | string   | Yes      |         | Your quote reference, 1 to 128 characters. Not personal data.                                                                                                    |
| `before`       | string   | Yes      |         | CSS selector for the step to protect: a button or link (its click) or a form (its submit).                                                                       |
| `quoteDetails` | function | Yes      |         | Returns `{ quote, mobile }`, synchronously or as a promise. Called when a review starts, and on each press after an approval. See [quoteDetails](#quotedetails). |
| `userId`       | string   | No       | None    | Your own ID for this customer. See [userId](#userid).                                                                                                            |
| `signal`       | object   | No       | None    | Your own detection result, for products set to use it. See [signal](#signal).                                                                                    |
| `onResult`     | function | No       | None    | Called once for each outcome. See [Handling the outcome](/uk/embed/sdk/on-result).                                                                                  |
| `timeoutMs`    | number   | No       | `2500`  | How long the step waits for its check before it proceeds. See [timeoutMs](#timeoutms).                                                                           |
| `baseUrl`      | string   | No       | None    | Leave it unset. The embed works out the host from your key.                                                                                                      |

## quoteDetails

```js
quoteDetails: async () => ({
  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: "GBP" },
  },
  mobile: "+44 7700 900482",
  quoteUrl: "https://www.example.com/quote/resume/9f2c…", // optional
}),
```

| Field      | Required | What it is                                                                                                                                                                                                                                                                                            |
| ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `quote`    | Yes      | The quote, shaped by the fields on your product's **Fields** page. Fields the product doesn't define are dropped.                                                                                                                                                                                     |
| `mobile`   | Yes      | The customer's own mobile, where the approval code is sent. Any valid mobile number, in international format, starting with `+` and its country code; a number without a country code is read as one from your region. A landline, or a number that isn't valid, is refused with `422 invalid_quote`. |
| `quoteUrl` | No       | A link where the customer can change the quote themselves. The approval page offers it if something's wrong. It must be `https`, on one of the product's allowed websites, and up to 2,000 characters. Never put personal data in it: use an opaque token your site resolves, with an expiry.         |

The quote must match your product's published fields: a variant the product doesn't list, or an extra it doesn't name, is refused. If the quote is refused, the step proceeds, as for any error, and the browser console lists each field that was refused.

**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, and the step waits up to 2.5 seconds (or `timeoutMs`) for it. If it throws, returns something that isn't `{ quote, mobile }`, or takes too long, the step proceeds and the browser console shows the reason. See [After an approval](/uk/embed/sdk/after-an-approval).

## userId

Your own ID for this customer, such as a customer or account number. It's sent with each check, so you can search a customer's checks and approvals in the portal, and your server gets it back with the review.

* 1 to 128 characters once trimmed, with no control characters. A value that breaks this is left out with a console warning, and the check still runs.
* **Use an opaque ID from your own system**, not an email address, phone number or name. If the value looks like an email address or phone number, it isn't stored.
* It's never kept in the browser's storage.

## signal

Your own detection result, sent instead of the embed's own measures, for products set to use your own signal on the portal's **Agent detection** page. All fields are optional:

```js
signal: { source: "edge", score: 2, verifiedBot: "ChatGPT-User", isAgent: true },
```

| Field         | Type    | What it is                                               |
| ------------- | ------- | -------------------------------------------------------- |
| `source`      | string  | Where the result came from, for your own records.        |
| `score`       | number  | Your detection's score.                                  |
| `verifiedBot` | string  | A verified bot's name, if your detection identified one. |
| `isAgent`     | boolean | Whether your detection decided this is an agent.         |

See [Agent detection](/uk/concepts/agent-detection) for how a product uses it.

## timeoutMs

How long the step waits for its check before it proceeds (fails open).

|                         | Default     | Range            |
| ----------------------- | ----------- | ---------------- |
| Live keys (`pk_live_…`) | 2.5 seconds | 500 to 20,000 ms |
| Test keys (`pk_test_…`) | 2.5 seconds | 500 to 20,000 ms |

A value outside the range is clamped to it.

* The same limit bounds the comparison after an approval.
* A re-check after a fail-open waits 1 second, or `timeoutMs` if that's shorter.
* When it runs out, `onResult` receives `fail_open` with `reason: "timeout"`.
* A longer limit means a customer can wait longer when HumanApproved is slow: at most twice per quote (the limit, then the 1-second re-check).
* For a [monitor-only](/uk/guides/monitor-only) product no press waits, so `timeoutMs` only matters for products that can pause a press.