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

# Bring Your Own Identity (BYOI)

> Pass your own user identities into a published Thesys agent — each user gets isolated conversation history without needing a Thesys login.

## Overview

BYOI lets you identify **your** users inside a Thesys-published agent. Instead of requiring end-users to create a Thesys account, you sign a lightweight JWT on your backend and pass it to the embedded agent. Thesys verifies the signature and scopes every conversation to the user ID you provide.

```mermaid theme={null}
sequenceDiagram
    participant User
    participant YourBackend as Your Backend
    participant Agent as Thesys Agent

    User->>YourBackend: Logs in to your app
    YourBackend->>YourBackend: Signs JWT with Identity Secret
    YourBackend->>Agent: Passes JWT (via widget or iframe)
    Agent->>Agent: Verifies JWT signature
    Agent->>Agent: Scopes conversations to externalUserId
    Agent-->>User: Personalized, isolated chat history
```

**What you get:**

* Per-user conversation history — no Thesys login required
* Full control over user identification
* Works with both the embed widget and direct iframe embedding
* Automatic token refresh when JWTs expire mid-session

***

## Quick Setup

<Steps>
  <Step title="Generate your Identity Secret">
    Open your agent in [Agent Builder](https://console.thesys.dev), click **Share** to open the publish side panel, and find the **Bring Your Own Identity** section.

    Click **Generate Secret** and copy the value. Store it securely on your backend — treat it like a password.

    <Warning>
      Never expose the Identity Secret in frontend code. It must only live on your server.
    </Warning>
  </Step>

  <Step title="Sign a JWT on your backend">
    Create an **HS256-signed JWT** containing your user's ID. The token must include:

    | Claim            | Type     | Required    | Description                         |
    | ---------------- | -------- | ----------- | ----------------------------------- |
    | `externalUserId` | `string` | **Yes**     | Your app's unique user identifier   |
    | `exp`            | `number` | Recommended | Expiration timestamp (Unix seconds) |

    <CodeGroup>
      ```javascript Node.js theme={null}
      const jwt = require('jsonwebtoken');

      function generateIdentityToken(userId) {
        return jwt.sign(
          { externalUserId: userId },
          process.env.THESYS_IDENTITY_SECRET,
          { algorithm: 'HS256', expiresIn: '1h' }
        );
      }

      // Express endpoint
      app.get('/api/thesys-token', (req, res) => {
        const token = generateIdentityToken(req.user.id);
        res.json({ token });
      });
      ```

      ```python Python theme={null}
      import jwt, time, os

      def generate_identity_token(user_id: str) -> str:
          return jwt.encode(
              {
                  "externalUserId": user_id,
                  "exp": int(time.time()) + 3600,  # 1 hour
              },
              os.environ["THESYS_IDENTITY_SECRET"],
              algorithm="HS256",
          )

      # Flask / FastAPI endpoint
      @app.get("/api/thesys-token")
      def get_token():
          token = generate_identity_token(current_user.id)
          return {"token": token}
      ```
    </CodeGroup>

    <Tip>
      Set a reasonable expiration (e.g. 1 hour). If you configure automatic token refresh (see below), expired tokens are silently renewed without interrupting the user.
    </Tip>
  </Step>

  <Step title="Pass the token to the agent">
    Choose the integration method that fits your setup:

    <Tabs>
      <Tab title="Embed Widget (Recommended)">
        Install the embed widget:

        ```bash theme={null}
        npm install agent-embed-widget
        ```

        Pass `identityToken` for the initial token and `getIdentityToken` for automatic refresh on expiry:

        ```javascript theme={null}
        import { embedWidget } from 'agent-embed-widget';

        const { token } = await fetch('/api/thesys-token')
          .then(r => r.json());

        embedWidget({
          url: 'https://console.thesys.dev/app/YOUR_SLUG',
          identityToken: token,
          getIdentityToken: async () => {
            // Called automatically when the token expires
            const res = await fetch('/api/thesys-token');
            const { token } = await res.json();
            return token;
          },
          hideLogin: true,
        });
        ```

        The widget handles the entire refresh cycle for you — when the JWT expires mid-conversation, it calls your `getIdentityToken` callback, gets a fresh token, and retries the failed request seamlessly.

        The widget supports three display modes: `tray` (default), `full-screen`, and `chatbar`.
      </Tab>

      <Tab title="Direct iframe">
        Append `IDENTITY_TOKEN` and `HIDE_LOGIN` as URL query parameters:

        ```html theme={null}
        <iframe
          id="thesys-agent"
          src="https://console.thesys.dev/app/YOUR_SLUG?IDENTITY_TOKEN=YOUR_JWT&HIDE_LOGIN=true"
          style="width: 100%; height: 600px; border: none;"
        ></iframe>
        ```

        To set the token dynamically:

        ```javascript theme={null}
        const token = await fetchTokenFromYourBackend();
        const iframe = document.getElementById('thesys-agent');
        iframe.src = `https://console.thesys.dev/app/YOUR_SLUG?IDENTITY_TOKEN=${token}&HIDE_LOGIN=true`;
        ```

        **Automatic token refresh for iframes** requires a `postMessage` listener. See the [Token Refresh](#automatic-token-refresh) section below for the full setup.
      </Tab>
    </Tabs>
  </Step>
</Steps>

***

## Automatic Token Refresh

When a JWT expires during an active conversation, the Thesys agent automatically requests a fresh token from the parent window using `postMessage`. If a new token is provided, the failed request is retried seamlessly — the user never sees an error.

### How it works

```mermaid theme={null}
sequenceDiagram
    participant Agent as Thesys Agent (iframe)
    participant Parent as Parent Window
    participant Backend as Your Backend

    Agent->>Agent: API call fails — token expired
    Agent->>Parent: postMessage: REFRESH_NEEDED
    Parent->>Backend: Fetch new token
    Backend-->>Parent: Fresh JWT
    Parent->>Agent: postMessage: REFRESHED {token}
    Agent->>Agent: Updates token & retries request
    Note over Agent: Conversation continues seamlessly
```

<Note>
  If the parent doesn't respond within 10 seconds (e.g. no listener configured), the agent falls back to showing an error modal asking the user to refresh the page.
</Note>

### Embed Widget

If you're using the embed widget, token refresh is handled automatically — just provide the `getIdentityToken` callback as shown in [Step 3](#quick-setup) above. No additional setup needed.

### Direct iframe

For direct iframe embeds, add a `message` event listener on your parent page:

```javascript theme={null}
const iframe = document.getElementById('thesys-agent');

window.addEventListener('message', async (event) => {
  if (event.data?.type === 'THESYS_IDENTITY_TOKEN_REFRESH_NEEDED') {
    try {
      const res = await fetch('/api/thesys-token');
      const { token } = await res.json();

      iframe.contentWindow.postMessage({
        type: 'THESYS_IDENTITY_TOKEN_REFRESHED',
        identityToken: token,
      }, '*');
    } catch (error) {
      console.error('Failed to refresh identity token:', error);
    }
  }
});
```

### PostMessage Protocol

| Direction       | Message Type                           | Payload                     |
| --------------- | -------------------------------------- | --------------------------- |
| iframe → parent | `THESYS_IDENTITY_TOKEN_REFRESH_NEEDED` | *(none)*                    |
| parent → iframe | `THESYS_IDENTITY_TOKEN_REFRESHED`      | `{ identityToken: string }` |

***

## How It Works Under the Hood

* Thesys verifies the JWT signature using your Identity Secret (HS256)
* The `externalUserId` claim is extracted and used to scope conversations
* Each unique `externalUserId` gets its own isolated conversation history
* If no identity token is provided, users are anonymous (session-based)
* If a user is logged into Thesys **and** has an identity token, the identity token takes priority

***

## Secret Rotation

If you need to rotate your Identity Secret:

1. Click **Rotate Secret** in the BYOI section of the publish panel
2. Copy the new secret to your backend environment
3. Redeploy your backend

<Warning>
  Rotating the secret invalidates **all** previously signed tokens immediately. Users with old tokens will see an "Authentication Failed" error until they get a new token (via refresh or page reload).
</Warning>

***

## Token Errors

| Error                       | Cause                                         | Resolution                                                                                     |
| --------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| **Session Expired**         | JWT `exp` has passed                          | Handled automatically if token refresh is configured; otherwise the user must refresh the page |
| **Authentication Failed**   | Signature doesn't match (secret was rotated?) | Ensure your backend uses the current Identity Secret                                           |
| **Invalid Identity Token**  | Missing `externalUserId` claim                | Add the required claim to your JWT payload                                                     |
| **Identity Not Configured** | No secret has been generated                  | Generate a secret in the publish panel                                                         |

***

## FAQ

<AccordionGroup>
  <Accordion title="Do I need to re-publish my agent after generating or rotating the secret?">
    No. The secret is synced to all published versions automatically.
  </Accordion>

  <Accordion title="Can I use the same secret across multiple agents?">
    No. Each agent has its own independent Identity Secret.
  </Accordion>

  <Accordion title="What JWT algorithm is supported?">
    Only **HS256** (HMAC-SHA256). Other algorithms will be rejected.
  </Accordion>

  <Accordion title="Is there a token size limit?">
    No hard limit, but keep payloads small. Only `externalUserId` and `exp` are read by Thesys.
  </Accordion>

  <Accordion title="Can I add custom claims to the JWT?">
    Yes, but Thesys only reads `externalUserId` and `exp`. Extra claims are ignored.
  </Accordion>

  <Accordion title="What happens if I don't set up token refresh?">
    When the token expires mid-session, the agent shows an error modal. The user must refresh the page to continue. Configuring automatic refresh avoids this interruption entirely.
  </Accordion>

  <Accordion title="Does BYOI work with the chatbar widget type?">
    Yes. BYOI works with all three embed widget display modes: `tray`, `full-screen`, and `chatbar`.
  </Accordion>
</AccordionGroup>
