Sharely.ai Web Control
The Sharely.ai Web Control allows you to embed intelligent AI agents directly into your web applications, providing a seamless, branded experience for your users.
Web Control is open source (Apache 2.0): github.com/sharelyai/webcontrol (opens in a new tab). Embed the hosted bundle with a script tag, compose the React packages into your own UI, or fork and self-host the whole thing — see Open Source & Self-Hosting.
What is the Web Control?
The Sharely.ai Web Control is an embeddable JavaScript component that brings the full power of Sharely.ai agents into your application:
- Fully Customizable UI - Match your brand and design system
- Your Domain - Host on your own domain with your branding
- Integrated Experience - Seamlessly blend AI into your user experience
- Programmatic Control - Full API control over agent behavior and appearance
- Flexible Authentication - Anonymous visitors or identities asserted by your app
- Role-Based Access Control - Built-in RBAC support for enterprise applications
- Custom Agent Rendering - Streams thinking steps, tool calls, and citations from your own agent
When to Use Web Control
Use Web Control when you want to:
- ✅ Embed AI capabilities directly into your existing application
- ✅ Maintain complete control over the user experience and branding
- ✅ Integrate with your existing authentication system
- ✅ Implement role-based content access control
- ✅ Provide a native, seamless AI assistant experience
Compare to Sharely.ai Hosted Agents:
| Feature | Web Control | Hosted Agents |
|---|---|---|
| Hosting | Your domain | Sharely.ai domain |
| Branding | Fully customizable | Sharely.ai branding |
| Authentication | Your auth system | Sharely.ai auth |
| Integration | Embedded in app | Standalone links |
| Development | Requires coding | No code needed |
| Best for | Custom applications | Quick launches, sharing |
Open Source & Self-Hosting
Web Control is developed in the open at github.com/sharelyai/webcontrol (opens in a new tab) under the Apache 2.0 license (© 2026 Sharely.ai, Inc.).
The repository is a monorepo with three ways to consume it:
-
Hosted embed bundle (easiest) — a single script tag served from
https://webcontrol.sharely.ai/assets/sharelyai.js. No build step. This is what the rest of this page documents. -
React packages — layered packages you can compose into your own React app for granular control:
@sharelyai/services— headless data layer (API client, hooks, store) with no UI@sharelyai/ui-shared— theme and base components@sharelyai/ui-chat,@sharelyai/ui-search,@sharelyai/ui-browse,@sharelyai/ui-agent-chat— individual feature panels@sharelyai/webcontrol— the full assembled component
See the package examples (opens in a new tab) and the runnable demo app in the repository.
-
Fork and self-host — build the single-file bundle from source (
pnpm install && pnpm build) and serve it from your own CDN with your own defaults. The repository's CONTRIBUTING guide (opens in a new tab) covers local development, environment variables, deployment (including Vercel), and rebranding guidance for forks.
You are free to use, modify, and deploy your own fork under the Apache 2.0 terms. Publishing to the @sharelyai npm scope remains restricted to Sharely.ai maintainers. Issues and contributions are welcome on GitHub.
Two Authentication Modes
Web Control supports exactly two integration modes. The mode is determined by what you pass to initialize().
| Anonymous | Host-Asserted Identity | |
|---|---|---|
| You pass | workspaceId only | workspaceId + externalToken + externalUserId (+ spaceId) |
| Who the user is | An anonymous visitor | A user your app has authenticated |
| Who asserts it | Nobody — Web Control mints a temporary session | Your backend, by minting a token |
| RBAC | Workspace defaults | Role embedded in the token |
| Best for | Public sites, demos, lead capture | Portals, SaaS apps, enterprise |
Not supported: passing
externalUserIdwithoutexternalToken. Identity must always be asserted with a token your backend minted — an unaccompanied user ID would let any visitor impersonate any user. If you want to identify users, use host-asserted mode.
Mode 1: Anonymous
How it works:
- User accesses your application without logging in
- Web Control initializes with just your
workspaceId - The widget creates a temporary space for the visitor and keeps a temporal token (persisted in a cookie) so the conversation survives page reloads
- User can interact with the AI assistant immediately
When to use:
- Public websites where anyone can access the AI
- Trial/freemium experiences
- Lead capture workflows
- Demo environments
Example use cases: website chatbot for visitor engagement, product demo assistant, lead qualification bot, public knowledge base assistant.
Mode 2: Host-Asserted Identity
How it works:
- User logs in to your application using your authentication system
- Your backend mints a Sharely.ai token for the authenticated user (the API key never leaves your server)
- Frontend initializes Web Control with
externalToken,spaceId, andexternalUserId— all three together - The AI assistant has full user context and role-based permissions
- User maintains their identity and conversation history across sessions
When to use:
- Enterprise applications with existing auth
- Customer portals with personalized content
- Applications requiring role-based access control (RBAC)
- SaaS platforms with multi-tenant requirements
Example use cases: customer support portal, internal knowledge assistant, partner/reseller portals, enterprise SaaS applications.
Installation
1. Include the Sharely.ai Script
Add the Sharely.ai Web Control script to your HTML:
<script type="module" crossorigin src="https://webcontrol.sharely.ai/assets/sharelyai.js"></script>Example in Next.js:
import Script from 'next/script';
<Script
type="module"
crossOrigin
src="https://webcontrol.sharely.ai/assets/sharelyai.js"
strategy="afterInteractive"
/>Example in React:
useEffect(() => {
const script = document.createElement('script');
script.src = 'https://webcontrol.sharely.ai/assets/sharelyai.js';
script.type = 'module';
script.crossOrigin = 'true';
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);If you self-host a fork, point the src at your own deployment instead.
2. Add the Container Element
Add a container div where you want the Web Control to appear:
<div id="sharelyai-webcontroller-id"></div>Note: The container ID must be sharelyai-webcontroller-id.
3. Initialize the Web Control
Initialize the Web Control with your configuration (see modes below).
Anonymous Implementation
For anonymous visitors, initialize with just your workspace ID:
<!DOCTYPE html>
<html>
<head>
<title>My App with Sharely AI</title>
<script type="module" crossorigin src="https://webcontrol.sharely.ai/assets/sharelyai.js"></script>
</head>
<body>
<h1>Welcome to My App</h1>
<!-- Web Control Container -->
<div id="sharelyai-webcontroller-id"></div>
<script>
// Initialize when page loads
window.addEventListener('load', function() {
if (window.sharelyai) {
window.sharelyai.initialize({
workspaceId: 'your-workspace-id',
mode: 'placed-inline',
displayMode: {
OPEN_BY_DEFAULT: true,
WIDTH: '100%',
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: true },
SEARCH: { SHOW: true },
BROWSE: { SHOW: true }
}
}
});
window.sharelyai.render();
}
});
</script>
</body>
</html>Do not pass externalUserId in anonymous mode — user identity is only supported together with a host-minted externalToken (see above).
Host-Asserted Implementation
Backend Token Generation
Your backend must generate a Sharely.ai token when users log in. This follows the two-step authentication process:
Step 1: Generate API Access Token
// Node.js/Express example
const API_KEY = process.env.SHARELY_API_KEY;
const WORKSPACE_ID = process.env.SHARELY_WORKSPACE_ID;
const ORGANIZATION_ID = process.env.SHARELY_ORGANIZATION_ID;
async function generateAPIAccessToken(customerRoleId) {
const response = await fetch(
`https://api.sharely.ai/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
customerRoleId: customerRoleId // Optional: for RBAC
})
}
);
if (!response.ok) {
throw new Error('Failed to generate API token');
}
return await response.json();
}Step 2: Create User Space Token
async function createUserSpaceToken(apiToken, userId) {
const response = await fetch(
`https://api.sharely.ai/workspaces/${WORKSPACE_ID}/activate-or-retrieve-user-space`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiToken}`,
'organizationId': ORGANIZATION_ID,
'Content-Type': 'application/json'
},
body: JSON.stringify({
workspaceId: WORKSPACE_ID,
customerIdString: userId // User email or custom ID
})
}
);
if (!response.ok) {
throw new Error('Failed to create user space');
}
return await response.json();
}Complete Login Endpoint
// Express endpoint that returns Sharely token on login
app.post('/api/login', async (req, res) => {
try {
const { email, password } = req.body;
// Your authentication logic
const user = await authenticateUser(email, password);
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Determine user's role (optional, for RBAC)
const customerRoleId = getUserRole(user); // e.g., "premium-user", "admin"
// Generate API access token with role
const apiToken = await generateAPIAccessToken(customerRoleId);
// Create user space token (role inherited automatically)
const userSpace = await createUserSpaceToken(apiToken.token, user.email);
// Return tokens to frontend
res.json({
user: {
id: user.id,
email: user.email
},
sharely: {
token: userSpace.token,
spaceId: userSpace.spaceId
}
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: error.message });
}
});Frontend Initialization
After user logs in, initialize the Web Control with the token from your backend. Pass externalToken, spaceId, and externalUserId together:
// Fetch Sharely token from your backend
async function initializeSharelyForUser() {
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: userEmail,
password: userPassword
})
});
const data = await response.json();
if (window.sharelyai) {
window.sharelyai.initialize({
workspaceId: 'your-workspace-id',
externalToken: data.sharely.token, // Token minted by your backend
spaceId: data.sharely.spaceId, // Space ID from your backend
externalUserId: data.user.email, // The identity that token asserts
mode: 'placed-inline',
displayMode: {
OPEN_BY_DEFAULT: true,
WIDTH: '100%',
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: true },
SEARCH: { SHOW: true },
BROWSE: { SHOW: true }
}
}
});
window.sharelyai.render();
}
} catch (error) {
console.error('Failed to initialize Sharely:', error);
}
}
// Call when user is authenticated
initializeSharelyForUser();Complete React Example
import { useEffect, useState } from 'react';
function SharelyWidget({ user }) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
if (!user) return;
const initializeSharely = async () => {
try {
// Fetch Sharely token for authenticated user
const response = await fetch('/api/sharely-token', {
method: 'GET',
headers: {
'Authorization': `Bearer ${user.sessionToken}`
}
});
if (!response.ok) {
throw new Error('Failed to get Sharely token');
}
const { token, spaceId } = await response.json();
// Wait for Sharely script to load
if (!window.sharelyai) {
throw new Error('Sharely script not loaded');
}
// Initialize with external token
window.sharelyai.initialize({
workspaceId: process.env.NEXT_PUBLIC_SHARELY_WORKSPACE_ID,
externalToken: token,
spaceId: spaceId,
externalUserId: user.email,
mode: 'placed-inline',
displayMode: {
OPEN_BY_DEFAULT: true,
WIDTH: '100%',
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: true },
SEARCH: { SHOW: true },
BROWSE: { SHOW: true }
}
}
});
window.sharelyai.render();
setLoading(false);
} catch (err) {
console.error('Sharely initialization error:', err);
setError(err.message);
setLoading(false);
}
};
initializeSharely();
}, [user]);
if (loading) return <div>Loading AI Assistant...</div>;
if (error) return <div>Error loading AI Assistant: {error}</div>;
return (
<div style={{ width: '100%', height: '600px' }}>
<div id="sharelyai-webcontroller-id" />
</div>
);
}
export default SharelyWidget;Initialization API Reference
window.sharelyai.initialize(config)
Initializes the Sharely.ai Web Control with the specified configuration. Calling initialize() again destroys any previous instance first.
Parameters
The config object accepts the following parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
workspaceId | string | Yes | Your Sharely.ai workspace UUID |
baseUrl | string | No | Backend API base URL. Defaults to https://api.sharely.ai. Useful for self-hosted forks and staging environments |
externalToken | string | No | Host-minted user space token (host-asserted mode) |
spaceId | string | No | Space ID from your backend (required when using externalToken) |
externalUserId | string | No | User identifier (email or custom ID). Only valid together with externalToken |
lang | string | No | UI language code (default: "en"). See supported languages |
langKnowledge | string | No | Knowledge content language filter (default: "en"). See supported languages |
mode | string | No | Launcher position — one of "placed-inline", "placed-floating", "bottom-right-floating", "bottom-center-floating", "top-center-floating". Invalid values throw an error |
displayMode | object | No | UI display configuration (see below) |
closedText | string | No | Text shown on the collapsed launcher |
justChat | boolean | No | Initialize in a chat-only flow |
avatarmodeDesktop | string | No | Avatar mode on desktop: "expanded" or "circle" |
avatarmodeMobile | string | No | Avatar mode on mobile: "expanded" or "circle" |
saveSpaceNumMgs | number | No | Number of messages to save locally (default: 1) |
env | string | No | Environment identifier sent in API headers |
onError | function | No | (error: Error) => void callback for runtime errors |
The legacy
apiparameter is still accepted as an alias forbaseUrl, but is deprecated.
Supported Languages
lang and langKnowledge accept: en, es, pt, pt-br, de, pl, zh, zh-hans, zh-hant, zh-yue.
displayMode Object
Controls the appearance and behavior of the Web Control UI:
| Property | Type | Description |
|---|---|---|
OPEN_BY_DEFAULT | boolean | Whether the widget is open when initialized |
MODE | string | Privacy mode: "PRIVATE" or "PUBLIC" |
WIDTH | string | Widget width (CSS value: "100%", "400px", etc.) |
HEIGHT | string | Widget height (CSS value) |
Z_INDEX | string | Stacking order for floating modes |
VIEWS | object | Configure which views are available (see below) |
VIEWS Object
Enable or disable specific views within the Web Control:
VIEWS: {
CHAT: { SHOW: true }, // Chat interface
SEARCH: { SHOW: true, SHOW_TAGS: true }, // Search interface (optionally with tag filters)
BROWSE: { SHOW: true }, // Browse/knowledge navigation
AGENT: { SHOW: true } // Custom agent chat (Bring Your Own Agent)
}The AGENT view renders conversations with your own agent server — streamed thinking steps, tool calls, and citations included. See Bring Your Own Agent.
Complete Example
window.sharelyai.initialize({
// Required
workspaceId: '550e8400-e29b-41d4-a716-446655440000',
// Host-asserted mode (all three together)
externalToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
spaceId: 'space-uuid-123',
externalUserId: 'user@example.com',
// Language configuration
lang: 'en', // UI language
langKnowledge: 'en', // Knowledge content language
// Display configuration
mode: 'bottom-right-floating',
closedText: 'Ask our AI',
displayMode: {
OPEN_BY_DEFAULT: false,
WIDTH: '420px',
HEIGHT: '640px',
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: true },
SEARCH: { SHOW: true, SHOW_TAGS: true },
BROWSE: { SHOW: true },
AGENT: { SHOW: false }
}
},
onError: (error) => console.error('Sharely error:', error)
});window.sharelyai.render()
Renders the Web Control into the #sharelyai-webcontroller-id container element. Must be called after initialize().
window.sharelyai.initialize({ /* config */ });
window.sharelyai.render();window.sharelyai.destroy()
Unmounts the Web Control and cleans up the instance. initialize() calls this automatically before re-initializing, but you can call it directly — for example, when the user logs out or navigates away in a single-page app.
window.sharelyai.destroy();window.sharelyai.config()
Returns the current configuration, including the resolved env.
const currentConfig = window.sharelyai.config();
console.log(currentConfig.lang); // "en"
console.log(currentConfig.workspaceId); // your workspace IDwindow.sharelyai.updateConfig(partialConfig)
Shallow-merges new configuration into the current instance at runtime — useful for switching languages without a full re-initialization:
window.sharelyai.updateConfig({ lang: 'es', langKnowledge: 'es' });Role-Based Access Control (RBAC)
When using host-asserted mode, you can implement role-based content access control by including a customerRoleId when generating API tokens.
How RBAC Works
- Create roles in your Sharely.ai workspace (e.g., "premium-user", "admin", "basic-user")
- Assign knowledge to specific roles via the Knowledge Roles API
- Generate tokens with roles by including
customerRoleIdin token generation - Users see filtered content - Web Control automatically shows only role-permitted content across Chat, Search, and Browse
Backend Implementation
// Determine user's role based on your app logic
function getUserRole(user) {
if (user.isAdmin) return 'admin-role';
if (user.subscriptionTier === 'premium') return 'premium-user';
return 'basic-user';
}
// Generate token with role
async function generateTokenWithRole(user) {
const customerRoleId = getUserRole(user);
// Step 1: Generate API token with role
const apiToken = await fetch(
`https://api.sharely.ai/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ customerRoleId })
}
).then(res => res.json());
// Step 2: Create user space (role inherited automatically)
const userSpace = await fetch(
`https://api.sharely.ai/workspaces/${WORKSPACE_ID}/activate-or-retrieve-user-space`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiToken.token}`,
'organizationId': ORGANIZATION_ID,
'Content-Type': 'application/json'
},
body: JSON.stringify({
workspaceId: WORKSPACE_ID,
customerIdString: user.email
})
}
).then(res => res.json());
return userSpace;
}The role is now embedded in the token - when you initialize Web Control with this token, content is automatically filtered by role.
Using Web Control with Your Own Agent
If you've deployed a custom agent with the Server SDK, Web Control is its delivery surface: enable the AGENT view and the widget renders your agent's streamed events — thinking steps, tool calls, sources — with no client UI to build.
window.sharelyai.initialize({
workspaceId: 'your-workspace-id',
displayMode: {
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: false },
SEARCH: { SHOW: true },
BROWSE: { SHOW: true },
AGENT: { SHOW: true } // conversations go to your registered agent server
}
}
});See Bring Your Own Agent and the Agent API for registering your agent server.
Best Practices
Token Management
✅ DO:
- Generate tokens on your backend, never expose API keys to frontend
- Implement token refresh logic (tokens expire after 24 hours)
- Store tokens securely (session storage, not local storage)
- Validate user authentication before generating tokens
❌ DON'T:
- Expose your Sharely API key in frontend code
- Share tokens between different users
- Pass
externalUserIdwithout anexternalToken— it is not a supported mode and would amount to unverified identity - Generate tokens without user authentication
Error Handling
async function initializeSharelyWithErrorHandling() {
try {
// Check if script loaded
if (!window.sharelyai) {
throw new Error('Sharely script not loaded');
}
// Fetch token
const response = await fetch('/api/sharely-token');
if (!response.ok) {
throw new Error(`Token fetch failed: ${response.status}`);
}
const { token, spaceId } = await response.json();
// Initialize
window.sharelyai.initialize({
workspaceId: WORKSPACE_ID,
externalToken: token,
spaceId: spaceId,
externalUserId: user.email,
mode: 'placed-inline',
onError: (error) => {
console.error('Sharely runtime error:', error);
},
displayMode: {
OPEN_BY_DEFAULT: true,
WIDTH: '100%',
MODE: 'PRIVATE',
VIEWS: {
CHAT: { SHOW: true },
SEARCH: { SHOW: true },
BROWSE: { SHOW: true }
}
}
});
window.sharelyai.render();
} catch (error) {
console.error('Sharely initialization failed:', error);
// Show fallback UI
document.getElementById('sharelyai-webcontroller-id').innerHTML = `
<div style="padding: 20px; border: 1px solid #ddd; border-radius: 4px;">
<p>AI Assistant temporarily unavailable. Please try again later.</p>
</div>
`;
}
}User Experience
Loading States:
// Show loading indicator
const container = document.getElementById('sharelyai-webcontroller-id');
container.innerHTML = '<div class="loading">Loading AI Assistant...</div>';
// Initialize
try {
await initializeSharely();
} catch (error) {
container.innerHTML = '<div class="error">Failed to load</div>';
}Lazy Loading:
// Only initialize when user interacts
document.getElementById('open-ai-button').addEventListener('click', function() {
initializeSharely();
this.disabled = true;
this.textContent = 'AI Assistant Loading...';
});Security Recommendations
- Always use HTTPS for production deployments
- Validate users on your backend before generating tokens
- Implement rate limiting on token generation endpoints
- Use Content Security Policy headers to restrict script sources
- Monitor token usage for suspicious activity
- Rotate API keys periodically
Troubleshooting
Web Control Not Appearing
Problem: Nothing appears in the container div
Solutions:
- Check that the script is loaded:
console.log(window.sharelyai) - Verify the container ID is exactly
sharelyai-webcontroller-id - Ensure
render()is called afterinitialize() - Check browser console for errors — an invalid
modevalue throws atinitialize()
Token Generation Failures
Problem: Backend fails to generate tokens
Solutions:
- Verify API key is correct and not expired
- Check that workspace ID and organization ID are correct
- Ensure role exists (if using RBAC)
- Check API endpoint URLs are correct
Content Not Filtered by Role
Problem: Users see content outside their role
Solutions:
- Verify RBAC is enabled for your workspace
- Check that roles are assigned to knowledge items
- Confirm
customerRoleIdis included in token generation - Verify token contains role information (decode JWT)
Related Documentation
Open Source
- Web Control on GitHub (opens in a new tab) - Source, examples, demo app, contributing
- Open Source at Sharely.ai - Everything we've opened and how it fits together
APIs
- Spaces API - User space provisioning and token generation
- Roles API - Create and manage RBAC roles
- Knowledge Roles API - Assign knowledge to roles
- Agent API - Custom agent threads, RAG, and server registration
- Authentication - API authentication guide
Concepts
- Roles Concept - Understanding workspace, space, and knowledge roles
- Space Concept - How spaces work in Sharely.ai
- Knowledge Concept - Knowledge management overview
Getting Help
- Documentation: https://docs.sharely.ai (opens in a new tab)
- GitHub Issues: https://github.com/sharelyai/webcontrol/issues (opens in a new tab)
- Support Email: support@sharely.ai
- API Status: https://status.sharely.ai (opens in a new tab)
Next Steps
- Get your API credentials - Sign up at https://app.sharely.ai (opens in a new tab)
- Choose your mode - Anonymous or host-asserted integration
- Implement backend token generation - Use the Spaces API (host-asserted mode)
- Initialize the Web Control - Add to your frontend
- Test thoroughly - Verify authentication and content filtering
- Deploy to production - Launch your AI-powered application