> For the complete documentation index, see [llms.txt](https://docs.limio.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limio.com/ai/chat-embed/identity.md).

# Identifying visitors

Without integration, every visitor chats as an anonymous guest. If your site has its own login, you can assert the visitor's identity to Limio. Identified visitors get the same conversation history on every device they sign in from, and your Limio data ties chat sessions to a stable user ID instead of a random guest.

The mechanism: your **backend** signs a short-lived JWT with a secret you share with Limio, and your page hands that JWT to the snippet. The browser never sees the secret, only the signed token.

## 1. Get your identity secret

Ask your Limio contact to generate an embed identity secret for your tenant. Store it in your backend's secret storage (an environment variable is fine). Never put it in frontend code or the page: anyone holding the secret can impersonate any of your users to the chat agent.

## 2. Sign assertions on your backend

The assertion is an HS256 JWT with three claims:

| Claim | Required | Value                                                                                                                              |
| ----- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `sub` | yes      | Your stable ID for the visitor (up to 128 characters)                                                                              |
| `iat` | yes      | Issued-at, now                                                                                                                     |
| `exp` | yes      | Expiry. Keep it short; 15 minutes is plenty. The assertion is a one-shot credential exchanged for a chat session; it isn't reused. |
| `aud` | no       | `limio-chat:<tenant>`, if your Limio environment is configured to require an audience                                              |

{% tabs %}
{% tab title="Node" %}

```javascript
import { SignJWT } from "jose"

const secret = new TextEncoder().encode(
  process.env.LIMIO_CHAT_IDENTITY_SECRET
)

export async function signChatAssertion(userId) {
  return new SignJWT({ sub: userId })
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime("15m")
    .sign(secret)
}
```

{% endtab %}

{% tab title="Python" %}

```python
import os
import time
import jwt  # PyJWT

def sign_chat_assertion(user_id: str) -> str:
    now = int(time.time())
    return jwt.encode(
        {"sub": user_id, "iat": now, "exp": now + 900},
        os.environ["LIMIO_CHAT_IDENTITY_SECRET"],
        algorithm="HS256",
    )
```

{% endtab %}

{% tab title="PHP" %}

```php
use Firebase\JWT\JWT;

function signChatAssertion(string $userId): string
{
    $now = time();
    return JWT::encode(
        ['sub' => $userId, 'iat' => $now, 'exp' => $now + 900],
        getenv('LIMIO_CHAT_IDENTITY_SECRET'),
        'HS256'
    );
}
```

{% endtab %}
{% endtabs %}

## 3. Hand the assertion to the snippet

Set `window.LimioChatConfig` **before** the snippet's script tag. Two options, which you can combine:

```html
<script>
  window.LimioChatConfig = {
    // Option A: your server renders the signed JWT into the page.
    identityAssertion: "<jwt rendered by your backend>",

    // Option B: the snippet fetches a fresh assertion when it needs one,
    // at first load if no static assertion is set, and again if the chat
    // session expires mid-visit. Point it at a small endpoint on your site
    // that returns a JWT for the logged-in user.
    getIdentityAssertion: async () => {
      const res = await fetch("/api/limio-chat-identity", {
        credentials: "include"
      })
      if (!res.ok) return null // visitor not logged in, chat as guest
      const { jwt } = await res.json()
      return jwt
    }
  }
</script>
<script src="https://<your-limio-domain>/__core/embed/bootstrap.js" defer></script>
```

The static assertion is used first when both are set. If neither produces a JWT, or your endpoint errors, the visitor chats as a guest; identity is never a hard requirement unless your Limio environment is configured to require it.

## Sign-in and sign-out without a page reload

On a single-page app, call the [JavaScript API](/ai/chat-embed/api-reference.md) when the visitor's login state changes:

```javascript
await LimioChat.identify(jwt) // after sign-in: switch to an identified session
await LimioChat.logout()      // after sign-out: switch to a fresh guest session
```

Both start a fresh conversation under the new identity. A guest conversation isn't carried over when the visitor signs in.

## How sessions behave

Exchanging an assertion gives the browser a chat session token, kept in your site's localStorage for 24 hours. The stored session is keyed to the asserted user: if a different user signs in (or the visitor signs out), the embed detects the mismatch on the next page load and starts the correct session automatically. If the session expires mid-visit, the embed calls your `getIdentityAssertion` endpoint once to renew it.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.limio.com/ai/chat-embed/identity.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
