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

# Identity

> Link website chats to signed-in customers and verify their identity.

By default, website visitors chat anonymously. When a customer is signed in to your site, call `identify()` so their chats are linked to their customer profile in Sagepilot and their conversation history follows them across devices.

## Identify a signed-in customer

Call `identify()` after the widget is ready and your site knows who the customer is.

```js theme={null}
await window.sagepilotWidgetReady;

const result = await window.ChatWidget.identify({
  user_id: "user_123",
  email: "jane@example.com",
  name: "Jane Doe",
  phone: "+14155550123",
  custom_properties: {
    plan: "premium",
    loyalty_tier: "gold"
  },
  user_hash: "HASH_FROM_YOUR_SERVER"
});

if (!result.success) {
  console.warn("Sagepilot identify failed:", result.error);
}
```

| Field               | Required                         | Description                                                                                                        |
| ------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| `user_id`           | Yes                              | Your stable, unique ID for the customer. Do not use a value that can change, such as an email address.             |
| `email`             | No                               | Customer email address.                                                                                            |
| `name`              | No                               | Customer display name.                                                                                             |
| `phone`             | No                               | Customer phone number. Include the country code.                                                                   |
| `custom_properties` | No                               | Object of extra customer data. Sagepilot saves it to the customer's custom fields.                                 |
| `user_hash`         | When identity verification is on | HMAC-SHA256 signature of `user_id`, generated on your server. See [Identity verification](#identity-verification). |

Other top-level fields are ignored. Put any extra data inside `custom_properties`.

Sagepilot looks up the customer by `user_id`. If none exists, it looks for a customer with the same `email`. If neither exists, it creates a new customer.

### Result

`identify()` resolves to an object. It does not throw.

| Field      | Description                                                                                                       |
| ---------- | ----------------------------------------------------------------------------------------------------------------- |
| `success`  | `true` if Sagepilot linked the visitor to the customer.                                                           |
| `customer` | On success: the customer's `id`, `name`, `email`, and `external_id`. `external_id` is the `user_id` you passed.   |
| `error`    | On failure: a message explaining why, such as `user_id is required` or `Invalid identity verification signature`. |

### When to call it

* Call it after sign-in, or on page load when a signed-in customer arrives.
* The identity is saved in the browser and restored on later page loads, so you do not need to call it on every page. Calling it again with the same customer is safe.
* If a different customer signs in on the same browser, call `logout()` first and then `identify()` with the new customer.

## Identity verification

Without verification, Sagepilot cannot confirm that a visitor is the customer they claim to be. Turn on identity verification for any site where customers sign in.

When verification is on, every `identify()` call must include a `user_hash`: an HMAC-SHA256 signature of the `user_id`, created with a secret that only your server knows. Calls without a valid hash fail.

<Steps>
  <Step title="Turn on verification">
    In Sagepilot, open **Channels**, click your website widget, and open **Identity**. Turn on **Enable Identity Verification** and save the channel.
  </Step>

  <Step title="Store the secret on your server">
    Copy **Your Secret Key** and store it with your other server-side secrets, for example as an environment variable.
  </Step>

  <Step title="Generate the hash on your server">
    Sign the exact `user_id` string you will pass to `identify()`, and output the result as lowercase hexadecimal.

    <CodeGroup>
      ```js Node.js theme={null}
      const crypto = require("crypto");

      function sagepilotUserHash(userId) {
        return crypto
          .createHmac("sha256", process.env.SAGEPILOT_IDENTITY_SECRET)
          .update(String(userId))
          .digest("hex");
      }
      ```

      ```python Python theme={null}
      import hashlib
      import hmac
      import os

      def sagepilot_user_hash(user_id: str) -> str:
          return hmac.new(
              os.environ["SAGEPILOT_IDENTITY_SECRET"].encode(),
              str(user_id).encode(),
              hashlib.sha256,
          ).hexdigest()
      ```

      ```php PHP theme={null}
      function sagepilot_user_hash(string $userId): string {
          return hash_hmac('sha256', $userId, getenv('SAGEPILOT_IDENTITY_SECRET'));
      }
      ```

      ```ruby Ruby theme={null}
      require "openssl"

      def sagepilot_user_hash(user_id)
        OpenSSL::HMAC.hexdigest("SHA256", ENV.fetch("SAGEPILOT_IDENTITY_SECRET"), user_id.to_s)
      end
      ```
    </CodeGroup>
  </Step>

  <Step title="Pass the hash to the page">
    Render the hash into the page or return it from an authenticated endpoint, then pass it to `identify()` as `user_hash`.
  </Step>
</Steps>

<Warning>
  Never put the secret key in frontend code, theme files, or a tag manager. Anyone who has it can sign in to chat as any of your customers.
</Warning>

If you regenerate the secret in Sagepilot, update your server at the same time. Hashes signed with the old secret stop working once the new secret is saved.

## Log out

When the customer signs out of your site, clear their identity from the widget:

```js theme={null}
await window.sagepilotWidgetReady;
window.ChatWidget.logout();
```

After `logout()`, the chat resets to an anonymous session in this browser, and the customer's conversations are no longer shown.

## Read the identity state

`getIdentityState()` returns the current state synchronously:

```js theme={null}
const { identified, pending, customer } = window.ChatWidget.getIdentityState();
```

| Field        | Description                                                                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `identified` | `true` if the visitor is linked to a customer, including an identity restored from an earlier page load.                                        |
| `pending`    | `true` while an `identify()` call is in progress.                                                                                               |
| `customer`   | The customer object from the last successful `identify()` on this page. It can be `null` after a page reload, even when `identified` is `true`. |

## Run code after identification

`onIdentify(callback)` runs your callback once the visitor is identified:

```js theme={null}
window.ChatWidget.onIdentify(function (result) {
  console.log("Identified as", result.customer && result.customer.id);
});
```

* If the visitor is already identified, the callback runs immediately.
* If an `identify()` call is in progress, the callback runs when it succeeds.
* If neither is true, the callback is not registered. Call `onIdentify()` after you call `identify()`.

## Identity and messages

Calling `identify()` or `logout()` cancels any `setDraft()` or `sendMessage()` call that has not finished yet. Those calls resolve with the code `identity_changed`. Calls made while `identify()` is in progress return `identity_pending`. Wait for `identify()` to finish before you send a message on the customer's behalf. See [Draft and send messages](/sdks/web/sending-messages).
