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-space

Authentication

This endpoint requires a two-step authentication process:

  1. Generate an API Key Token using your workspace API key
  2. Use the token to call this endpoint

See the authentication flow below.

Path Parameters

ParameterTypeRequiredDescription
workspaceIdstring (UUID)YesThe ID of your workspace

Headers

HeaderTypeRequiredDescription
AuthorizationstringYesBearer token from generate-access-key-token endpoint
organizationIdstring (UUID)YesYour organization ID
Content-TypestringYesMust be application/json

Request Body

FieldTypeRequiredDescription
workspaceIdstring (UUID)YesThe ID of your workspace (same as path parameter)
userIdstring (UUID)ConditionalUser's UUID. Required if customerIdString not provided
customerIdStringstringConditionalCustom user identifier (email, username, etc). Required if userId not provided
roleIdstring (UUID)NoRole UUID for RBAC. Mutually exclusive with customerRoleId
customerRoleIdstringNoCustom role identifier for RBAC. Mutually exclusive with roleId

Important Notes:

  • You must provide either userId (UUID) OR customerIdString (any string), but not both
  • If using RBAC, you can optionally provide either roleId (UUID) OR customerRoleId (string), but not both
  • The customerIdString allows 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

FieldTypeDescription
tokenstring (JWT)Access token for the user's space
spaceIdstring (UUID)Unique identifier for the space
userIdstringUser identifier provided in request
workspaceIdstring (UUID)Workspace ID from path parameter
isNewbooleantrue 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 Role

The 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 customerRoleId values
  • 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 role

What 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

FeatureCustomer Role IDRole ID (UUID)
Identifier TypeCustom 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
RecommendedYes - for most use casesOnly 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:

  1. Active Sharely Workspace with RBAC enabled (contact support@sharely.ai if needed)
  2. Workspace API Key (format: sk-sharely-...)
  3. Workspace ID and Organization ID (UUIDs)
  4. 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:

  1. Create roles with customer role IDs (e.g., "sales-manager", "admin-role")
  2. Assign permissions to roles (which knowledge items/categories they can access)
  3. 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-access

2. 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


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 roleId or customerRoleId will 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.