> ## Documentation Index
> Fetch the complete documentation index at: https://developer.watson-orchestrate.ibm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Context variables

Use context variables to pass additional information from your web application into an agent run through the embedded chat. This page covers the two delivery methods available for the embedded chat: JWT token and instance methods.

For a full explanation of context variables, including types, naming rules, precedence, scope, persistence strategies, tool support, and channel limitations, see [Context variables](/conversation/context_variables).

The following methods are available for passing context variables to agents from the embedded web chat:

1. **JWT token** — Context embedded in the signed token (requires secured flow). Best for stable, identity-related values.
2. **Instance methods (`pre:send` event)** — Context added dynamically per message (works with or without token signing). Best for values that change during the session.

<Note>
  ##### System and custom variables

  Context variables are either system variables or custom variables. System variables use the reserved `wxo_` prefix and are populated automatically by the platform on every run. Custom variables are defined and supplied by your application. For details, see [Types of context variables](/conversation/context_variables#types-of-context-variables).

  ##### Naming convention for context variables

  Follow the standard naming rules for custom context variables. For details, see [Naming rules](../conversation/context_variables#naming-rules).

  ##### Scope and persistence

  Context is scoped to a single run and is not persisted across turns in a thread. For details and persistence strategies, see [Persisting context across turns](../conversation/context_variables#persisting-context-across-turns).

  ##### Channel support

  Channels might not support context-variable propagation. For details, see [Channel support](../conversation/context_variables#channel-support).
</Note>

## Setup

To use context variables:

1. Enable `context_access_enabled: true` in your agent definition.
2. Add variables to the `context_variables` list in your agent [definition file](../agents/build_agent).
3. Reference these variables in your agent `instructions` by using curly brace syntax: `{variable_name}`.
4. Reimport the agent.
5. Pass context variables by using one of the two following methods.

### Agent Definition Example

```yaml theme={null}
spec_version: v1
style: react_core
name: askHR_agent
llm: watsonx/meta-llama/llama-3-2-90b-vision-instruct
description: You are a helpful agent that answers HR-related queries
instructions: >-
  You have access to the following context values:
     `clientID: {clientID}`
     `name: {name}`
     `role: {role}`
     `user_name: {user_name}`
     `email_id: {email_id}`
  Pass these values exactly as provided.
  Do not ask the user for these values — they are always available in the context.
collaborators: []
tools: []
context_access_enabled: true
context_variables:
  - clientID
  - name
  - role
  - user_name
  - email_id
```

**Key points:**

* List all context variables in the `context_variables` array
* Reference them in `instructions` by using `{variable_name}` syntax
* Instruct the agent not to ask users for these values since they're provided automatically
* The agent can use these values when it calls tools or responds to users

<Note>
  **Important naming conventions:**

  * Do NOT use the `wxo_` prefix in your custom context variable names. This prefix is reserved for system variables.
  * If the same context variable name exists in both the JWT and the instance method (pre:send), the **JWT value takes precedence** over the `/runs` API value.
  * To avoid conflicts, use different variable names for JWT context and dynamic context that is passed through instance methods.
</Note>

## Method 1: Context Variables - JWT Token

This method embeds context variables directly in the JWT token. It is suitable for **stable, identity-related context** that doesn't change frequently, such as user ID, role, or organization ID.

### When to use JWT context:

* Fixed user identity information (user ID, role, department)
* Organization or tenant identifiers
* Permissions or access levels
* Any context that remains constant throughout the session

You can add context variables to a JWT token by using a JavaScript script. The following script shows how to include context variables inside a JWT token in your server:

```javascript createJWT [expandable] theme={null}
/**
 * JWT Creation Service for watsonx Orchestrate Secure Embed Chat
 *
 * This module handles the server-side creation of JSON Web Tokens (JWTs) for secure
 * authentication with IBM watsonx Orchestrate embed chat. It demonstrates:
 *
 * 1. RS256 JWT signing using your private key
 * 2. Optional user payload encryption using IBM's public key
 * 3. Cookie-based anonymous user tracking
 * 4. Session-based user information enrichment
 *
 * Security Notes:
 * - Never expose your private key to the client side
 * - Always generate JWTs server-side only
 * - Use HTTPS in production environments
 * - Adjust token expiration times based on your security requirements
 */

const fs = require("fs");
const path = require("path");
const express = require("express");
const { v4: uuid } = require("uuid");
const jwtLib = require("jsonwebtoken");
const NodeRSA = require("node-rsa");

const router = express.Router();

/**
 * Load your server-side RSA private key (PEM format)
 *
 * This key is used to sign JWTs with the RS256 algorithm. The corresponding public key
 * must be uploaded to your watsonx Orchestrate instance security settings.
 *
 * IMPORTANT: Keep this file secure and never expose it to clients!
 *
 * Generate a new key pair using either method:
 *
 * Method 1 - Using ssh-keygen:
 *   ssh-keygen -t rsa -b 4096 -m PEM -f example-jwtRS256.key
 *   openssl rsa -in example-jwtRS256.key -pubout -outform PEM -out example-jwtRS256.key.pub
 *
 * Method 2 - Using openssl directly:
 *   openssl genrsa -out example-jwtRS256.key 4096
 *   openssl rsa -in example-jwtRS256.key -pubout -out example-jwtRS256.key.pub
 */
const PRIVATE_KEY_PATH = path.join(__dirname, "../keys/example-jwtRS256.key");
if (!fs.existsSync(PRIVATE_KEY_PATH)) {
  throw new Error(`Private key not found at ${PRIVATE_KEY_PATH}`);
}
const PRIVATE_KEY = fs.readFileSync(PRIVATE_KEY_PATH);

/**
 * Load IBM's RSA public key (PEM format)
 *
 * This key is provided by IBM watsonx Orchestrate and is used to encrypt the user_payload
 * field in the JWT. This ensures that sensitive user information cannot be read by clients,
 * as only IBM's servers can decrypt it.
 *
 * You can obtain this key by:
 * 1. Running the wxo-embed-security-v4.sh script (recommended)
 * 2. Calling the generate-key-pair API endpoint manually
 *
 * The encrypted payload will be decrypted server-side by watsonx Orchestrate.
 */

const IBM_PUBLIC_KEY_PATH = path.join(__dirname, "../keys/ibmPublic.key.pub");
if (!fs.existsSync(IBM_PUBLIC_KEY_PATH)) {
  throw new Error(`IBM public key not found at ${IBM_PUBLIC_KEY_PATH}`);
}
const IBM_PUBLIC_KEY = fs.readFileSync(IBM_PUBLIC_KEY_PATH);

/**
 * Cookie lifetime configuration
 *
 * This defines how long the anonymous user ID cookie will persist.
 * Currently set to 60ms for demonstration purposes. For production, use a longer duration
 * such as 45 days (45 * 24 * 60 * 60 * 1000 milliseconds).
 */
const TIME_15_MINUTES = 15 * 60 * 1000; // 15 minutes for demonstration. Adjust based on your security requirements.


/**
 * Create a signed JWT string for the Orchestrate embedded chat client.
 *
 * This function constructs a JWT with the following structure:
 *
 * @param {string} anonymousUserID - A stable identifier for anonymous users (from cookie)
 * @param {object|null} sessionInfo - Optional authenticated user session data
 *
 * JWT Claims:
 * - sub: Subject (user identifier) - should be a stable, unique ID for the user
 * - user_payload: Encrypted user data that will be decrypted by watsonx Orchestrate
 *   - name: User's display name
 *   - custom_message: Any custom message or metadata
 *   - custom_user_id: Your application's internal user ID
 *   - sso_token: Single sign-on token if applicable
 * - context: Additional context data accessible by the agent
 *   - clientID: Your client/organization identifier
 *   - user_name: User's name for display in chat
 *   - user_role: User's role (e.g., Admin, User, Guest)
 *
 * @returns {string} A signed JWT token string
 */
function createJWTString(anonymousUserID, sessionInfo) {
  // Base JWT claims structure
  // Customize these fields based on your application's requirements
  const jwtContent = {
    // Subject: Unique identifier for the user
    // In production, use a real user ID from your authentication system
    sub: "FHY1234DD5", // Example value. Replace with actual user ID.

    // User payload: It will be encrypted with IBM's public key
    // This data is sensitive and will only be readable by watsonx Orchestrate servers
    // Optional
    user_payload: {
      sso_token: "sso_token",
    },

    // Context: Additional metadata accessible by the agent
    // This data is NOT encrypted and can be read by the client
    context: {
      clientID: "865511",          // Your client/organization ID
      user_name: "Ava",            // Display name in chat
      user_role: "Admin",          // User role
    },
  };

  // Enrich the JWT with authenticated session data if available
  // In production, this would come from your authentication system
  if (sessionInfo) {
    jwtContent.user_payload.name = sessionInfo.userName;
    jwtContent.user_payload.custom_user_id = sessionInfo.customUserID;
  }

  // Encrypt the user_payload using IBM's RSA public key
  // This ensures sensitive user data cannot be read by clients
  // Only watsonx Orchestrate servers can decrypt this data
  if (jwtContent.user_payload) {
    const rsaKey = new NodeRSA(IBM_PUBLIC_KEY);
    const dataString = JSON.stringify(jwtContent.user_payload);
    const utf8Data = Buffer.from(dataString, "utf-8");
    // Encrypt and encode as base64 string
    jwtContent.user_payload = rsaKey.encrypt(utf8Data, "base64");
  }

  // Sign the JWT using RS256 algorithm with your private key
  // The token expiration should be set based on your security requirements
  // Common values: "1h" (1 hour), "6h" (6 hours), "1d" (1 day)
  const jwtString = jwtLib.sign(jwtContent, PRIVATE_KEY, {
    algorithm: "RS256",
    expiresIn: "15m", // 15 minutes for demonstration. Adjust based on your security requirements.
  });

  return jwtString;
}

/**
 * Retrieve or create a stable anonymous user ID stored in a cookie
 *
 * This function ensures that anonymous users maintain a consistent identity across
 * page refreshes and sessions. The ID is stored in a secure HTTP-only cookie.
 *
 * Benefits:
 * - Prevents user identity from changing mid-session
 * - Enables conversation continuity for anonymous users
 * - Provides basic user tracking without requiring authentication
 *
 * @param {object} request - Express request object
 * @param {object} response - Express response object
 * @returns {string} The anonymous user ID
 */
function getOrSetAnonymousID(request, response) {
  // Check if an anonymous ID already exists in cookies
  let anonymousID = request.cookies["ANONYMOUS-USER-ID"];

  // Generate a new ID if none exists
  if (!anonymousID) {
    // Create a short, readable ID using UUID (first 5 characters for demonstration)
    // In production, you might want to use the full UUID for better uniqueness
    anonymousID = `anon-${uuid().slice(0, 5)}`;
  }

  // Set or refresh the cookie with each request to maintain the session
  response.cookie("ANONYMOUS-USER-ID", anonymousID, {
    expires: new Date(Date.now() + TIME_45_DAYS),
    httpOnly: true,  // Prevents client-side JavaScript from accessing the cookie (security)
    sameSite: "Lax", // Provides CSRF protection while allowing normal navigation
    secure: false,   // Set to true in production when using HTTPS
  });

  return anonymousID;
}

/**
 * Parse authenticated session information from cookies
 *
 * This function retrieves user session data if the user is authenticated.
 * In a production application, you would:
 * 1. Verify the session token/cookie
 * 2. Fetch user information from your database or identity provider
 * 3. Validate user permissions
 *
 * For this demonstration, we simply parse a JSON string from a cookie.
 *
 * @param {object} request - Express request object
 * @returns {object|null} Session info object or null if not authenticated
 */
function getSessionInfo(request) {
  const sessionInfo = request.cookies?.SESSION_INFO;
  if (!sessionInfo) return null;

  try {
    // Parse the JSON session data
    return JSON.parse(sessionInfo);
  } catch {
    // Return null if parsing fails (invalid JSON)
    return null;
  }
}

/**
 * Express route handler for JWT creation
 *
 * This endpoint is called by the client to obtain a fresh JWT token.
 * The token is required for secure authentication with watsonx Orchestrate embedded chat.
 *
 * Flow:
 * 1. Retrieve or create an anonymous user ID (stored in cookie)
 * 2. Check for authenticated session information
 * 3. Generate a signed JWT with user data
 * 4. Return the JWT as plain text response
 *
 * The client will include this JWT in the wxOConfiguration.token field
 * when initializing the embedded chat.
 *
 * @param {object} request - Express request object
 * @param {object} response - Express response object
 */
function createJWT(request, response) {
  // Ensure we have a stable user ID (anonymous or authenticated)
  const anonymousUserID = getOrSetAnonymousID(request, response);

  // Get authenticated session data if available
  const sessionInfo = getSessionInfo(request);

  // Create and sign the JWT
  const token = createJWTString(anonymousUserID, sessionInfo);

  // Return the JWT as plain text
  response.send(token);
}

// Define the GET endpoint that returns a signed JWT string
// This endpoint is called by the client before initializing the chat
router.get("/", createJWT);

module.exports = router;
```

### Using the JWT token

After the JWT token generates, pass it to the embedded chat. The following example shows how to do that:

You can check the [examples for watsonx Assistant web chat](https://github.com/watson-developer-cloud/assistant-toolkit/tree/master/integrations/webchat/examples/web-chat-security-complex) that are mostly compatible with the watsonx Orchestrate embedded chat.

```javascript JavaScript [expandable] theme={null}
<script>
    function getUserId() {
        let embed_user_id = getCookie('embed_user_id');
        if (!embed_user_id) {
            embed_user_id = Math.trunc(Math.random() * 1000000);
            setCookie('embed_user_id', embed_user_id);
        }
        return embed_user_id;
    }

    function getCookie(name) {
        console.log('getCookie');
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
    }

    function setCookie(name, value) {
        document.cookie = `${name}=${value}; path=/`;
    }
    function onChatLoad(instance) {
        // Save instance for later use
        window.wxoChatInstance = instance;
        console.log('Chat loaded with JWT authentication');
    }
    async function getIdentityToken() {
        // This will make a call to your server to request a new JWT.
        const result = await fetch(
            "http://localhost:3000/createJWT?user_id=" + getUserId()
        );
        window.wxOConfiguration.token = await result.text();
    }

    window.wxOConfiguration = {
        orchestrationID: "your-orchestration-id",
        hostURL: "https://us-south.watson-orchestrate.cloud.ibm.com",
        rootElementID: "root",
        deploymentPlatform: "ibmcloud",
        crn: "your-crn",
        chatOptions: {
            agentId: "your-agent-id",
            agentEnvironmentId: "your-agent-environment-id",
            onLoad: onChatLoad
        },
    };
    getIdentityToken().then(() => {
        const script = document.createElement("script");
        script.src = `${window.wxOConfiguration.hostURL}/wxochat/wxoLoader.js?embed=true`;
        script.addEventListener("load", function () {
            wxoLoader.init();
        });
        document.head.appendChild(script);
    });
</script>
```

## Method 2: Context Variables - Instance Methods (pre:send event)

This method dynamically injects context variables into each message by using the `pre:send` event handler. It's ideal for **dynamic context** that changes frequently or varies per message, such as current page, selected profile, or user input.

### When to use instance method context:

* Dynamic values that change during the session (current page, selected item)
* User-specific data that varies per interaction (form inputs, selections)
* Profile or when switching persona within the same session
* Any context that needs to be updated without page reload

### Simple Example: Basic context injection

The following example shows the simplest way to inject context variables into messages:

```html theme={null}
<html>
<body>
  <div id="root"></div>
  
  <script>
    // Handler to inject context into outgoing messages
    function preSendHandler(event, instance) {
      event.message.context = {
        ...event.message.context,
        clientID: "12345",
        user_name: "John Doe",
        user_role: "Employee"
      };
      console.log('Context injected:', event.message.context);
    }

    // Called when chat loads
    function onChatLoad(instance) {
      // Register the pre:send event handler
      instance.on('pre:send', preSendHandler);
    }

    // Configure the chat
    window.wxOConfiguration = {
      orchestrationID: "your-orchestration-id",
      hostURL: "https://us-south.watson-orchestrate.cloud.ibm.com",
      rootElementID: "root",
      deploymentPlatform: "ibmcloud",
      crn: "your-crn",
      chatOptions: {
        agentId: "your-agent-id",
        onLoad: onChatLoad
      }
    };

    // Load the chat script
    const script = document.createElement('script');
    script.src = `${window.wxOConfiguration.hostURL}/wxochat/wxoLoader.js?embed=true`;
    script.addEventListener('load', function () {
      wxoLoader.init();
    });
    document.head.appendChild(script);
  </script>
</body>
</html>
```

<Note>
  **Event choice:** Use `pre:send` instead of `send` for context injection. The `pre:send` event fires before the message is sent, helping ensure that context is included in the request. The `send` event fires after the message is already sent.
</Note>

## Comparison: JWT vs Instance Method

| Aspect               | JWT Token                                               | Instance Method (pre:send)                      |
| -------------------- | ------------------------------------------------------- | ----------------------------------------------- |
| **Security**         | Requires a signed token                                 | Works with or without token                     |
| **Use case**         | Stable, identity-related context                        | Dynamic, frequently changing context            |
| **Update frequency** | Fixed until the token expires                           | Can change per message                          |
| **Examples**         | User ID, role, organization                             | Current page, form inputs, selections           |
| **Precedence**       | **Higher** - JWT values override instance method values | Lower - overridden by JWT if same variable name |
| **Best for**         | Authentication, permissions, fixed identity             | User interactions, session state, dynamic data  |

## Implementation guidelines

1. **Use distinct variable names** for JWT-based context and instance method context to prevent naming conflicts.
2. **Avoid the `wxo_` prefix** in custom context variable names, as it is reserved for system-defined variables.
3. **Use JWT-based context** for stable data such as user identity, role, and organization.
4. **Use instance methods** for dynamic data such as the current page, selections, or form input.
5. **Keep context minimal** by passing only the data required for correct processing.
6. **Document all context variables** in the agent definition to support long-term maintainability.

### JWT size and header limits

When you use SSO/OBO connections with remote MCP toolkits, the platform propagates the user's access token in a request header during tool execution. watsonx Orchestrate enforces an **8 KB per-header size limit** as part of RFC 6585-compliant header validation.

If the JWT contains many context variables, the resulting access token can exceed this limit, causing MCP tool calls to fail silently with an HTTP 500 error (underlying cause: HTTP 431 Request Header Fields Too Large).

To avoid this, keep your JWT payload small. Include only the context variables the agent actually needs and remove internal system values, verbose metadata, or redundant identity provider fields.

For troubleshooting steps, see [MCP tool execution fails with HTTP 431](/release/troubleshooting#mcp-tool-execution-fails-with-http-431-request-header-fields-too-large).

## Example: Combined approach

```javascript theme={null}
// JWT payload (stable context)
{
  "sub": "user-12345",
  "context": {
    "user_id": "12345",           // Stable user identifier
    "user_role": "employee",      // User's role
    "organization_id": "org-789"  // Organization identifier
  }
}

// Instance method (dynamic context)
function preSendHandler(event, instance) {
  event.message.context = {
    ...(event.message.context || {}),
    current_page: window.location.pathname,  // Current page
    selected_product: getSelectedProduct(),  // User selection
    form_data: getCurrentFormData()          // Form inputs
  };
}
```

In this example:

* JWT provides stable identity context (`user_id`, `user_role`, `organization_id`)
* Instance method provides dynamic session context (`current_page`, `selected_product`, `form_data`)
* All variable names are unique to avoid conflicts
