How to Manage Sessions with JavaScript in Release0 (with Code Snippets)

43 minutes read

Custom session management is now possible in Release0 embedded agents thanks to a powerful JavaScript-based Session Manager. This post walks through the usage of persistent and temporary sessions using localStorage and sessionStorage, and how to enable seamless cross-tab/session tracking — perfect for authentication flows.


🚀 What Is the Session Manager?

The Session Manager is a plug-and-play JavaScript snippet you can embed in your agent's <head> via Advanced Settings > Custom head code.

It manages user data (like name and email) in either:

  • localStorage: for persistent sessions across reloads and tabs (with optional expiry)
  • sessionStorage: for temporary sessions that expire when the tab or browser is closed

This approach avoids the limitations of cookies in iframe environments, which often face cross-origin restrictions.

🔗 Live Demo: Session Manager

Open your browser's DevTools to inspect how session data is stored and cleared.
(Press F12 on Windows/Linux or Cmd + Option + I on macOS)


🧠 Use Cases

✅ Authentication

You can store authenticated user data like email and role after login:


_10
interface SessionData {
_10
name: string;
_10
email: string;
_10
expiry: number;
_10
}
_10
_10
const userData = AgentSessionManager.readSession('persistentUserSession') as SessionData | null;
_10
if (userData) return userData;
_10
return '';

Later, retrieve it:


_10
{{=JSON.parse({{persistentUserData}}).name=}}

🔒 Temp Chat Sessions

Perfect for storing session-specific data, e.g. survey state:


_10
AgentSessionManager.createTempSession('chatFlow', 'Temporary User', 'temp@demo.com');

🔄 Update or Delete


_10
AgentSessionManager.updateSession('userAuth', 'Lisa R.', 'lisa.new@example.com');
_10
AgentSessionManager.deleteSession('userAuth');

📬 Message to Parent Site

If your R0 agent is embedded via iframe, send session data to the parent app:


_10
AgentSessionManager.sendToParent('userAuth', 'SESSION_DATA');


💡 Best Practices

  • Use persistent sessions for login/auth use cases
  • Use temporary sessions for contextual interactions
  • Set a default expiry (DEFAULT_EXPIRY_DAYS) in the script or customize per session
  • Sanitize user input before storing
  • Use getAllSessions() for analytics/debugging
  • Monitor operations using the console for debugging and user flow analysis
  • Implement postMessage communication for iframe-embedded agents
  • If you're using TypeScript, define a proper interface for session data to avoid runtime errors.

🧪 Example Agent with Session Logic

Try this embedded agent to see session logic in action:

Open your browser's DevTools and navigate to Application > Storage to inspect stored session values.
(Shortcut: F12 on Windows/Linux or Cmd + Option + I on macOS)


This approach lets you bypass cookie limitations and build advanced stateful flows — even in restrictive iframe environments.

Let us know how you’re using session logic in your projects!


🛠️ How to Integrate It

To enable session tracking in your embedded agent, you'll need to install and configure the Session Manager script using the steps below.

1. Paste the Script

Go to your agent’s Advanced Settings > Custom head code, and insert the full script inside:

Click to reveal the full Session Manager script

