@digitalsamba/embedded-api-mcp-server
Version:
Digital Samba Embedded API MCP Server - Model Context Protocol server for Digital Samba's Embedded API
1,431 lines (1,430 loc) • 50.3 kB
JavaScript
/**
* Digital Samba API Client Module
*
* This module provides a comprehensive client for interacting with the Digital Samba API.
* It offers interfaces for all API entities and a client class that handles authentication,
* request processing, and provides methods for all available API endpoints.
*
* Key features include:
* - Authentication using either direct developer key or the ApiKeyContext for session-based auth
* - Comprehensive coverage of all Digital Samba API endpoints
* - Type-safe interfaces for request and response data
* - Error handling and logging
* - Support for pagination, filtering, and other API parameters
*
* @module digital-samba-api
* @author Digital Samba Team
* @version 0.1.0
*/
// Local modules
import apiKeyContext from "./auth.js";
import { ApiRequestError, ApiResponseError, AuthenticationError, ResourceNotFoundError, ValidationError, } from "./errors.js";
import logger from "./logger.js";
export class DigitalSambaApiClient {
/**
* Creates an instance of the Digital Samba API Client
*
* @constructor
* @param {string} [apiKey] - Optional developer key for direct authentication. If not provided,
* the client will use the ApiKeyContext for session-based authentication
* @param {string} [apiBaseUrl='https://api.digitalsamba.com/api/v1'] - Base URL for the Digital Samba API
* @example
* // Create a client with the default API URL
* const client = new DigitalSambaApiClient('your-developer-key');
*
* // Create a client with a custom API URL
* const customClient = new DigitalSambaApiClient('your-developer-key', 'https://custom-api.example.com/v1');
*/
constructor(apiKey, apiBaseUrl = "https://api.digitalsamba.com/api/v1", cache) {
// Store the developer key in ApiKeyContext if provided
if (apiKey) {
// For direct usage outside of MCP context
this._apiKey = apiKey;
}
this.apiBaseUrl = apiBaseUrl;
this.cache = cache;
}
/**
* Get the developer key from context or direct value
*
* This method retrieves the developer key using a prioritized approach:
* 1. First tries to get the developer key from the ApiKeyContext (for session-based auth)
* 2. If not found, falls back to using the direct developer key if provided during construction
* 3. If neither source provides a developer key, throws an AuthenticationError
*
* @protected
* @returns {string} The developer key to use for authentication
* @throws {AuthenticationError} If no developer key is available from any source
*/
getApiKey() {
// Try to get developer key from context first
const contextApiKey = apiKeyContext.getStore();
if (contextApiKey) {
return contextApiKey;
}
// Fall back to direct developer key if set
if (this._apiKey) {
return this._apiKey;
}
// No developer key available
throw new AuthenticationError("No developer key found in context or provided directly. Please include an Authorization header with a Bearer token.");
}
/**
* Make an authenticated request to the Digital Samba API
*
* This method handles all API requests including authentication, error handling, and response parsing.
* It automatically adds the Authorization header with the API key, logs request details (excluding sensitive
* information), and processes the response. It also handles special cases like 204 No Content responses and
* adds array-like properties to ApiResponse objects for easier consumption.
*
* @protected
* @template T - The expected response type
* @param {string} endpoint - The API endpoint path (without the base URL)
* @param {RequestInit} [options={}] - Request options, including method, body, and additional headers
* @returns {Promise<T>} A promise resolving to the parsed response data
* @throws {AuthenticationError} If no API key is available for authentication
* @throws {ApiRequestError} If a network error occurs during the request
* @throws {ApiResponseError} If the API returns a non-2xx status code
* @throws {ValidationError} If the API returns a 400 Bad Request with validation errors
* @throws {ResourceNotFoundError} If the API returns a 404 Not Found response
* @example
* // Example internal usage
* const rooms = await this.request<ApiResponse<Room>>('/rooms');
*/
async request(endpoint, options = {}) {
// Handle case where endpoint is already a full URL (starts with http:// or https://)
const url = endpoint.startsWith("http")
? endpoint
: `${this.apiBaseUrl}${endpoint}`;
const method = options.method || "GET";
const isCacheable = this.cache && method === "GET";
// Generate a cache key based on endpoint and API key (to avoid cross-client leakage)
const cacheNamespace = "api";
const cacheKey = endpoint;
// Check cache first for GET requests
if (isCacheable) {
const cachedResponse = this.cache.get(cacheNamespace, cacheKey);
if (cachedResponse) {
logger.debug(`Cache hit for ${endpoint}`);
// Metrics recording removed - metrics.js no longer exists
return cachedResponse.value;
}
else if (this.cache) {
// Metrics recording removed - metrics.js no longer exists
}
}
// Timer removed - no longer needed without metrics
try {
const apiKey = this.getApiKey();
const headers = {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
...options.headers,
};
// Log the request details (excluding sensitive info)
logger.debug(`Making API request to: ${url}`, {
method,
headers: { ...headers, Authorization: "[REDACTED]" },
cacheStatus: isCacheable ? "miss" : "disabled",
});
// Metrics tracking removed - metrics.js no longer exists
let response;
try {
response = await fetch(url, {
...options,
headers,
});
}
catch (error) {
// Handle network errors
logger.error("Network error in API request", {
url,
method,
error: error instanceof Error ? error.message : String(error),
});
// Metrics tracking removed - metrics.js no longer exists
throw new ApiRequestError(`Network error while connecting to Digital Samba API: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
}
// Log response details
logger.debug(`Response status: ${response.status} ${response.statusText}`);
// Status code handling - metrics removed
if (!response.ok) {
const errorText = await response.text();
logger.error(`API Error Response: ${errorText}`, {
status: response.status,
statusText: response.statusText,
});
// Metrics tracking removed - metrics.js no longer exists
// Parse error text as JSON if possible
let errorData;
try {
errorData = JSON.parse(errorText);
}
catch {
// Not JSON, use as plain text
errorData = { message: errorText };
}
// Handle specific error types based on status code
// This provides better error context for API consumers
if (response.status === 400) {
// Bad Request - typically validation errors
// Digital Samba API returns validation errors in the 'errors' field
const validationErrors = errorData.errors || {};
throw new ValidationError(`Validation error: ${errorData.message || errorText}`, { validationErrors: validationErrors });
}
else if (response.status === 401 || response.status === 403) {
// 401: Missing or invalid API key
// 403: Valid API key but insufficient permissions
throw new AuthenticationError(`Authentication error: ${errorData.message || errorText}`);
}
else if (response.status === 404) {
// Not Found error - resource doesn't exist
// Try to extract resource type and ID from the endpoint for future error enhancement
const matches = endpoint.match(/\/([^/]+)\/([^/]+)/);
// Resource type and ID extraction - currently unused but kept for future error details
void matches;
// For backwards compatibility with tests, throw a generic API error
throw new ApiResponseError(`Digital Samba API error (${response.status}): ${errorData.message || errorText}`, {
statusCode: response.status,
apiErrorMessage: errorData.message || errorText,
});
}
else {
// Generic API error
throw new ApiResponseError(`Digital Samba API error (${response.status}): ${errorData.message || errorText}`, {
statusCode: response.status,
apiErrorMessage: errorData.message || errorText,
apiErrorData: errorData,
});
}
}
// Return empty object for 204 No Content responses
if (response.status === 204) {
// Metrics tracking removed - metrics.js no longer exists
return {};
}
// Get response text first to check if it's empty
const responseText = await response.text();
// Handle empty response bodies (some endpoints return 200 with empty body or {})
if (!responseText || responseText.trim() === '') {
logger.debug(`Empty response body for ${endpoint}`);
return {};
}
// Parse the JSON response
let responseData;
try {
responseData = JSON.parse(responseText);
}
catch (parseError) {
logger.error(`Failed to parse JSON response for ${endpoint}`, {
responseText,
error: parseError instanceof Error ? parseError.message : String(parseError),
});
throw new ApiResponseError(`Invalid JSON response from Digital Samba API: ${parseError instanceof Error ? parseError.message : String(parseError)}`, {
statusCode: response.status,
apiErrorMessage: `Failed to parse JSON: ${responseText}`,
apiErrorData: { responseText },
});
}
// Add array-like properties to ApiResponse objects
if (responseData &&
responseData.data &&
Array.isArray(responseData.data)) {
// Add length property
responseData.length = responseData.data.length;
// Add map function that forwards to the data array
responseData.map = function (callback) {
return this.data.map(callback);
};
}
// Metrics tracking removed - metrics.js no longer exists
// Store successful GET responses in cache
if (isCacheable) {
logger.debug(`Caching response for ${endpoint}`);
this.cache.set(cacheNamespace, cacheKey, responseData);
// Metrics tracking removed - metrics.js no longer exists
}
return responseData;
}
catch (error) {
// Metrics tracking removed - metrics.js no longer exists
// Catch and re-throw errors that aren't already one of our custom types
if (!(error instanceof AuthenticationError) &&
!(error instanceof ApiRequestError) &&
!(error instanceof ApiResponseError) &&
!(error instanceof ValidationError) &&
!(error instanceof ResourceNotFoundError)) {
logger.error("Unexpected error in API request", {
url,
method: options.method || "GET",
error: error instanceof Error ? error.message : String(error),
});
// Metrics tracking removed - metrics.js no longer exists
throw new ApiRequestError(`Unexpected error in Digital Samba API request: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
}
// Re-throw custom error types
throw error;
}
}
// Default Room Settings
/**
* Get default room settings
*/
async getDefaultRoomSettings() {
return this.request("/");
}
/**
* Update default room settings
*/
async updateDefaultRoomSettings(settings) {
return this.request("/", {
method: "PATCH",
body: JSON.stringify(settings),
});
}
// Rooms
/**
* List all rooms
*
* Retrieves a paginated list of all rooms in your Digital Samba account.
* Supports pagination, filtering, and sorting options.
*
* @param {PaginationParams} [params] - Optional pagination parameters
* @param {number} [params.limit] - Number of items per page (default: 10)
* @param {number} [params.offset] - Number of items to skip
* @param {'asc'|'desc'} [params.order] - Sort order
* @param {string} [params.after] - Cursor for pagination
* @returns {Promise<ApiResponse<Room>>} Paginated list of rooms
*
* @example
* // Get first 20 rooms
* const rooms = await client.listRooms({ limit: 20 });
*
* @example
* // Get next page
* const nextPage = await client.listRooms({
* limit: 20,
* after: rooms.data[rooms.data.length - 1].id
* });
*/
async listRooms(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms${query}`);
}
/**
* Get details for a specific room
*/
async getRoom(roomId) {
return this.request(`/rooms/${roomId}`);
}
/**
* Create a new room
*
* Creates a new video conferencing room with the specified settings.
* Only the 'name' field is required; all other settings are optional.
*
* @param {RoomCreateSettings} settings - Room configuration
* @param {string} settings.name - Room name (required)
* @param {string} [settings.description] - Room description
* @param {'public'|'private'} [settings.privacy] - Room privacy setting
* @param {number} [settings.max_participants] - Maximum participants (2-2000)
* @param {string} [settings.friendly_url] - Custom URL slug
* @returns {Promise<Room>} The created room object
* @throws {ValidationError} If required fields are missing or invalid
* @throws {ApiResponseError} If room creation fails
*
* @example
* // Create a basic room
* const room = await client.createRoom({
* name: 'Team Standup'
* });
*
* @example
* // Create a fully configured room
* const room = await client.createRoom({
* name: 'All Hands Meeting',
* description: 'Monthly company meeting',
* privacy: 'private',
* max_participants: 200,
* friendly_url: 'all-hands',
* recordings_enabled: true,
* chat_enabled: true
* });
*/
async createRoom(settings) {
// Make sure name is defined (it's required by the API)
const roomSettings = {
...settings,
name: settings.name || "New Meeting Room",
};
return this.request("/rooms", {
method: "POST",
body: JSON.stringify(roomSettings),
});
}
/**
* Update an existing room
*/
async updateRoom(roomId, settings) {
return this.request(`/rooms/${roomId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a room
*/
async deleteRoom(roomId, options) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
return this.request(`/rooms/${roomId}`, {
method: "DELETE",
body: options ? JSON.stringify(options) : undefined,
});
}
/**
* Generate a token for joining a room
*
* Creates a secure access token that allows a user to join a specific room.
* Tokens can include user information, roles, and expiration settings.
*
* @param {string} roomId - The ID of the room to generate a token for
* @param {TokenOptions} options - Token configuration options
* @param {string} [options.u] - User name to display
* @param {string} [options.ud] - External user identifier
* @param {string} [options.role] - User role (e.g., 'moderator', 'participant')
* @param {string} [options.avatar] - URL to user's avatar image
* @param {string} [options.exp] - Token expiration in minutes
* @returns {Promise<TokenResponse>} Object containing token and join link
*
* @example
* // Generate a basic participant token
* const { token, link } = await client.generateRoomToken('room-123', {
* u: 'John Doe'
* });
*
* @example
* // Generate a moderator token with expiration
* const { token, link } = await client.generateRoomToken('room-123', {
* u: 'Jane Smith',
* ud: 'user-456',
* role: 'moderator',
* exp: '120' // Expires in 2 hours
* });
*/
async generateRoomToken(roomId, options) {
return this.request(`/rooms/${roomId}/token`, {
method: "POST",
body: JSON.stringify(options),
});
}
/**
* Delete all resources for a room
*/
async deleteRoomResources(roomId) {
await this.request(`/rooms/${roomId}/resources`, {
method: "DELETE",
});
}
// Live Participants
/**
* Get rooms with live participants count
*/
async getLiveRooms(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/live${query}`);
}
/**
* Get rooms with live participants data
*/
async getLiveRoomsWithParticipants(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/live/participants${query}`);
}
/**
* Get single room with live participants count
*/
async getRoomLiveParticipantsCount(roomId) {
return this.request(`/rooms/${roomId}/live`);
}
/**
* Get single room with live participants data
*/
async getRoomLiveParticipantsData(roomId) {
return this.request(`/rooms/${roomId}/live/participants`);
}
// Participants
/**
* List all participants
*/
async listParticipants(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/participants${query}`);
}
/**
* Get details for a specific participant
*/
async getParticipant(participantId) {
return this.request(`/participants/${participantId}`);
}
/**
* List participants in a room
*/
async listRoomParticipants(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/participants${query}`);
}
/**
* List participants in a session
*/
async listSessionParticipants(sessionId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/sessions/${sessionId}/participants${query}`);
}
/**
* Phone participants joined
*/
async phoneParticipantsJoined(roomId, participants) {
await this.request(`/rooms/${roomId}/phone-participants/joined`, {
method: "POST",
body: JSON.stringify(participants),
});
}
/**
* Phone participants left
*/
async phoneParticipantsLeft(roomId, callIds) {
await this.request(`/rooms/${roomId}/phone-participants/left`, {
method: "POST",
body: JSON.stringify(callIds),
});
}
// Recordings
/**
* List all recordings
*/
async listRecordings(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/recordings${query}`);
}
/**
* List archived recordings
*/
async listArchivedRecordings(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/recordings/archived${query}`);
}
/**
* Get a specific recording
*/
async getRecording(recordingId) {
return this.request(`/recordings/${recordingId}`);
}
/**
* Delete a recording
*/
async deleteRecording(recordingId) {
await this.request(`/recordings/${recordingId}`, {
method: "DELETE",
});
}
/**
* Get a download link for a recording
*/
async getRecordingDownloadLink(recordingId, validForMinutes) {
const queryParams = new URLSearchParams();
if (validForMinutes !== undefined) {
queryParams.append("valid_for_minutes", String(validForMinutes));
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/recordings/${recordingId}/download${query}`);
}
/**
* Archive a recording
*/
async archiveRecording(recordingId) {
await this.request(`/recordings/${recordingId}/archive`, {
method: "POST",
});
}
/**
* Unarchive a recording
*/
async unarchiveRecording(recordingId) {
await this.request(`/recordings/${recordingId}/unarchive`, {
method: "POST",
});
}
/**
* Start recording in a room
*/
async startRecording(roomId) {
await this.request(`/rooms/${roomId}/recordings/start`, {
method: "POST",
});
}
/**
* Stop recording in a room
*/
async stopRecording(roomId) {
await this.request(`/rooms/${roomId}/recordings/stop`, {
method: "POST",
});
}
/**
* Start transcription in a room
*/
async startTranscription(roomId) {
await this.request(`/rooms/${roomId}/transcription/start`, {
method: "POST",
});
}
/**
* Stop transcription in a room
*/
async stopTranscription(roomId) {
await this.request(`/rooms/${roomId}/transcription/stop`, {
method: "POST",
});
}
// Webhooks
/**
* List available event types for webhooks
*/
async listWebhookEvents() {
return this.request("/events");
}
/**
* List all webhooks
*/
async listWebhooks(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/webhooks${query}`);
}
/**
* Create a new webhook
*/
async createWebhook(settings) {
return this.request("/webhooks", {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific webhook
*/
async getWebhook(webhookId) {
return this.request(`/webhooks/${webhookId}`);
}
/**
* Update a webhook
*/
async updateWebhook(webhookId, settings) {
return this.request(`/webhooks/${webhookId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a webhook
*/
async deleteWebhook(webhookId) {
await this.request(`/webhooks/${webhookId}`, {
method: "DELETE",
});
}
// Roles and Permissions
/**
* List all roles
*/
async listRoles(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/roles${query}`);
}
/**
* Create a new role
*/
async createRole(settings) {
return this.request("/roles", {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific role
*/
async getRole(roleId) {
return this.request(`/roles/${roleId}`);
}
/**
* Update a role
*/
async updateRole(roleId, settings) {
return this.request(`/roles/${roleId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a role
*/
async deleteRole(roleId) {
await this.request(`/roles/${roleId}`, {
method: "DELETE",
});
}
/**
* List all available permissions
*/
async listPermissions() {
return this.request("/permissions");
}
// Sessions
/**
* List all sessions
*/
async listSessions(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/sessions${query}`);
}
/**
* List sessions for a specific room
*/
async listRoomSessions(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/sessions${query}`);
}
/**
* Get session statistics
*/
async getSessionStatistics(sessionId, metrics) {
const queryParams = new URLSearchParams();
if (metrics) {
queryParams.append("metrics", metrics);
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/sessions/${sessionId}${query}`);
}
/**
* End a live session
*/
async endSession(sessionId) {
await this.request(`/sessions/${sessionId}/end`, {
method: "POST",
});
}
/**
* Get session summary
*/
async getSessionSummary(sessionId) {
return this.request(`/sessions/${sessionId}/summary`);
}
/**
* Delete session data
*/
async deleteSessionData(sessionId, dataType) {
await this.request(`/sessions/${sessionId}/${dataType}`, {
method: "DELETE",
});
}
// Chat and Q&A
/**
* Get chat messages
*/
async getChatMessages(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/chat${query}`);
}
/**
* Delete chat messages
*/
async deleteChatMessages(roomId) {
await this.request(`/rooms/${roomId}/chat`, {
method: "DELETE",
});
}
/**
* Get Q&A
*/
async getQuestionsAndAnswers(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/questions${query}`);
}
/**
* Delete Q&A
*/
async deleteQA(roomId) {
await this.request(`/rooms/${roomId}/questions`, {
method: "DELETE",
});
}
// Polls
/**
* Get polls
*/
async getPolls(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/polls${query}`);
}
/**
* Create a poll
*/
async createPoll(roomId, settings) {
return this.request(`/rooms/${roomId}/polls`, {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific poll
*/
async getPoll(roomId, pollId) {
return this.request(`/rooms/${roomId}/polls/${pollId}`);
}
/**
* Update a poll
*/
async updatePoll(roomId, pollId, settings) {
return this.request(`/rooms/${roomId}/polls/${pollId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a poll
*/
async deletePoll(roomId, pollId) {
await this.request(`/rooms/${roomId}/polls/${pollId}`, {
method: "DELETE",
});
}
/**
* Get poll results
*/
async getPollResults(roomId, pollId, sessionId) {
const queryParams = new URLSearchParams();
if (sessionId) {
queryParams.append("session_id", sessionId);
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/polls/${pollId}/results${query}`);
}
/**
* Export polls
*/
async exportPolls(roomId, options) {
const queryParams = new URLSearchParams();
if (options) {
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
const apiKey = this.getApiKey();
const response = await fetch(`${this.apiBaseUrl}/rooms/${roomId}/polls/export${query}`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Digital Samba API error (${response.status}): ${errorText}`);
}
return response.text();
}
/**
* Export chat messages
*/
async exportChatMessages(roomId, options) {
const queryParams = new URLSearchParams();
if (options) {
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
const apiKey = this.getApiKey();
const response = await fetch(`${this.apiBaseUrl}/rooms/${roomId}/chat/export${query}`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Digital Samba API error (${response.status}): ${errorText}`);
}
return response.text();
}
/**
* Export Q&A (questions and answers)
*/
async exportQA(roomId, options) {
const queryParams = new URLSearchParams();
if (options) {
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
const apiKey = this.getApiKey();
const response = await fetch(`${this.apiBaseUrl}/rooms/${roomId}/questions/export${query}`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Digital Samba API error (${response.status}): ${errorText}`);
}
return response.text();
}
/**
* Export session transcripts
*/
async exportTranscripts(sessionId, options) {
const queryParams = new URLSearchParams();
if (options) {
Object.entries(options).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
const apiKey = this.getApiKey();
const response = await fetch(`${this.apiBaseUrl}/sessions/${sessionId}/transcripts/export${query}`, {
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Digital Samba API error (${response.status}): ${errorText}`);
}
return response.text();
}
// Libraries
/**
* List all libraries
*/
async listLibraries(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/libraries${query}`);
}
/**
* Create a new library
*/
async createLibrary(settings) {
return this.request("/libraries", {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific library
*/
async getLibrary(libraryId) {
return this.request(`/libraries/${libraryId}`);
}
/**
* Update a library
*/
async updateLibrary(libraryId, settings) {
return this.request(`/libraries/${libraryId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a library
*/
async deleteLibrary(libraryId) {
await this.request(`/libraries/${libraryId}`, {
method: "DELETE",
});
}
/**
* Get library hierarchy
*/
async getLibraryHierarchy(libraryId) {
return this.request(`/libraries/${libraryId}/hierarchy`);
}
/**
* List library folders
*/
async listLibraryFolders(libraryId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/libraries/${libraryId}/folders${query}`);
}
/**
* Create a library folder
*/
async createLibraryFolder(libraryId, settings) {
return this.request(`/libraries/${libraryId}/folders`, {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific library folder
*/
async getLibraryFolder(libraryId, folderId) {
return this.request(`/libraries/${libraryId}/folders/${folderId}`);
}
/**
* Update a library folder
*/
async updateLibraryFolder(libraryId, folderId, settings) {
return this.request(`/libraries/${libraryId}/folders/${folderId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a library folder
*/
async deleteLibraryFolder(libraryId, folderId) {
await this.request(`/libraries/${libraryId}/folders/${folderId}`, {
method: "DELETE",
});
}
/**
* List library files
*/
async listLibraryFiles(libraryId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/libraries/${libraryId}/files${query}`);
}
/**
* Create a new library file (get upload URL and token)
*/
async createLibraryFile(libraryId, settings) {
return this.request(`/libraries/${libraryId}/files`, {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Get a specific library file
*/
async getLibraryFile(libraryId, fileId) {
return this.request(`/libraries/${libraryId}/files/${fileId}`);
}
/**
* Update a library file
*/
async updateLibraryFile(libraryId, fileId, settings) {
return this.request(`/libraries/${libraryId}/files/${fileId}`, {
method: "PATCH",
body: JSON.stringify(settings),
});
}
/**
* Delete a library file
*/
async deleteLibraryFile(libraryId, fileId) {
await this.request(`/libraries/${libraryId}/files/${fileId}`, {
method: "DELETE",
});
}
/**
* Get file links
*/
async getFileLinks(libraryId, fileId) {
return this.request(`/libraries/${libraryId}/files/${fileId}/links`);
}
// Webapps
/**
* Create a webapp in a library
*/
async createWebapp(libraryId, settings) {
return this.request(`/libraries/${libraryId}/webapps`, {
method: "POST",
body: JSON.stringify(settings),
});
}
// Whiteboards
/**
* Create a whiteboard in a library
*/
async createWhiteboard(libraryId, settings) {
return this.request(`/libraries/${libraryId}/whiteboards`, {
method: "POST",
body: JSON.stringify(settings),
});
}
// Statistics
/**
* Get team global statistics by period
*/
async getTeamStatistics(params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/statistics${query}`);
}
/**
* Get team global statistics by current period
*/
async getTeamCurrentStatistics(metrics) {
const queryParams = new URLSearchParams();
if (metrics) {
queryParams.append("metrics", metrics);
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/statistics/team/current${query}`);
}
/**
* Get team statistics for current period (simplified)
*/
async getSimplifiedTeamCurrentStatistics() {
return this.request("/statistics/current");
}
/**
* Get room statistics by period
*/
async getRoomStatistics(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/statistics${query}`);
}
/**
* Get room statistics for current period
*/
async getRoomCurrentStatistics(roomId, metrics) {
const queryParams = new URLSearchParams();
if (metrics) {
queryParams.append("metrics", metrics);
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/statistics/current${query}`);
}
/**
* Get participant statistics
*/
async getParticipantStatistics(participantId) {
return this.request(`/participants/${participantId}/statistics`);
}
// Breakout Rooms
/**
* List breakout rooms for a parent room
*/
async listBreakoutRooms(roomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/breakout-rooms${query}`);
}
/**
* Get a specific breakout room
*/
async getBreakoutRoom(roomId, breakoutRoomId) {
return this.request(`/rooms/${roomId}/breakout-rooms/${breakoutRoomId}`);
}
/**
* Create breakout rooms
*/
async createBreakoutRooms(roomId, settings) {
return this.request(`/rooms/${roomId}/breakout-rooms`, {
method: "POST",
body: JSON.stringify(settings),
});
}
/**
* Delete a breakout room
*/
async deleteBreakoutRoom(roomId, breakoutRoomId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/rooms/${roomId}/breakout-rooms/${breakoutRoomId}`, {
method: "DELETE",
});
}
/**
* Delete all breakout rooms
*/
async deleteAllBreakoutRooms(roomId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/rooms/${roomId}/breakout-rooms`, {
method: "DELETE",
});
}
/**
* List participants in a breakout room
*/
async listBreakoutRoomParticipants(roomId, breakoutRoomId, params) {
const queryParams = new URLSearchParams();
if (params) {
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryParams.append(key, String(value));
}
});
}
const query = queryParams.toString() ? `?${queryParams.toString()}` : "";
return this.request(`/rooms/${roomId}/breakout-rooms/${breakoutRoomId}/participants${query}`);
}
/**
* Assign participants to breakout rooms
*/
async assignParticipantsToBreakoutRooms(roomId, assignments) {
await this.request(`/rooms/${roomId}/breakout-rooms/assignments`, {
method: "POST",
body: JSON.stringify(assignments),
});
}
/**
* Return all participants to the main room
*/
async returnAllParticipantsToMainRoom(roomId) {
await this.request(`/rooms/${roomId}/breakout-rooms/return-all`, {
method: "POST",
});
}
/**
* Broadcast message to all breakout rooms
*/
async broadcastToBreakoutRooms(roomId, options) {
await this.request(`/rooms/${roomId}/breakout-rooms/broadcast`, {
method: "POST",
body: JSON.stringify(options),
});
}
/**
* Open breakout rooms (start breakout sessions)
*/
async openBreakoutRooms(roomId) {
await this.request(`/rooms/${roomId}/breakout-rooms/open`, {
method: "POST",
});
}
/**
* Close breakout rooms
*/
async closeBreakoutRooms(roomId) {
await this.request(`/rooms/${roomId}/breakout-rooms/close`, {
method: "POST",
});
}
// Communication Management Methods
/**
* Delete session chats
*/
async deleteSessionChats(sessionId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/sessions/${sessionId}/chat`, {
method: "DELETE",
});
}
/**
* Delete session Q&A
*/
async deleteSessionQA(sessionId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/sessions/${sessionId}/questions`, {
method: "DELETE",
});
}
/**
* Delete session summaries
*/
async deleteSessionSummaries(sessionId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/sessions/${sessionId}/summaries`, {
method: "DELETE",
});
}
/**
* Delete session polls
*/
async deleteSessionPolls(sessionId) {
// Invalidate cache when deleting resources
if (this.cache) {
this.cache.invalidateNamespace("api");
}
await this.request(`/sessions/${sessionId}/polls`, {