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

# Trusted Accounts quickstart

> Turn on the offer on a Human Gate, add the widget after your sign-in, and verify the trusted result on your server.

This guide takes you from an existing Human Gate to a secured account that your server has verified. Work in **Development** first, with a `pk_test_...` site key.

## Before you start

Trusted Accounts is part of a paid plan. See [pricing](https://botshield.ai/pricing).

You need:

* A site key whose allowed origins include your page. See [Keys and environments](/console/keys-and-environments).
* An active **Human Gate**. See [Place a gate](/gate/place-a-gate). An Age Gate cannot offer Trusted Accounts.
* A signed-in user on your side, and a **stable ID** for that user's account.

<Warning>
  `platform-user-ref` must be an ID that never changes for the account, such as your internal user ID. It must not be an email address. BotShield refuses a value that contains `@` with `notarize_ref_must_be_stable`, because an email address can change and it identifies a person. BotShield stores the value only as a one-way hash.
</Warning>

## Steps

<Steps>
  <Step title="Turn on the offer">
    In the [BotShield Console](https://console.botshield.ai), open **BotShield Gate**, select your Human Gate, and find **Notarize account with BotShield** in the **Configuration** card. Turn it on.

    For a **Production** gate, the Console asks you to confirm with your Console passkey before the switch changes.

    In the gate list, a gate with the offer on shows **Notarize · On**.

    The switch is the authority. The offer appears only while it is on. Your page markup cannot turn the offer on.
  </Step>

  <Step title="Add the widget after your sign-in">
    Show the offer only to a user you have just signed in or re-authenticated. The binding says that the human confirming is the one using this account, so your own sign-in has to come first.

    ```html theme={null}
    <script src="https://cdn.botshield.ai/sdk.js"></script>

    <botshield-verify
      site-key="pk_test_..."
      scope="account-security"
      scan-mode="modal"
      platform-user-ref="usr_8f31c2d9"
      account-hint="hana@example.com"
      checkout="false"
    ></botshield-verify>
    ```

    | Attribute | Value |
    | - | - |
    | `site-key` | Your public site key. |
    | `scope` | The gate **Key** from the Console, case-sensitive. |
    | `platform-user-ref` | A stable ID for the account on your platform, used to recognise a Trusted Account. Never an email address. |
    | `account-hint` | Optional. The account the person is signed in to, for example their email address. The widget shows it masked, as `h•••@e•••.com`, and never sends it to BotShield. |
    | `checkout` | `false` hides the action button the widget draws under the card. |
    | `notarize` | Optional. Leave it out: the gate's switch turns the offer on. Set `notarize="false"` to hide the offer on one placement. |
  </Step>

  <Step title="The person secures the account">
    The widget shows a card titled **Secure your account with BotShield**, with a **Link BotShield ID** button.

    | Device | What happens on tap |
    | - | - |
    | Phone | BotShield opens and asks the person to confirm. |
    | Desktop | BotShield opens in a new tab. The person can choose **Use your phone instead** to see a QR code and a 6-character code. |

    The code lasts 5 minutes and works once. The person can scan the QR code, or type the code at `app.botshield.ai/link`.

    In BotShield, the person sees **Secure your account?** with your platform's name, and confirms with a passkey. The card on your page then reads **Your account is secured**.
  </Step>

  <Step title="Handle the result on your page">
    Listen for `botshield:success` and send the token to your server. `trusted` is `true` when the account is secured.

    ```html theme={null}
    <script>
      const widget = document.querySelector('botshield-verify');

      widget.addEventListener('botshield:success', async (e) => {
        const { token, request_id, trusted } = e.detail;
        await fetch('/api/botshield/secured', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ token, request_id }),
        });
      });

      widget.addEventListener('botshield:failure', (e) => {
        console.warn('BotShield:', e.detail.reason);
      });
    </script>
    ```

    Treat `trusted` in the browser as a hint. The decision belongs on your server.
  </Step>

  <Step title="Verify on your server">
    Send the token to `POST /sdk/verify-token`. The call needs no API key. The token lives 120 seconds.

    ```bash theme={null}
    curl -s https://api.botshield.ai/operations/sdk/verify-token \
      -H "Content-Type: application/json" \
      -d '{"token":"eyJhbGciOiJFUzI1NiIsImtpZCI6..."}'
    ```

    ```json theme={null}
    {
      "data": {
        "data": {
          "valid": true,
          "claims": {
            "request_id": "req_4b1f0c6e2a9d4e7f8a3b5c6d7e8f9a0b",
            "verified": true,
            "organization_id": "org_...",
            "timestamp": "2026-10-20T17:04:05.000Z",
            "trusted": true,
            "trusted_since": "2026-10-20T17:04:05.000Z",
            "last_pass_at": "2026-10-20T17:04:05.000Z"
          }
        }
      }
    }
    ```

    Check all of these before you mark the account as trusted:

    1. `data.error` is absent and `valid` is `true`.
    2. `organization_id` is your organization.
    3. `request_id` is the one your page sent.
    4. `trusted` is `true`.

    Then store `trusted_since` against **your** user record. See [Token and webhooks](/trusted-accounts/token-and-webhooks) for local verification and for the events your endpoint receives.
  </Step>

  <Step title="See it in the Console">
    Open **Trusted Accounts** in the Console. The **Registry** tab lists the account by its opaque handle, with the date it became trusted and the gate it came through.

    The **Try it** tab runs the same widget against your Development key, with you as the customer. Use it to see each card before you ship.
  </Step>
</Steps>

## Server-to-server

If you create verification requests from your server instead of using the widget, pass `partner_user_ref` to `POST /sdk/create-verification-link`. The same rules apply: a Human Gate with the switch on, and a stable ID that is not an email address. `notarize: false` skips the offer for one request. `notarize: true` cannot turn the offer on while the gate's switch is off. The request is then an ordinary verification.

## Test the other paths

| To see | Do this |
| - | - |
| No offer for a secured account | Load the page again as the same user. The widget shows the normal verify button. |
| `already_trusted` | With the same BotShield ID, try to secure a second account that has a different `platform-user-ref`. |
| `notarize_not_enabled` | Turn the gate's switch off and add `notarize` to the element. The widget shows the normal verify button. |
| The offer again | Unlink the account in the BotShield app, or select **Revoke** in the Registry, then reload the page. |
| A declined request | Select **Cancel** in BotShield when it asks. Your page receives `botshield:failure` with `reason: "declined"`. |