_596
<script id="agent-session-manager">
_596
_596
'use strict';
_596
/**
_596
* Agent Session Manager
_596
* @description Session management utilities for embedded agent environments using localStorage/sessionStorage
_596
* @version 1.0
_596
* @author Chris HK
_596
*/
_596
_596
/**
_596
* Session Manager for iframe environments
_596
* Uses localStorage and sessionStorage instead of cookies to avoid cross-origin restrictions
_596
*/
_596
const AgentSessionManager = {
_596
_596
/**
_596
* Configuration constants
_596
*/
_596
CONFIG: {
_596
SESSION_PREFIX: 'agent_session_',
_596
DEFAULT_EXPIRY_DAYS: 1,
_596
LOG_PREFIX: '[AgentSession]'
_596
},
_596
_596
/**
_596
* Utility functions
_596
*/
_596
utils: {
_596
/**
_596
* Safe logging with prefix
_596
* @param {string} level - log level (log, warn, error)
_596
* @param {string} message - message to log
_596
*/
_596
log: function(level, message) {
_596
if (typeof console !== 'undefined' && console[level]) {
_596
console[level](AgentSessionManager.CONFIG.LOG_PREFIX + ' ' + message);
_596
}
_596
},
_596
_596
/**
_596
* Validate email format
_596
* @param {string} email - email to validate
_596
* @returns {boolean} - true if valid email format
_596
*/
_596
isValidEmail: function(email) {
_596
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
_596
return typeof email === 'string' && emailRegex.test(email);
_596
},
_596
_596
/**
_596
* Check if localStorage is available
_596
* @returns {boolean} - true if localStorage is available
_596
*/
_596
isLocalStorageAvailable: function() {
_596
try {
_596
const test = '__localStorage_test__';
_596
localStorage.setItem(test, test);
_596
localStorage.removeItem(test);
_596
return true;
_596
} catch (e) {
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Check if sessionStorage is available
_596
* @returns {boolean} - true if sessionStorage is available
_596
*/
_596
isSessionStorageAvailable: function() {
_596
try {
_596
const test = '__sessionStorage_test__';
_596
sessionStorage.setItem(test, test);
_596
sessionStorage.removeItem(test);
_596
return true;
_596
} catch (e) {
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Create expiration timestamp
_596
* @param {number} days - number of days from now
_596
* @returns {number} - timestamp
_596
*/
_596
createExpiryTimestamp: function(days) {
_596
return Date.now() + (days * 24 * 60 * 60 * 1000);
_596
},
_596
_596
/**
_596
* Check if data has expired
_596
* @param {number} expiryTimestamp - expiry timestamp
_596
* @returns {boolean} - true if expired
_596
*/
_596
isExpired: function(expiryTimestamp) {
_596
return Date.now() > expiryTimestamp;
_596
}
_596
},
_596
_596
/**
_596
* Create a session with user data (persistent storage)
_596
* @param {string} sessionName - name of the session
_596
* @param {string} userName - user name
_596
* @param {string} userEmail - user email
_596
* @param {number} expiryDays - expiry in days
_596
* @returns {boolean} - true if session created successfully
_596
*/
_596
createSession: function(sessionName, userName, userEmail, expiryDays = this.CONFIG.DEFAULT_EXPIRY_DAYS) {
_596
try {
_596
// Parameter validation
_596
if (!sessionName || !userName || !userEmail) {
_596
this.utils.log('error', 'All parameters are required: sessionName, userName, userEmail');
_596
return false;
_596
}
_596
_596
// Email validation
_596
if (!this.utils.isValidEmail(userEmail)) {
_596
this.utils.log('error', 'Invalid email format: ' + userEmail);
_596
return false;
_596
}
_596
_596
// Check localStorage availability
_596
if (!this.utils.isLocalStorageAvailable()) {
_596
this.utils.log('error', 'localStorage is not available');
_596
return false;
_596
}
_596
_596
// Create session data
_596
const sessionData = {
_596
name: userName,
_596
email: userEmail,
_596
created: new Date().toISOString(),
_596
expires: this.utils.createExpiryTimestamp(expiryDays)
_596
};
_596
_596
// Store in localStorage with prefix
_596
const key = this.CONFIG.SESSION_PREFIX + sessionName;
_596
localStorage.setItem(key, JSON.stringify(sessionData));
_596
_596
this.utils.log('log', 'Persistent session created successfully: ' + sessionName);
_596
return true;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error creating session: ' + error.message);
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Create a temporary session (session storage - expires when tab closes)
_596
* @param {string} sessionName - name of the session
_596
* @param {string} userName - user name
_596
* @param {string} userEmail - user email
_596
* @returns {boolean} - true if session created successfully
_596
*/
_596
createTempSession: function(sessionName, userName, userEmail) {
_596
try {
_596
// Parameter validation
_596
if (!sessionName || !userName || !userEmail) {
_596
this.utils.log('error', 'All parameters are required: sessionName, userName, userEmail');
_596
return false;
_596
}
_596
_596
// Email validation
_596
if (!this.utils.isValidEmail(userEmail)) {
_596
this.utils.log('error', 'Invalid email format: ' + userEmail);
_596
return false;
_596
}
_596
_596
// Check sessionStorage availability
_596
if (!this.utils.isSessionStorageAvailable()) {
_596
this.utils.log('error', 'sessionStorage is not available');
_596
return false;
_596
}
_596
_596
// Create session data
_596
const sessionData = {
_596
name: userName,
_596
email: userEmail,
_596
created: new Date().toISOString(),
_596
temporary: true
_596
};
_596
_596
// Store in sessionStorage with prefix
_596
const key = this.CONFIG.SESSION_PREFIX + sessionName;
_596
sessionStorage.setItem(key, JSON.stringify(sessionData));
_596
_596
this.utils.log('log', 'Temporary session created successfully: ' + sessionName);
_596
return true;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error creating temporary session: ' + error.message);
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Read a session (checks both persistent and temporary storage)
_596
* @param {string} sessionName - name of the session to read
_596
* @returns {Object|null} - session data or null if not found/expired
_596
*/
_596
readSession: function(sessionName) {
_596
try {
_596
if (!sessionName) {
_596
this.utils.log('error', 'Session name is required');
_596
return null;
_596
}
_596
_596
// Try persistent storage first
_596
const persistentKey = this.CONFIG.SESSION_PREFIX + sessionName;
_596
if (this.utils.isLocalStorageAvailable()) {
_596
const persistentData = localStorage.getItem(persistentKey);
_596
if (persistentData) {
_596
try {
_596
const sessionData = JSON.parse(persistentData);
_596
_596
// Check if expired
_596
if (sessionData.expires && this.utils.isExpired(sessionData.expires)) {
_596
this.utils.log('log', 'Persistent session expired, removing: ' + sessionName);
_596
localStorage.removeItem(persistentKey);
_596
return null;
_596
}
_596
_596
this.utils.log('log', 'Persistent session read successfully: ' + sessionName);
_596
return sessionData;
_596
} catch (parseError) {
_596
this.utils.log('error', 'Error parsing persistent session data: ' + parseError.message);
_596
}
_596
}
_596
}
_596
_596
// Try temporary storage
_596
const tempKey = this.CONFIG.SESSION_PREFIX + sessionName;
_596
if (this.utils.isSessionStorageAvailable()) {
_596
const tempData = sessionStorage.getItem(tempKey);
_596
if (tempData) {
_596
try {
_596
const sessionData = JSON.parse(tempData);
_596
this.utils.log('log', 'Temporary session read successfully: ' + sessionName);
_596
return sessionData;
_596
} catch (parseError) {
_596
this.utils.log('error', 'Error parsing temporary session data: ' + parseError.message);
_596
}
_596
}
_596
}
_596
_596
this.utils.log('log', 'Session not found: ' + sessionName);
_596
return null;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error reading session: ' + error.message);
_596
return null;
_596
}
_596
},
_596
_596
/**
_596
* Update an existing session
_596
* @param {string} sessionName - name of the session to update
_596
* @param {string} userName - new user name
_596
* @param {string} userEmail - new user email
_596
* @returns {boolean} - true if session updated successfully
_596
*/
_596
updateSession: function(sessionName, userName, userEmail) {
_596
try {
_596
// Parameter validation
_596
if (!sessionName || !userName || !userEmail) {
_596
this.utils.log('error', 'All parameters are required: sessionName, userName, userEmail');
_596
return false;
_596
}
_596
_596
// Email validation
_596
if (!this.utils.isValidEmail(userEmail)) {
_596
this.utils.log('error', 'Invalid email format: ' + userEmail);
_596
return false;
_596
}
_596
_596
// Check if session exists
_596
const existingSession = this.readSession(sessionName);
_596
if (!existingSession) {
_596
this.utils.log('error', 'Session not found, cannot update: ' + sessionName);
_596
return false;
_596
}
_596
_596
// Determine storage type and update
_596
if (existingSession.temporary) {
_596
// Update temporary session
_596
const sessionData = {
_596
name: userName,
_596
email: userEmail,
_596
created: existingSession.created,
_596
updated: new Date().toISOString(),
_596
temporary: true
_596
};
_596
_596
const key = this.CONFIG.SESSION_PREFIX + sessionName;
_596
sessionStorage.setItem(key, JSON.stringify(sessionData));
_596
this.utils.log('log', 'Temporary session updated successfully: ' + sessionName);
_596
} else {
_596
// Update persistent session
_596
const sessionData = {
_596
name: userName,
_596
email: userEmail,
_596
created: existingSession.created,
_596
updated: new Date().toISOString(),
_596
expires: existingSession.expires
_596
};
_596
_596
const key = this.CONFIG.SESSION_PREFIX + sessionName;
_596
localStorage.setItem(key, JSON.stringify(sessionData));
_596
this.utils.log('log', 'Persistent session updated successfully: ' + sessionName);
_596
}
_596
_596
return true;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error updating session: ' + error.message);
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Delete a session (removes from both storages)
_596
* @param {string} sessionName - name of the session to delete
_596
* @returns {boolean} - true if session deleted successfully
_596
*/
_596
deleteSession: function(sessionName) {
_596
try {
_596
if (!sessionName) {
_596
this.utils.log('error', 'Session name is required');
_596
return false;
_596
}
_596
_596
let deleted = false;
_596
_596
// Remove from localStorage
_596
if (this.utils.isLocalStorageAvailable()) {
_596
const persistentKey = this.CONFIG.SESSION_PREFIX + sessionName;
_596
if (localStorage.getItem(persistentKey)) {
_596
localStorage.removeItem(persistentKey);
_596
deleted = true;
_596
}
_596
}
_596
_596
// Remove from sessionStorage
_596
if (this.utils.isSessionStorageAvailable()) {
_596
const tempKey = this.CONFIG.SESSION_PREFIX + sessionName;
_596
if (sessionStorage.getItem(tempKey)) {
_596
sessionStorage.removeItem(tempKey);
_596
deleted = true;
_596
}
_596
}
_596
_596
if (deleted) {
_596
this.utils.log('log', 'Session deleted successfully: ' + sessionName);
_596
return true;
_596
} else {
_596
this.utils.log('log', 'Session not found for deletion: ' + sessionName);
_596
return false;
_596
}
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error deleting session: ' + error.message);
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Clear all sessions (both persistent and temporary)
_596
* @returns {boolean} - true if sessions cleared successfully
_596
*/
_596
clearAllSessions: function() {
_596
try {
_596
let cleared = false;
_596
_596
// Clear from localStorage
_596
if (this.utils.isLocalStorageAvailable()) {
_596
const keys = Object.keys(localStorage);
_596
keys.forEach(key => {
_596
if (key.startsWith(this.CONFIG.SESSION_PREFIX)) {
_596
localStorage.removeItem(key);
_596
cleared = true;
_596
}
_596
});
_596
}
_596
_596
// Clear from sessionStorage
_596
if (this.utils.isSessionStorageAvailable()) {
_596
const keys = Object.keys(sessionStorage);
_596
keys.forEach(key => {
_596
if (key.startsWith(this.CONFIG.SESSION_PREFIX)) {
_596
sessionStorage.removeItem(key);
_596
cleared = true;
_596
}
_596
});
_596
}
_596
_596
if (cleared) {
_596
this.utils.log('log', 'All sessions cleared successfully');
_596
} else {
_596
this.utils.log('log', 'No sessions found to clear');
_596
}
_596
_596
return true;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error clearing sessions: ' + error.message);
_596
return false;
_596
}
_596
},
_596
_596
/**
_596
* Get all active sessions
_596
* @returns {Array} - array of session objects
_596
*/
_596
getAllSessions: function() {
_596
const sessions = [];
_596
_596
try {
_596
// Get persistent sessions
_596
if (this.utils.isLocalStorageAvailable()) {
_596
const keys = Object.keys(localStorage);
_596
keys.forEach(key => {
_596
if (key.startsWith(this.CONFIG.SESSION_PREFIX)) {
_596
try {
_596
const sessionData = JSON.parse(localStorage.getItem(key));
_596
const sessionName = key.replace(this.CONFIG.SESSION_PREFIX, '');
_596
_596
// Check if expired
_596
if (!sessionData.expires || !this.utils.isExpired(sessionData.expires)) {
_596
sessions.push({
_596
name: sessionName,
_596
data: sessionData,
_596
type: 'persistent'
_596
});
_596
} else {
_596
// Remove expired session
_596
localStorage.removeItem(key);
_596
}
_596
} catch (parseError) {
_596
this.utils.log('error', 'Error parsing session: ' + key);
_596
}
_596
}
_596
});
_596
}
_596
_596
// Get temporary sessions
_596
if (this.utils.isSessionStorageAvailable()) {
_596
const keys = Object.keys(sessionStorage);
_596
keys.forEach(key => {
_596
if (key.startsWith(this.CONFIG.SESSION_PREFIX)) {
_596
try {
_596
const sessionData = JSON.parse(sessionStorage.getItem(key));
_596
const sessionName = key.replace(this.CONFIG.SESSION_PREFIX, '');
_596
sessions.push({
_596
name: sessionName,
_596
data: sessionData,
_596
type: 'temporary'
_596
});
_596
} catch (parseError) {
_596
this.utils.log('error', 'Error parsing session: ' + key);
_596
}
_596
}
_596
});
_596
}
_596
_596
this.utils.log('log', 'Retrieved ' + sessions.length + ' active sessions');
_596
return sessions;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error getting all sessions: ' + error.message);
_596
return [];
_596
}
_596
},
_596
_596
/**
_596
* Send session data to parent window via postMessage
_596
* @param {string} sessionName - session name
_596
* @param {string} messageType - type of message
_596
* @returns {boolean} - true if message sent successfully
_596
*/
_596
sendToParent: function(sessionName, messageType = 'SESSION_DATA') {
_596
try {
_596
if (window.parent === window) {
_596
this.utils.log('warn', 'Not in iframe, cannot send to parent');
_596
return false;
_596
}
_596
_596
const sessionData = this.readSession(sessionName);
_596
if (!sessionData) {
_596
this.utils.log('error', 'Session not found for sending: ' + sessionName);
_596
return false;
_596
}
_596
_596
const message = {
_596
type: messageType,
_596
sessionName: sessionName,
_596
sessionData: sessionData,
_596
timestamp: new Date().toISOString()
_596
};
_596
_596
window.parent.postMessage(message, '*');
_596
this.utils.log('log', 'Session data sent to parent: ' + sessionName);
_596
return true;
_596
_596
} catch (error) {
_596
this.utils.log('error', 'Error sending to parent: ' + error.message);
_596
return false;
_596
}
_596
}
_596
};
_596
_596
// Usage Examples:
_596
/*
_596
// === Basic Usage ===
_596
_596
// Create a persistent session (expires in 1 day by default)
_596
AgentSessionManager.createSession('userLogin', 'John Doe', 'john.doe@example.com');
_596
_596
// Create a temporary session (expires when tab closes)
_596
AgentSessionManager.createTempSession('chatSession', 'Jane Smith', 'jane.smith@example.com');
_596
_596
// Read a session
_596
const userData = AgentSessionManager.readSession('userLogin');
_596
if (userData) {
_596
console.log('User Name:', userData.name);
_596
console.log('User Email:', userData.email);
_596
console.log('Created:', new Date(userData.created).toLocaleString());
_596
}
_596
_596
// Update a session
_596
AgentSessionManager.updateSession('userLogin', 'John Smith', 'john.smith@example.com');
_596
_596
// Delete a session
_596
AgentSessionManager.deleteSession('userLogin');
_596
_596
// === Advanced Usage ===
_596
_596
// Get all active sessions
_596
const allSessions = AgentSessionManager.getAllSessions();
_596
console.log('Active sessions:', allSessions);
_596
_596
// Clear all sessions
_596
AgentSessionManager.clearAllSessions();
_596
_596
// === Iframe Communication ===
_596
_596
// Send session data to parent window
_596
AgentSessionManager.sendToParent('userLogin', 'USER_AUTHENTICATED');
_596
_596
// Listen for messages from parent (in iframe)
_596
window.addEventListener('message', function(event) {
_596
if (event.data.type === 'REQUEST_SESSION') {
_596
AgentSessionManager.sendToParent(event.data.sessionName, 'SESSION_RESPONSE');
_596
}
_596
});
_596
_596
// === Error Handling Example ===
_596
_596
// Create session with error handling
_596
if (AgentSessionManager.createSession('userSession', 'Test User', 'invalid-email')) {
_596
console.log('Session created successfully');
_596
} else {
_596
console.log('Failed to create session - check console for errors');
_596
}
_596
_596
// === Custom Expiry Example ===
_596
_596
// Create session with custom expiry (30 days)
_596
AgentSessionManager.createSession('longTermSession', 'VIP User', 'vip@example.com', 30);
_596
_596
*/
_596
_596
// Export for module usage (optional)
_596
if (typeof module !== 'undefined' && module.exports) {
_596
module.exports = AgentSessionManager;
_596
}
_596
_596
// Global assignment for direct usage
_596
if (typeof window !== 'undefined') {
_596
window.AgentSessionManager = AgentSessionManager;
_596
}
_596
_596
</script>
_596
_596
<script id="send-message-to-parent">
_596
// Send a session operation message to the parent page
_596
function sendSessionOperation(operation) {
_596
const message = {
_596
type: "agent",
_596
message: operation
_596
};
_596
_596
// Send to parent window (if your agent is in an iframe)
_596
window.parent.postMessage(message, '*');
_596
}
_596
</script>

2. Use the API in Your Agent

Once loaded, the script is globally accessible as AgentSessionManager. Example:


_10
AgentSessionManager.createSession('userLogin', 'Alex Blue', 'alex@example.com', 7);

Where:

  • 'userLogin' is the session key
  • 'Alex Blue' is the user's name
  • 'alex@example.com' is the user's email
  • 7 is the number of days until the session expires