Deploying the Genesys Cloud Web Messaging Widget with Custom Guest Attributes for Authenticated Users
What You Will Build
- You will build a web application that initializes the Genesys Cloud Web Messaging widget and automatically injects custom guest attributes based on the current user’s authentication status.
- You will use the Genesys Cloud Web Messaging JavaScript SDK and the Genesys Cloud Platform API to retrieve and set user-specific data.
- You will use JavaScript (ES6+) for the frontend implementation and Node.js for the backend proxy to handle secure API calls.
Prerequisites
- OAuth Client Type: Public Client (for Web Messaging initialization) and Confidential Client (for backend API calls to Genesys Cloud).
- Required Scopes:
- Web Messaging: None (relies on the widget configuration).
- Backend API:
webmessaging:guest:write(to update guest attributes),webmessaging:guest:read(optional, for verification).
- SDK/API Version: Genesys Cloud Web Messaging SDK v2 (latest), Genesys Cloud API v2.
- Language/Runtime Requirements: Node.js 18+, Modern Browser (Chrome, Firefox, Safari, Edge).
- External Dependencies:
- Frontend:
@genesys/web-messaging(loaded via CDN or npm). - Backend:
axios,dotenv,express(for the proxy server).
- Frontend:
Authentication Setup
Web Messaging operates differently from standard API integrations. It does not use a standard OAuth 2.0 flow for the end-user session. Instead, it relies on a Widget Configuration retrieved from Genesys Cloud. However, to update guest attributes programmatically, you must interact with the Genesys Cloud API using a backend service that holds a valid OAuth token.
Step 1: Backend OAuth Token Acquisition
You need a backend service to obtain an OAuth token using Client Credentials flow. This token will be used to call the Web Messaging API on behalf of your application.
File: server.js (Node.js)
require('dotenv').config();
const axios = require('axios');
const GENESYS_REGION = process.env.GENESYS_REGION || 'us-east-1';
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const BASE_URL = `https://api.${GENESYS_REGION}.mygenesys.com`;
let accessToken = null;
let tokenExpiry = 0;
async function getAccessToken() {
if (accessToken && Date.now() < tokenExpiry) {
return accessToken;
}
try {
const response = await axios.post(`${BASE_URL}/oauth/token`, {
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
});
accessToken = response.data.access_token;
tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000; // Refresh 1 min early
return accessToken;
} catch (error) {
console.error('Failed to get access token:', error.response?.data || error.message);
throw new Error('Authentication failed');
}
}
module.exports = { getAccessToken, BASE_URL };
Step 2: Frontend Widget Initialization
The frontend does not need an OAuth token to initialize the widget. It needs the widgetId and the region. The widget handles its own session management.
File: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Genesys Cloud Web Messaging with Custom Attributes</title>
<style>
body { font-family: sans-serif; padding: 20px; }
#user-info { margin-bottom: 20px; padding: 10px; background: #f0f0f0; }
</style>
</head>
<body>
<h1>Web Messaging Demo</h1>
<div id="user-info">
<p>Simulated User: <strong>John Doe</strong></p>
<p>User ID: <strong>user-12345</strong></p>
<p>Membership Level: <strong>Gold</strong></p>
</div>
<!-- Genesys Cloud Web Messaging Widget Script -->
<script src="https://files.mygenesys.com/YOUR_ORG_ID/webmessaging/YOUR_WIDGET_ID.js"></script>
<script src="app.js"></script>
</body>
</html>
Note: Replace YOUR_ORG_ID and YOUR_WIDGET_ID with your actual Genesys Cloud organization ID and Web Messaging widget ID.
Implementation
Step 1: Initialize the Widget and Listen for Events
You must initialize the widget and listen for the onReady event. This event confirms that the widget is fully loaded and ready to accept configuration changes or custom attributes.
File: app.js (Frontend)
// Configuration
const WIDGET_CONFIG = {
widgetId: 'YOUR_WIDGET_ID',
orgId: 'YOUR_ORG_ID',
region: 'us-east-1' // Adjust based on your region
};
// Simulated authenticated user data
const currentUser = {
id: 'user-12345',
name: 'John Doe',
membershipLevel: 'Gold',
email: 'john.doe@example.com'
};
let wmInstance = null;
// Initialize the widget
function initWebMessaging() {
if (typeof GenesysCloudWebMessaging === 'undefined') {
console.error('Genesys Cloud Web Messaging SDK not loaded.');
return;
}
try {
// Create the widget instance
wmInstance = GenesysCloudWebMessaging.createWidget(WIDGET_CONFIG);
// Listen for the ready event
wmInstance.onReady(() => {
console.log('Web Messaging Widget is ready.');
updateGuestAttributes();
});
// Optional: Listen for errors
wmInstance.onError((error) => {
console.error('Web Messaging Error:', error);
});
} catch (error) {
console.error('Failed to initialize Web Messaging:', error);
}
}
// Call init on load
document.addEventListener('DOMContentLoaded', initWebMessaging);
Step 2: Define the Custom Attributes Structure
Genesys Cloud Web Messaging allows you to set custom guest attributes. These attributes are key-value pairs that can be used in routing strategies, analytics, and IVR flows. You must define the structure of these attributes.
File: app.js (Frontend - continued)
/**
* Formats the user data into the structure expected by Genesys Cloud Web Messaging.
* @param {Object} user - The authenticated user object.
* @returns {Object} - The formatted attributes object.
*/
function formatCustomAttributes(user) {
return {
// System-defined attributes (if allowed by your widget config)
// Note: Some attributes like 'name' and 'email' might be handled by the widget's identity flow.
// Custom attributes should use a namespace to avoid conflicts.
'custom.userId': user.id,
'custom.membershipLevel': user.membershipLevel,
'custom.isAuthenticated': true
};
}
/**
* Updates the guest attributes on the widget.
* This function uses the widget's API to set attributes locally.
* These attributes are then sent to Genesys Cloud when a conversation starts.
*/
async function updateGuestAttributes() {
if (!wmInstance) {
console.error('Widget instance not initialized.');
return;
}
const attributes = formatCustomAttributes(currentUser);
try {
// Use the widget's API to set guest attributes
// The 'setGuestAttributes' method is part of the Genesys Cloud Web Messaging SDK
await wmInstance.setGuestAttributes(attributes);
console.log('Guest attributes updated successfully:', attributes);
} catch (error) {
console.error('Failed to update guest attributes:', error);
}
}
Note: The setGuestAttributes method is available in the Genesys Cloud Web Messaging SDK. It updates the local guest profile, which is then synced with Genesys Cloud when the conversation begins or when explicitly triggered.
Step 3: Backend Proxy for Secure Attribute Updates (Optional but Recommended)
While the frontend can set attributes locally, for critical data or to ensure attributes are persisted before the conversation starts, you may want to update them via the backend API. This is especially useful if you need to validate the user’s data against your database before passing it to Genesys.
File: server.js (Node.js - continued)
const express = require('express');
const app = express();
app.use(express.json());
const { getAccessToken, BASE_URL } = require('./auth'); // Assuming auth.js contains the getAccessToken function
/**
* Endpoint to update guest attributes via Genesys Cloud API.
* This is useful for server-side validation and persistence.
* POST /api/update-guest-attributes
*/
app.post('/api/update-guest-attributes', async (req, res) => {
const { guestId, attributes } = req.body;
if (!guestId || !attributes) {
return res.status(400).json({ error: 'guestId and attributes are required' });
}
try {
const token = await getAccessToken();
// Genesys Cloud API endpoint for updating guest attributes
const apiEndpoint = `${BASE_URL}/api/v2/webmessaging/guests/${guestId}/attributes`;
const response = await axios.put(apiEndpoint, {
attributes: attributes
}, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
console.log('Guest attributes updated via API:', response.data);
res.status(200).json({ success: true, data: response.data });
} catch (error) {
console.error('Failed to update guest attributes via API:', error.response?.data || error.message);
res.status(500).json({ error: 'Failed to update guest attributes', details: error.response?.data });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Step 4: Integrating Frontend and Backend
To use the backend proxy, the frontend must send the guest ID and attributes to the backend. The guest ID is generated by the widget when a new session starts.
File: app.js (Frontend - updated)
// ... (previous code)
let currentGuestId = null;
// Listen for the guest session start event
wmInstance.onGuestSessionStart((event) => {
console.log('Guest session started:', event);
currentGuestId = event.guestId; // Store the guest ID for backend calls
// Update attributes via backend for persistence
updateAttributesViaBackend();
});
async function updateAttributesViaBackend() {
if (!currentGuestId) return;
const attributes = formatCustomAttributes(currentUser);
try {
const response = await fetch('/api/update-guest-attributes', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
guestId: currentGuestId,
attributes: attributes
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Backend attribute update response:', data);
} catch (error) {
console.error('Failed to update attributes via backend:', error);
}
}
Complete Working Example
Backend (server.js)
require('dotenv').config();
const axios = require('axios');
const express = require('express');
const app = express();
app.use(express.json());
const GENESYS_REGION = process.env.GENESYS_REGION || 'us-east-1';
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const BASE_URL = `https://api.${GENESYS_REGION}.mygenesys.com`;
let accessToken = null;
let tokenExpiry = 0;
async function getAccessToken() {
if (accessToken && Date.now() < tokenExpiry) {
return accessToken;
}
try {
const response = await axios.post(`${BASE_URL}/oauth/token`, {
grant_type: 'client_credentials',
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
});
accessToken = response.data.access_token;
tokenExpiry = Date.now() + (response.data.expires_in * 1000) - 60000;
return accessToken;
} catch (error) {
console.error('Failed to get access token:', error.response?.data || error.message);
throw new Error('Authentication failed');
}
}
app.post('/api/update-guest-attributes', async (req, res) => {
const { guestId, attributes } = req.body;
if (!guestId || !attributes) {
return res.status(400).json({ error: 'guestId and attributes are required' });
}
try {
const token = await getAccessToken();
const apiEndpoint = `${BASE_URL}/api/v2/webmessaging/guests/${guestId}/attributes`;
const response = await axios.put(apiEndpoint, {
attributes: attributes
}, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
res.status(200).json({ success: true, data: response.data });
} catch (error) {
console.error('Failed to update guest attributes via API:', error.response?.data || error.message);
res.status(500).json({ error: 'Failed to update guest attributes', details: error.response?.data });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Frontend (index.html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Genesys Cloud Web Messaging with Custom Attributes</title>
<style>
body { font-family: sans-serif; padding: 20px; }
#user-info { margin-bottom: 20px; padding: 10px; background: #f0f0f0; }
</style>
</head>
<body>
<h1>Web Messaging Demo</h1>
<div id="user-info">
<p>Simulated User: <strong>John Doe</strong></p>
<p>User ID: <strong>user-12345</strong></p>
<p>Membership Level: <strong>Gold</strong></p>
</div>
<!-- Replace with your actual widget script URL -->
<script src="https://files.mygenesys.com/YOUR_ORG_ID/webmessaging/YOUR_WIDGET_ID.js"></script>
<script src="app.js"></script>
</body>
</html>
Frontend (app.js)
const WIDGET_CONFIG = {
widgetId: 'YOUR_WIDGET_ID',
orgId: 'YOUR_ORG_ID',
region: 'us-east-1'
};
const currentUser = {
id: 'user-12345',
name: 'John Doe',
membershipLevel: 'Gold',
email: 'john.doe@example.com'
};
let wmInstance = null;
let currentGuestId = null;
function initWebMessaging() {
if (typeof GenesysCloudWebMessaging === 'undefined') {
console.error('Genesys Cloud Web Messaging SDK not loaded.');
return;
}
try {
wmInstance = GenesysCloudWebMessaging.createWidget(WIDGET_CONFIG);
wmInstance.onReady(() => {
console.log('Web Messaging Widget is ready.');
// Set initial attributes locally
const attributes = formatCustomAttributes(currentUser);
wmInstance.setGuestAttributes(attributes);
});
wmInstance.onGuestSessionStart((event) => {
console.log('Guest session started:', event);
currentGuestId = event.guestId;
updateAttributesViaBackend();
});
wmInstance.onError((error) => {
console.error('Web Messaging Error:', error);
});
} catch (error) {
console.error('Failed to initialize Web Messaging:', error);
}
}
function formatCustomAttributes(user) {
return {
'custom.userId': user.id,
'custom.membershipLevel': user.membershipLevel,
'custom.isAuthenticated': true
};
}
async function updateAttributesViaBackend() {
if (!currentGuestId) return;
const attributes = formatCustomAttributes(currentUser);
try {
const response = await fetch('/api/update-guest-attributes', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
guestId: currentGuestId,
attributes: attributes
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log('Backend attribute update response:', data);
} catch (error) {
console.error('Failed to update attributes via backend:', error);
}
}
document.addEventListener('DOMContentLoaded', initWebMessaging);
Common Errors & Debugging
Error: 401 Unauthorized
- What causes it: The backend OAuth token is invalid, expired, or the client credentials are incorrect.
- How to fix it: Verify your
CLIENT_IDandCLIENT_SECRETin the.envfile. Ensure the OAuth client has thewebmessaging:guest:writescope. Check the backend logs for the exact error message from Genesys Cloud.
Error: 403 Forbidden
- What causes it: The OAuth client does not have the required permissions to update guest attributes.
- How to fix it: In the Genesys Cloud Admin portal, navigate to Applications > OAuth clients. Select your client and ensure the scope
webmessaging:guest:writeis checked. Also, ensure the client is associated with the correct Web Messaging widget.
Error: Widget Not Loading
- What causes it: The widget script URL is incorrect, or the widget is not published.
- How to fix it: Verify the
orgIdandwidgetIdin the script URL. Ensure the widget is published in the Genesys Cloud Admin portal under Web Messaging > Widgets.
Error: Attributes Not Appearing in Conversations
- What causes it: The attributes are not being set before the conversation starts, or they are not included in the routing strategy.
- How to fix it: Ensure
setGuestAttributesis called in theonReadyhandler. If using the backend API, ensure theguestIdis valid and the API call succeeds. Check the Genesys Cloud conversation logs to verify the attributes are being received.