Activate or Retrieve User Space
Ensure that a space exists for a specific user. If the space doesn't exist, it will be created automatically. This endpoint is idempotent - calling it multiple times for the same user will return the same space.
Endpoint
PUT /workspaces/{workspaceId}/activate-or-retrieve-user-spaceAuthentication
This endpoint requires a two-step authentication process:
- Generate an API Key Token using your workspace API key
- Use the token to call this endpoint
See the authentication flow below.
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
workspaceId | string (UUID) | Yes | The ID of your workspace |
Headers
| Header | Type | Required | Description |
|---|---|---|---|
Authorization | string | Yes | Bearer token from generate-access-key-token endpoint |
organizationId | string (UUID) | Yes | Your organization ID |
Content-Type | string | Yes | Must be application/json |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
workspaceId | string (UUID) | Yes | The ID of your workspace (same as path parameter) |
userId | string (UUID) | Conditional | User's UUID. Required if customerIdString not provided |
customerIdString | string | Conditional | Custom user identifier (email, username, etc). Required if userId not provided |
roleId | string (UUID) | No | Role UUID for RBAC. Mutually exclusive with customerRoleId |
customerRoleId | string | No | Custom role identifier for RBAC. Mutually exclusive with roleId |
Important Notes:
- You must provide either
userId(UUID) ORcustomerIdString(any string), but not both - If using RBAC, you can optionally provide either
roleId(UUID) ORcustomerRoleId(string), but not both - The
customerIdStringallows you to use your existing user identifiers without converting them to UUIDs
Response
Success Response
Status Code: 200 OK
Body:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"spaceId": "550e8400-e29b-41d4-a716-446655440000",
"userId": "user-123",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"isNew": false
}Response Fields
| Field | Type | Description |
|---|---|---|
token | string (JWT) | Access token for the user's space |
spaceId | string (UUID) | Unique identifier for the space |
userId | string | User identifier provided in request |
workspaceId | string (UUID) | Workspace ID from path parameter |
isNew | boolean | true if space was just created, false if it already existed |
Authentication Flow
This endpoint requires a two-step authentication process:
Step 1: Generate an Access Key Token
First, use your workspace API key to generate an access token:
curl -X POST \
'https://api.sharely.ai/workspaces/{workspaceId}/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Step 2: Activate or Retrieve User Space
Use the token from Step 1 to provision the user space:
curl -X PUT \
'https://api.sharely.ai/workspaces/{workspaceId}/activate-or-retrieve-user-space' \
-H 'Authorization: Bearer {token-from-step-1}' \
-H 'organizationId: {your-organization-id}' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "{workspaceId}",
"userId": "550e8400-e29b-41d4-a716-446655440000"
}'Creating Role-Bound Tokens for Web Control
When building authenticated Web Control experiences, you can create role-bound tokens that automatically scope all knowledge search, retrieval, and taxonomy APIs to a specific role. This is essential for implementing RBAC (Role-Based Access Control) in your application.
What Are Role-Bound Tokens?
Role-bound tokens are access tokens that include a customerRoleId, which:
- ✅ Automatically propagate role permissions through the entire authentication flow
- ✅ Control which knowledge base content users can access based on their role
- ✅ Scope all knowledge search and retrieval to role-specific content
- ✅ Filter taxonomy APIs (categories) based on role permissions
- ✅ Eliminate the need to manage roles separately at each API call
Key Concept: When you generate an API access token with a customerRoleId, that role automatically flows through to the user space token and all subsequent API calls. You only specify the role once.
How It Works
Your Backend → Generate API Token with customerRoleId → Create User Space → User Gets Role-Scoped Token → All APIs Auto-Scoped to RoleThe role information is embedded in the JWT token, so even though the API responses don't explicitly return customerRoleId fields currently, the role-based filtering is active and working.
Two Methods for Role Assignment
You can assign roles to tokens using either customerRoleId (recommended) or roleId. These are mutually exclusive - you can only use one per token.
Method 1: Customer Role ID (Recommended)
Use your own role identifiers - no mapping required! This is the simplest approach for most developers.
Benefits:
- ✅ Use existing role identifiers from your application (e.g., "admin", "sales-manager")
- ✅ No need to create a mapping between your roles and Sharely UUIDs
- ✅ Human-readable and easier to debug
- ✅ Simpler integration and maintenance
Example: If your application already has roles like "admin", "sales-rep", "viewer", you can use those exact strings directly.
curl -X POST \
'https://api.sharely.ai/workspaces/{workspaceId}/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"customerRoleId": "sales-manager"
}'Naming Rules for Customer Role IDs:
- Use lowercase with hyphens:
sales-manager,content-viewer - Only alphanumeric characters, hyphens (
-), and underscores (_) - Max length: 255 characters
- Must match a role created in Sharely.ai with the same
customerRoleId
Method 2: Role ID (UUID)
Use Sharely's internal role UUIDs - requires maintaining a mapping.
When to use:
- You need to reference roles by their Sharely-generated UUIDs
- You're working with roles created without custom
customerRoleIdvalues - You have existing code that already uses UUIDs
curl -X POST \
'https://api.sharely.ai/workspaces/{workspaceId}/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"roleId": "550e8400-e29b-41d4-a716-446655440000"
}'Important: You must provide either customerRoleId or roleId, but not both. If you provide both, the API will return an error.
Two-Step Process with Roles
Step 1: Generate API Access Token with Role
Using Customer Role ID (Recommended):
curl -X POST \
'https://api.sharely.ai/workspaces/{workspaceId}/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"customerRoleId": "sales-manager"
}'Using Role ID (UUID):
curl -X POST \
'https://api.sharely.ai/workspaces/{workspaceId}/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"roleId": "550e8400-e29b-41d4-a716-446655440000"
}'Current Response (both methods):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}Future Enhanced Response (planned):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"customerRoleId": "sales-manager", // or "roleId": "uuid"
"expiresIn": "1d"
}Note: The role information is embedded in the JWT token's metadata, so role-based access control is active even though the field isn't explicitly returned in the current API version.
Step 2: Create User Space (Role Inherited Automatically)
curl -X PUT \
'https://api.sharely.ai/workspaces/{workspaceId}/activate-or-retrieve-user-space' \
-H 'Authorization: Bearer {token-from-step-1}' \
-H 'organizationId: {your-organization-id}' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "{workspaceId}",
"customerIdString": "john.doe@example.com"
}'Current Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"spaceId": "space-uuid-123"
}Future Enhanced Response (planned):
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"space": {
"id": "space-uuid-123"
},
"user": {
"id": "user-uuid-456",
"email": "john.doe@example.com"
},
"temporalUserId": "temporal-uuid-789",
"customerRoleId": "sales-manager"
}Important: The customerRoleId from Step 1 is automatically inherited and embedded in the user space token. Role-based filtering is active for all subsequent API calls using this token.
Complete JavaScript Example
const API_KEY = 'sk-sharely-your-api-key';
const WORKSPACE_ID = 'your-workspace-id';
const ORGANIZATION_ID = 'your-organization-id';
const BASE_URL = 'https://api.sharely.ai';
// Role mapping - map your internal roles to customer role IDs
const ROLE_MAPPING = {
'admin': 'admin-role',
'manager': 'sales-manager',
'viewer': 'content-viewer',
'basic': 'basic-access'
};
// Determine user's role based on your application logic
function getUserRole(user) {
if (user.isAdmin) return ROLE_MAPPING.admin;
if (user.isManager) return ROLE_MAPPING.manager;
if (user.isPremium) return ROLE_MAPPING.viewer;
return ROLE_MAPPING.basic;
}
// Step 1: Generate API access token with customer role ID
async function generateAPIAccessToken(customerRoleId) {
const response = await fetch(
`${BASE_URL}/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({ customerRoleId })
}
);
if (!response.ok) {
throw new Error(`Failed to generate API token: ${response.statusText}`);
}
return await response.json();
}
// Step 2: Create user space token with inherited role
async function createUserSpaceToken(apiToken, userId) {
const response = await fetch(
`${BASE_URL}/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 // Using email or custom user ID
})
}
);
if (!response.ok) {
throw new Error(`Failed to create user space: ${response.statusText}`);
}
return await response.json();
}
// Complete authentication flow for user login
async function authenticateUserWithRole(user) {
// Determine the appropriate role for this user
const customerRoleId = getUserRole(user);
console.log(`Assigning role to ${user.email}:`, customerRoleId);
// Generate API access token with role
const apiTokenData = await generateAPIAccessToken(customerRoleId);
console.log('API token generated with embedded role');
// Create user space token (role inherited automatically)
const userSpaceData = await createUserSpaceToken(
apiTokenData.token,
user.email
);
console.log('User space token created with inherited role');
// Return token for Web Control initialization
return {
sharelyToken: userSpaceData.token,
spaceId: userSpaceData.spaceId,
customerRoleId: customerRoleId // Track locally for your app
};
}
// Usage in your login endpoint
app.post('/api/login', async (req, res) => {
const user = await authenticateUser(req.body);
const sharelyData = await authenticateUserWithRole(user);
res.json({
user: {
id: user.id,
email: user.email
},
sharely: sharelyData
});
});Initializing Web Control with Role-Bound Token
After obtaining the role-bound token from your backend, initialize the Web Control in your frontend:
// Frontend: Initialize Web Control with role-bound token
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password })
});
const { sharely } = await response.json();
// Initialize Web Control
window.sharelyai.initialize({
externalToken: sharely.sharelyToken,
spaceId: sharely.spaceId
});
// Now all knowledge searches, retrievals, and taxonomy APIs
// are automatically scoped to the user's roleWhat Gets Automatically Scoped?
When using a role-bound token, these APIs automatically filter results based on the role:
✅ Knowledge Search - Only returns knowledge items assigned to the role ✅ Knowledge Retrieval - Only allows access to role-permitted content ✅ Categories API - Only shows categories accessible to the role ✅ Taxonomy APIs - All taxonomy operations respect role boundaries
No additional code needed - the role-bound token handles all filtering automatically.
Comparison: Customer Role ID vs Role ID
| Feature | Customer Role ID | Role ID (UUID) |
|---|---|---|
| Identifier Type | Custom string (e.g., "sales-manager") | UUID (e.g., "550e8400-...") |
| Mapping Required | ❌ No - use your app's existing role names | ✅ Yes - must map your roles to Sharely UUIDs |
| Readability | ✅ Human-readable | ❌ UUIDs are hard to read |
| Debugging | ✅ Easy - see role name in logs | ❌ Harder - need to look up UUID |
| Integration Effort | ✅ Minimal - use existing role strings | ❌ More work - maintain mapping table |
| Recommended | ✅ Yes - for most use cases | Only if you must use UUIDs |
Example - No Mapping Needed with Customer Role ID:
// Your application's existing roles
const userRole = user.isAdmin ? 'admin' :
user.isSalesRep ? 'sales-rep' :
'viewer';
// Use directly in Sharely API - no mapping needed!
const apiToken = await generateAPIAccessToken(userRole);Example - Mapping Required with Role ID:
// Must maintain this mapping
const ROLE_MAPPING = {
'admin': '550e8400-e29b-41d4-a716-446655440000',
'sales-rep': '660e9511-f30c-52e5-b827-557766551111',
'viewer': '770ea622-g41d-63f6-c938-668877662222'
};
// Extra step: convert your role to UUID
const userRole = user.isAdmin ? 'admin' :
user.isSalesRep ? 'sales-rep' :
'viewer';
const sharelyRoleUUID = ROLE_MAPPING[userRole];
const apiToken = await generateAPIAccessToken({ roleId: sharelyRoleUUID });Direct Role Mapping Example
When using customerRoleId, your application's roles map directly to Sharely roles:
// No mapping table needed!
// Your app role → Sharely customerRoleId (same string)
const ROLES = {
ADMIN: 'admin', // Direct use
SALES_MANAGER: 'sales-manager', // Direct use
VIEWER: 'content-viewer', // Direct use
BASIC: 'basic-access' // Direct use
};Backend Integration Example (Express)
const express = require('express');
const app = express();
// Configuration
const SHARELY_CONFIG = {
apiKey: process.env.SHARELY_API_KEY,
workspaceId: process.env.SHARELY_WORKSPACE_ID,
organizationId: process.env.SHARELY_ORGANIZATION_ID,
apiBaseUrl: 'https://api.sharely.ai'
};
// Role mapping
const ROLE_MAPPING = {
'admin': 'admin-role',
'manager': 'sales-manager',
'sales': 'sales-role',
'premium': 'premium-role',
'basic': 'basic-role'
};
// Determine user's Sharely role
function determineUserRole(user) {
if (user.isAdmin) return ROLE_MAPPING.admin;
if (user.isManager) return ROLE_MAPPING.manager;
if (user.department === 'Sales') return ROLE_MAPPING.sales;
if (user.subscriptionTier === 'premium') return ROLE_MAPPING.premium;
return ROLE_MAPPING.basic;
}
// Generate API access token with role
async function generateAPIAccessToken(customerRoleId) {
const response = await fetch(
`${SHARELY_CONFIG.apiBaseUrl}/workspaces/${SHARELY_CONFIG.workspaceId}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': SHARELY_CONFIG.apiKey,
'Content-Type': 'application/json'
},
body: JSON.stringify({ customerRoleId })
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Sharely API error: ${error.message}`);
}
return await response.json();
}
// Create user space token with inherited role
async function createUserSpaceToken(apiToken, userId) {
const response = await fetch(
`${SHARELY_CONFIG.apiBaseUrl}/workspaces/${SHARELY_CONFIG.workspaceId}/activate-or-retrieve-user-space`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${apiToken}`,
'organizationId': SHARELY_CONFIG.organizationId,
'Content-Type': 'application/json'
},
body: JSON.stringify({
workspaceId: SHARELY_CONFIG.workspaceId,
customerIdString: userId
})
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Sharely API error: ${error.message}`);
}
return await response.json();
}
// Login endpoint - returns Sharely token with role
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 Sharely role for this user
const customerRoleId = determineUserRole(user);
console.log(`User ${user.email} assigned role:`, customerRoleId);
// Generate API access token with role
const apiTokenData = await generateAPIAccessToken(customerRoleId);
console.log('API token generated with embedded role');
// Create user space token (role inherited automatically)
const userSpaceData = await createUserSpaceToken(
apiTokenData.token,
user.email
);
console.log('User space token created with inherited role');
// Return to frontend
res.json({
user: {
id: user.id,
email: user.email,
name: user.name
},
sharely: {
token: userSpaceData.token,
spaceId: userSpaceData.spaceId,
customerRoleId: customerRoleId // For local tracking
}
});
} catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: error.message });
}
});
app.listen(3000);Prerequisites
Before implementing role-bound tokens:
- Active Sharely Workspace with RBAC enabled (contact support@sharely.ai if needed)
- Workspace API Key (format:
sk-sharely-...) - Workspace ID and Organization ID (UUIDs)
- Roles Created in your Sharely workspace with assigned permissions
Creating Roles
Roles must be created in your Sharely workspace before use. Use the Roles API to:
- Create roles with customer role IDs (e.g., "sales-manager", "admin-role")
- Assign permissions to roles (which knowledge items/categories they can access)
- Use the Knowledge Roles API to assign knowledge to roles
Example: Creating a role via API:
curl -X POST \
'https://api.sharely.ai/v1/workspaces/{workspaceId}/roles' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"customerRoleId": "sales-manager",
"name": "Sales Manager",
"description": "Access to sales-related content and analytics"
}'Then assign knowledge to the role:
curl -X POST \
'https://api.sharely.ai/v1/workspaces/{workspaceId}/knowledge/{knowledgeId}/role' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"roleIds": ["role-uuid-from-creation"]
}'Error Handling
Role Not Found:
{
"error": "Role Not Found",
"message": "Customer role ID does not exist in this workspace"
}Solution: Ensure the role exists in your workspace before generating tokens. Use the Roles API to create the role first.
Invalid Customer Role ID Format:
{
"error": "Validation Error",
"message": "customerRoleId must contain only alphanumeric characters, hyphens, and underscores"
}Solution: Use valid characters only: a-z, A-Z, 0-9, -, _
Best Practices
1. Store Role Mappings in Configuration:
# .env file
SHARELY_ROLE_ADMIN=admin-role
SHARELY_ROLE_MANAGER=sales-manager
SHARELY_ROLE_VIEWER=content-viewer
SHARELY_ROLE_BASIC=basic-access2. Implement Token Caching:
let cachedAccessToken = null;
let tokenExpiry = null;
async function getCachedAccessToken(customerRoleId) {
if (cachedAccessToken && tokenExpiry && Date.now() < tokenExpiry) {
return cachedAccessToken;
}
const response = await generateAPIAccessToken(customerRoleId);
cachedAccessToken = response.token;
tokenExpiry = Date.now() + (23 * 60 * 60 * 1000); // 23 hours
return cachedAccessToken;
}3. Consistent User Identifiers:
// Always normalize user identifiers
const userId = user.email.toLowerCase();
const space = await createUserSpaceToken(apiToken, userId);4. Graceful Error Handling:
async function safeCreateRoleBoundToken(user) {
try {
const customerRoleId = getUserRole(user);
const apiToken = await generateAPIAccessToken(customerRoleId);
const userSpace = await createUserSpaceToken(apiToken.token, user.email);
return userSpace;
} catch (error) {
if (error.message.includes('Role Not Found')) {
console.error(`Role not found for user ${user.email}, falling back to basic access`);
// Fallback: generate token without role
const apiToken = await generateAPIAccessToken(null);
return await createUserSpaceToken(apiToken.token, user.email);
}
throw error;
}
}Backward Compatibility
Role-bound tokens are 100% backward compatible. Existing integrations work without changes:
// Existing code (no role) - still works
const response = await fetch(
`${apiUrl}/workspaces/${workspaceId}/generate-access-key-token`,
{
method: 'POST',
headers: { 'x-api-key': apiKey }
// No body = no role = full access (if RBAC disabled)
}
);Token Lifecycle
- API Access Token (Step 1): Valid for 24 hours
- User Space Token (Step 2): Valid for 24 hours
- Role Inheritance: Automatic from Step 1 to Step 2 via JWT metadata
- Role Changes: Require generating a new token
Future API Enhancements
The Sharely.ai API is actively being enhanced. Future versions will include:
Enhanced Token Generation Response:
{
"token": "...",
"customerRoleId": "sales-manager", // Explicitly returned
"expiresIn": "1d"
}Enhanced User Space Response:
{
"token": "...",
"space": { "id": "..." }, // Wrapped object
"user": { "id": "...", "email": "..." }, // User details
"temporalUserId": "...", // Temporal user ID
"customerRoleId": "..." // Inherited role
}These enhancements will make it easier to verify role assignment and debug authentication flows. Your current code will continue to work when these enhancements are released.
Related Documentation
- Roles API - Create and manage roles
- Knowledge Roles API - Assign knowledge to roles
- Categories API - Organize role-scoped content
- Authentication - General authentication guide
Examples
Basic: Activate Space with UUID
# Step 1: Get access token
ACCESS_TOKEN=$(curl -X POST \
'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
| jq -r '.token')
# Step 2: Activate or retrieve user space
curl -X PUT \
'https://api.sharely.ai/workspaces/your-workspace-id/activate-or-retrieve-user-space' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'organizationId: your-organization-id' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "your-workspace-id",
"userId": "550e8400-e29b-41d4-a716-446655440000"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"spaceId": "789e0123-f45a-67b8-c901-234567890def",
"userId": "550e8400-e29b-41d4-a716-446655440000",
"workspaceId": "your-workspace-id",
"isNew": true
}Using customerIdString (Email as User ID)
# Step 1: Get access token
ACCESS_TOKEN=$(curl -X POST \
'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
| jq -r '.token')
# Step 2: Activate with email as identifier
curl -X PUT \
'https://api.sharely.ai/workspaces/your-workspace-id/activate-or-retrieve-user-space' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'organizationId: your-organization-id' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "your-workspace-id",
"customerIdString": "john.doe@example.com"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"spaceId": "456e7890-a12b-34c5-d678-901234567890",
"userId": "john.doe@example.com",
"workspaceId": "your-workspace-id",
"isNew": false
}With RBAC: Using roleId
# Step 1: Get access token
ACCESS_TOKEN=$(curl -X POST \
'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
| jq -r '.token')
# Step 2: Activate with role-based access
curl -X PUT \
'https://api.sharely.ai/workspaces/your-workspace-id/activate-or-retrieve-user-space' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'organizationId: your-organization-id' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "your-workspace-id",
"customerIdString": "john.doe@example.com",
"roleId": "123e4567-e89b-12d3-a456-426614174000"
}'With RBAC: Using customerRoleId
# Step 1: Get access token
ACCESS_TOKEN=$(curl -X POST \
'https://api.sharely.ai/workspaces/your-workspace-id/generate-access-key-token' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
| jq -r '.token')
# Step 2: Activate with custom role identifier
curl -X PUT \
'https://api.sharely.ai/workspaces/your-workspace-id/activate-or-retrieve-user-space' \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H 'organizationId: your-organization-id' \
-H 'Content-Type: application/json' \
-d '{
"workspaceId": "your-workspace-id",
"customerIdString": "john.doe@example.com",
"customerRoleId": "sales-manager"
}'JavaScript Example
const API_KEY = 'sk-sharely-your-api-key';
const WORKSPACE_ID = 'your-workspace-id';
const ORGANIZATION_ID = 'your-organization-id';
const BASE_URL = 'https://api.sharely.ai';
async function activateOrRetrieveUserSpace(userId, options = {}) {
// Step 1: Generate access token
const accessTokenResponse = await fetch(
`${BASE_URL}/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
}
);
if (!accessTokenResponse.ok) {
throw new Error(`Failed to generate access token: ${accessTokenResponse.statusText}`);
}
const { token: accessToken } = await accessTokenResponse.json();
// Step 2: Activate or retrieve user space
const requestBody = {
workspaceId: WORKSPACE_ID,
};
// Add user identifier (UUID or custom string)
if (options.useCustomId) {
requestBody.customerIdString = userId;
} else {
requestBody.userId = userId;
}
// Add role if provided
if (options.roleId) {
requestBody.roleId = options.roleId;
} else if (options.customerRoleId) {
requestBody.customerRoleId = options.customerRoleId;
}
const response = await fetch(
`${BASE_URL}/workspaces/${WORKSPACE_ID}/activate-or-retrieve-user-space`,
{
method: 'PUT',
headers: {
'Authorization': `Bearer ${accessToken}`,
'organizationId': ORGANIZATION_ID,
'Content-Type': 'application/json'
},
body: JSON.stringify(requestBody)
}
);
if (!response.ok) {
throw new Error(`Failed to activate or retrieve space: ${response.statusText}`);
}
return await response.json();
}
// Usage examples:
// 1. Basic UUID-based user
const space1 = await activateOrRetrieveUserSpace('550e8400-e29b-41d4-a716-446655440000');
// 2. Using email as user identifier
const space2 = await activateOrRetrieveUserSpace('john.doe@example.com', {
useCustomId: true
});
// 3. With RBAC using role UUID
const space3 = await activateOrRetrieveUserSpace('john.doe@example.com', {
useCustomId: true,
roleId: '123e4567-e89b-12d3-a456-426614174000'
});
// 4. With RBAC using custom role identifier
const space4 = await activateOrRetrieveUserSpace('john.doe@example.com', {
useCustomId: true,
customerRoleId: 'sales-manager'
});
console.log('Space activated:', space4.spaceId);
console.log('User token:', space4.token);Python Example
import requests
API_KEY = 'sk-sharely-your-api-key'
WORKSPACE_ID = 'your-workspace-id'
ORGANIZATION_ID = 'your-organization-id'
BASE_URL = 'https://api.sharely.ai'
def activate_or_retrieve_user_space(user_id, use_custom_id=False, role_id=None, customer_role_id=None):
# Step 1: Generate access token
access_token_response = requests.post(
f'{BASE_URL}/workspaces/{WORKSPACE_ID}/generate-access-key-token',
headers={
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
)
access_token_response.raise_for_status()
access_token = access_token_response.json()['token']
# Step 2: Activate or retrieve user space
request_body = {
'workspaceId': WORKSPACE_ID,
}
# Add user identifier
if use_custom_id:
request_body['customerIdString'] = user_id
else:
request_body['userId'] = user_id
# Add role if provided
if role_id:
request_body['roleId'] = role_id
elif customer_role_id:
request_body['customerRoleId'] = customer_role_id
response = requests.put(
f'{BASE_URL}/workspaces/{WORKSPACE_ID}/activate-or-retrieve-user-space',
headers={
'Authorization': f'Bearer {access_token}',
'organizationId': ORGANIZATION_ID,
'Content-Type': 'application/json'
},
json=request_body
)
response.raise_for_status()
return response.json()
# Usage examples:
# 1. Basic UUID-based user
space1 = activate_or_retrieve_user_space('550e8400-e29b-41d4-a716-446655440000')
# 2. Using email as user identifier
space2 = activate_or_retrieve_user_space('john.doe@example.com', use_custom_id=True)
# 3. With RBAC using role UUID
space3 = activate_or_retrieve_user_space(
'john.doe@example.com',
use_custom_id=True,
role_id='123e4567-e89b-12d3-a456-426614174000'
)
# 4. With RBAC using custom role identifier
space4 = activate_or_retrieve_user_space(
'john.doe@example.com',
use_custom_id=True,
customer_role_id='sales-manager'
)
print(f"Space activated: {space4['spaceId']}")
print(f"User token: {space4['token']}")Error Responses
400 Bad Request
Missing or invalid request parameters:
{
"error": "Bad Request",
"message": "userId must be a valid UUID"
}Providing both userId and customerIdString:
{
"error": "Bad Request",
"message": "Provide only one of userId or customerIdString"
}Providing both roleId and customerRoleId:
{
"error": "Bad Request",
"message": "Provide only one of roleId or customerRoleId"
}401 Unauthorized
Invalid or expired access token:
{
"error": "Unauthorized",
"message": "Invalid or expired token"
}403 Forbidden
Insufficient permissions:
{
"error": "Forbidden",
"message": "Token does not have permission to access this workspace"
}404 Not Found
Workspace not found:
{
"error": "Not Found",
"message": "Workspace not found"
}Role not found (when using roleId or customerRoleId):
{
"error": "Not Found",
"message": "Role not found"
}500 Internal Server Error
Server error during space creation:
{
"error": "Internal Server Error",
"message": "Failed to create or retrieve user space"
}Use Cases
User Onboarding in SaaS Application
Integrate Sharely.ai into your SaaS application's login flow:
async function onboardUser(user) {
try {
// Use email as the user identifier
const space = await activateOrRetrieveUserSpace(user.email, {
useCustomId: true
});
if (space.isNew) {
// New user - initialize their space
console.log(`Created new space for user: ${user.email}`);
await initializeUserSpace(space.spaceId);
} else {
// Returning user
console.log(`Welcome back: ${user.email}`);
}
// Store space token for web control authentication
return {
spaceId: space.spaceId,
token: space.token
};
} catch (error) {
console.error('Failed to provision user space:', error);
throw error;
}
}Role-Based Access Control
Provision spaces with role-based access to control what content users can see:
async function provisionSpaceWithRole(user) {
const space = await activateOrRetrieveUserSpace(user.email, {
useCustomId: true,
customerRoleId: user.role // e.g., 'sales-manager', 'support-agent'
});
console.log(`Provisioned space with role: ${user.role}`);
return space;
}
// Users will only see knowledge items assigned to their role
const salesSpace = await provisionSpaceWithRole({
email: 'john@example.com',
role: 'sales-manager'
});Batch User Provisioning
Pre-provision spaces for multiple users:
async function provisionSpaces(users) {
const results = [];
for (const user of users) {
try {
const space = await activateOrRetrieveUserSpace(user.email, {
useCustomId: true,
customerRoleId: user.role
});
results.push({
userId: user.email,
spaceId: space.spaceId,
role: user.role,
isNew: space.isNew,
status: 'success'
});
} catch (error) {
results.push({
userId: user.email,
status: 'error',
error: error.message
});
}
// Rate limiting
await sleep(100);
}
return results;
}
const users = [
{ email: 'user1@example.com', role: 'viewer' },
{ email: 'user2@example.com', role: 'editor' },
{ email: 'user3@example.com', role: 'admin' }
];
const results = await provisionSpaces(users);
console.log(`Provisioned ${results.filter(r => r.status === 'success').length} spaces`);Best Practices
Token Caching
Cache the access key token to avoid repeated API calls:
let cachedAccessToken = null;
let tokenExpiry = null;
async function getCachedAccessToken() {
if (cachedAccessToken && tokenExpiry && Date.now() < tokenExpiry) {
return cachedAccessToken;
}
const response = await fetch(
`${BASE_URL}/workspaces/${WORKSPACE_ID}/generate-access-key-token`,
{
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
}
}
);
const data = await response.json();
cachedAccessToken = data.token;
// Cache for 23 hours (tokens typically valid for 24 hours)
tokenExpiry = Date.now() + (23 * 60 * 60 * 1000);
return cachedAccessToken;
}Space Token Storage
Store the returned space token securely for web control authentication:
// Server-side: Set HTTP-only cookies
res.setHeader('Set-Cookie', [
`sharely-token_${workspaceId}=${space.token}; Path=/; HttpOnly; Secure; SameSite=Strict`,
`sharely-space_${workspaceId}=${space.spaceId}; Path=/; HttpOnly; Secure; SameSite=Strict`
]);
// Or use the external token mode with jswebcontrol
window.sharelyai.setExternalToken(space.token);
window.sharelyai.setSpaceId(space.spaceId);Consistent User Identifiers
Always use the same identifier format for each user:
// Good - consistent identifier
const userId = user.email.toLowerCase();
const space = await activateOrRetrieveUserSpace(userId, { useCustomId: true });
// Avoid - inconsistent identifiers
const space1 = await activateOrRetrieveUserSpace('John@Example.com', { useCustomId: true });
const space2 = await activateOrRetrieveUserSpace('john@example.com', { useCustomId: true });
// These create different spaces!Error Handling
Handle errors gracefully, especially during user login:
async function safeActivateSpace(userId, options = {}) {
try {
return await activateOrRetrieveUserSpace(userId, options);
} catch (error) {
// Log error for debugging
console.error('Space activation failed:', {
userId,
error: error.message,
timestamp: new Date().toISOString()
});
// Return null or throw based on your error handling strategy
if (error.message.includes('Unauthorized')) {
throw new Error('Authentication failed. Please check your API credentials.');
}
// For other errors, you might want to retry
return null;
}
}Role Management with RBAC
When using RBAC, ensure roles exist before assigning them:
// Check if role exists first
async function activateSpaceWithValidatedRole(userId, customerRoleId) {
try {
// Attempt to get the role to validate it exists
const roleResponse = await fetch(
`${BASE_URL}/v1/workspaces/${WORKSPACE_ID}/role/by-customer-role-id/${customerRoleId}`,
{
headers: {
'x-api-key': API_KEY
}
}
);
if (!roleResponse.ok) {
console.warn(`Role ${customerRoleId} not found, provisioning without role`);
return await activateOrRetrieveUserSpace(userId, { useCustomId: true });
}
// Role exists, proceed with RBAC
return await activateOrRetrieveUserSpace(userId, {
useCustomId: true,
customerRoleId
});
} catch (error) {
console.error('Error validating role:', error);
throw error;
}
}Notes
Idempotency
This endpoint is idempotent. Calling it multiple times with the same user identifier will always return the same space. The isNew flag indicates whether the space was just created or already existed.
User Identification
- userId: Must be a valid UUID (e.g.,
550e8400-e29b-41d4-a716-446655440000) - customerIdString: Can be any string (email, username, custom ID, etc.)
- Choose one identifier type and use it consistently for each user across all sessions
Role-Based Access Control
When RBAC is enabled for your workspace:
- Users with a
roleIdorcustomerRoleIdwill only see knowledge items assigned to their role - Without a role, users see all knowledge items
- Roles must exist in your workspace before they can be assigned
- See the Roles API for managing roles
Token Validity
- The access key token (from Step 1) is typically valid for 24 hours
- The space token (from Step 2) is valid for the duration of the user's session
- Tokens should be securely stored and never exposed to client-side code
Space Lifecycle
- Spaces persist until explicitly deleted
- Users maintain conversation history and context across sessions
- Changing a user's role requires creating a new space activation
Concurrent Requests
The endpoint safely handles concurrent requests for the same user, ensuring only one space is created.