Framework-agnostic HTTP client and types for Hiyve Cloud AI services. Works with any JavaScript runtime -- Node.js, browsers, Deno, or edge functions. For React applications, use @hiyve/react-intelligence which wraps this package with React context and hooks.
npm install @hiyve/cloud
import { CloudClient } from '@hiyve/cloud';
// Create a client with a token generator (most common pattern)
const client = new CloudClient({
generateCloudToken: async () => {
const res = await fetch('/api/generate-cloud-token', { method: 'POST' });
const data = await res.json();
return data.cloudToken;
},
});
// AI query
const response = await client.query('Summarize the discussion', {
userId: 'user@example.com',
roomName: 'team-standup',
});
console.log(response.content);
// Generate a meeting summary
const summary = await client.getSummary(transcript, {
userId: 'user@example.com',
maxLength: 500,
});
// Extract action items
const items = await client.getActionItems(transcript, {
userId: 'user@example.com',
});
CloudClient requires either a cloudToken or a generateCloudToken function. Tokens are generated lazily on first request and automatically refreshed when expired.
Provide a function that fetches a cloud token from your backend. Tokens are generated lazily on first request and automatically refreshed when expired. When your server uses @hiyve/admin middleware, your endpoint can delegate to the built-in token route.
const client = new CloudClient({
generateCloudToken: async () => {
const res = await fetch('/api/generate-cloud-token', { method: 'POST' });
return (await res.json()).cloudToken;
},
});
Your generator must return a real token or reject. Resolving with an empty string is rejected as an error — to signal "this user cannot use cloud features", throw or reject so the calling code can handle it.
A pre-generated short-lived token (prefixed ct_). Obtain from your backend by calling POST https://cloud.hiyve.io/cloud-token with your API key.
const client = new CloudClient({
cloudToken: 'ct_abc123...',
});
You can update the token later with client.setCloudToken(newToken) or force a refresh with client.clearToken().
When the API rate limits a request, the client pauses further requests to that
endpoint until the limit window passes, honouring the Retry-After the server
sends. Calls made during the pause reject immediately, with the remaining wait
in the error message, rather than being sent and rejected again.
The pause applies per endpoint, so reaching a limit on one (for example an AI quota) does not affect the rest. It lasts until the window the server asked for has passed.
If you call the API on a timer, catch and skip — there is no need to add your own backoff:
try {
await client.query('summarise the last hour', { userId });
} catch (err) {
// Includes rate-limit pauses; the next tick will retry once the window passes.
}
new CloudClient(config: CloudConfig)
| Option | Type | Default | Description |
|---|---|---|---|
cloudToken |
string |
- | Pre-generated cloud token |
generateCloudToken |
(params) => Promise<CloudTokenResponse> |
- | Token generator function |
userId |
string |
- | User email for token generation. Required when using generateCloudToken |
environment |
'production' | 'development' |
'production' |
Target environment |
baseUrl |
string |
- | Custom API URL (overrides environment) |
timeout |
number |
30000 |
Request timeout in milliseconds |
presence |
boolean | { intervalMs?: number } |
false |
Enable automatic presence heartbeat. true uses 10s interval; pass { intervalMs } to customize |
| Method | Description |
|---|---|
setCloudToken(token) |
Update the cached cloud token |
clearToken() |
Clear cached token, forcing re-generation on next request |
setUserId(userId) |
Set or change the current user's email. Forces a token refresh on next request |
query(text, options): Promise<AIResponse>Send a general AI query.
const response = await client.query('What were the key takeaways?', {
userId: 'user@example.com',
roomName: 'team-standup',
model: 'gpt-4o-mini',
systemPrompt: 'You are a meeting analyst.',
context: 'Additional context here...',
});
console.log(response.content);
getSummary(transcript, options): Promise<string>Generate a summary from a transcript.
const summary = await client.getSummary(transcript, {
userId: 'user@example.com',
maxLength: 200,
});
getActionItems(transcript, options): Promise<ActionItem[]>Extract action items from a transcript.
const items = await client.getActionItems(transcript, {
userId: 'user@example.com',
});
items.forEach(item => {
console.log(`[${item.priority}] ${item.action} (${item.category})`);
});
enhanceNote(text, options): Promise<string>Enhance or polish text content.
const enhanced = await client.enhanceNote(roughNotes, {
userId: 'user@example.com',
style: 'professional', // 'professional' | 'casual' | 'academic' etc.
});
queryResponse(responseId, userId, query, options?): Promise<AIResponse>Query against accumulated meeting context identified by a response ID.
const response = await client.queryResponse(
'resp_abc123',
'user@example.com',
'Summarize the first 10 minutes.'
);
pushMoodToResponse(responseId, userId, sentimentContext): Promise<{ responseId: string }>Push mood data to a specific response context.
await client.pushMoodToResponse('resp_abc123', 'user@example.com', [
{ participant: 'user@example.com', sentiment: 'positive', confidence: 0.9 },
]);
getAlertSettings(roomId): Promise<AlertSettings>Retrieve alert settings for a room.
updateAlertSettings(roomId, settings): Promise<AlertSettings>Update alert settings for a room.
const updated = await client.updateAlertSettings('room-123', {
enabled: true,
audioAlerts: false,
categories: {
action_item: { enabled: true, sensitivity: 'high', minConfidence: 0.6 },
},
});
getAlertHistory(roomId, options?): Promise<MeetingAlert[]>Retrieve alert history with optional filtering.
const alerts = await client.getAlertHistory('room-123', {
limit: 50,
priority: 'high',
type: 'action_item',
since: '2024-01-01T00:00:00Z',
});
acknowledgeAlert(alertId, note?): Promise<MeetingAlert>Acknowledge a meeting alert.
await client.acknowledgeAlert('alert-456', 'Reviewed and assigned to team.');
createMeeting(options): Promise<Meeting>Create a scheduled meeting.
const meeting = await client.createMeeting({
title: 'Weekly Standup',
startTime: new Date('2024-06-01T09:00:00Z'),
endTime: new Date('2024-06-01T09:30:00Z'),
timezone: 'America/New_York',
participants: [
{ email: 'john@example.com', name: 'John', role: 'organizer' },
{ email: 'jane@example.com', name: 'Jane', role: 'attendee' },
],
recurrence: { pattern: 'weekly', interval: 1 },
});
getMeetings(options?): Promise<Meeting[]>List meetings with optional filters.
const meetings = await client.getMeetings({ limit: 20, status: 'scheduled' });
getMeeting(meetingId): Promise<Meeting>Get details for a specific meeting.
updateMeeting(meetingId, updates): Promise<Meeting>Update a meeting.
await client.updateMeeting('meeting-123', { title: 'Updated Standup' });
cancelMeeting(meetingId): Promise<void>Cancel a meeting.
sendMeetingNotification(meetingId, type?): Promise<void>Send a notification to meeting participants.
await client.sendMeetingNotification('meeting-123', 'reminder');
// type: 'reminder' | 'invitation' | 'update' | 'cancellation'
Semantic search across meeting transcriptions and documents.
search(query, options?): Promise<SearchResults>Search across all sources.
const results = await client.search('budget discussion', {
userId: 'user@example.com',
roomNames: ['team-standup'],
limit: 10,
threshold: 0.6,
});
results.results.forEach(r => {
console.log(`[${r.source}] ${r.content} (score: ${r.similarity})`);
});
searchTranscriptions(query, options?): Promise<SearchResults>Search within transcriptions only.
searchDocuments(query, options?): Promise<SearchResults>Search within documents only.
ask(query, options?): Promise<AskResponse>Search for relevant context, then generate an AI answer from the results.
const answer = await client.ask('What was decided about the launch date?', {
userId: 'user@example.com',
roomNames: ['product-review'],
model: 'gpt-4o-mini',
});
console.log(answer.content);
console.log(`Sources: ${answer.sources.length}`);
SearchOptions
| Option | Type | Default | Description |
|---|---|---|---|
sources |
('transcriptions' | 'documents')[] |
all | Filter by source type |
roomNames |
string[] |
- | Filter by room names |
dateRange |
{ start?, end? } |
- | Filter by date range |
limit |
number |
20 |
Maximum results |
threshold |
number |
0.5 |
Minimum similarity score (0-1) |
userId |
string |
- | User ID for scoping |
roomName |
string |
- | Room name for scoping |
recordingId |
string |
- | Recording ID for scoping |
Analyze meeting transcripts with purpose-built scorecards.
analyzeSalesCall(options): Promise<AnalysisResponse<SalesScorecard>>Score a sales call across discovery, qualification, objection handling, closing, and next steps.
const result = await client.analyzeSalesCall({
transcript,
userId: 'user@example.com',
});
console.log(`Overall: ${result.analysis.overallScore}/100`);
console.log(`Discovery: ${result.analysis.dimensions.discovery.score}/100`);
analyzeInterview(options): Promise<AnalysisResponse<InterviewAssessment>>Assess an interview across technical competency, communication, problem solving, culture fit, and enthusiasm.
const result = await client.analyzeInterview({ transcript, userId: 'user@example.com' });
console.log(`Recommendation: ${result.analysis.hiringRecommendation}`);
analyzeSession(options): Promise<AnalysisResponse<SessionSummary>>Summarize a session including primary concerns, topics discussed, recommendations, and follow-ups.
analyzeCoaching(options): Promise<AnalysisResponse<CoachingAnalysis>>Analyze communication patterns including talk/listen ratio, filler words, interruptions, and question quality.
analyzeCustom(options): Promise<AnalysisResponse<CustomAnalysis>>Run a custom analysis with your own rubric dimensions.
const result = await client.analyzeCustom({
transcript,
userId: 'user@example.com',
rubric: {
dimensions: [
{ name: 'Clarity', description: 'How clearly were points communicated?' },
{ name: 'Engagement', description: 'How engaged were participants?' },
],
},
});
console.log(`Clarity: ${result.analysis.dimensions['Clarity'].score}/100`);
AnalysisOptions
| Option | Type | Description |
|---|---|---|
transcript |
string |
Meeting transcript text (required) |
userId |
string |
User ID |
roomName |
string |
Room name |
model |
string |
AI model override |
checkPresence(callerProfileId, match): Promise<string[]>Check which connected users match a metadata-based relationship with the caller. The server derives match values from the caller's profile.
const onlineUserIds = await client.checkPresence('profile_123', {
userField: 'organizationId',
callerField: 'organizationId',
});
console.log('Online teammates:', onlineUserIds);
Advertise rooms and discover active rooms in real time.
advertiseRoom(options): Promise<void>Make a room discoverable by targeted users.
await client.advertiseRoom({
name: 'team-standup',
ownerDisplayName: 'John',
targetUserIds: ['jane@example.com', 'bob@example.com'],
metadata: { passwordRequired: false },
});
removeAdvertisedRoom(roomName): Promise<void>Remove a room from discovery.
updateAdvertisedRoom(roomName, updates): Promise<void>Update an advertised room's metadata or target users.
getActiveRooms(userId?): Promise<ActiveRoom[]>List active rooms. When userId is provided, only rooms targeting that user are returned.
const rooms = await client.getActiveRooms('jane@example.com');
rooms.forEach(room => {
console.log(`${room.name} by ${room.ownerDisplayName}`);
});
getActiveRoomsStreamUrl(userId): Promise<string>Get a signed SSE stream URL for active room discovery. The URL includes a single-use ticket for authentication (since EventSource does not support custom headers).
const url = await client.getActiveRoomsStreamUrl('jane@example.com');
const eventSource = new EventSource(url);
connectActiveRoomsStream(userId, handlers?): Promise<ActiveRoomsStreamHandle>Open a real-time stream for active room updates. Returns a handle with close() to disconnect.
const stream = await client.connectActiveRoomsStream('jane@example.com', {
onSnapshot: (rooms) => console.log('All rooms:', rooms),
onRoomAdded: (room) => console.log('New room:', room.name),
onRoomRemoved: (name) => console.log('Room closed:', name),
onRoomUpdated: (room) => console.log('Room updated:', room.name),
onError: (err) => console.error('Stream error:', err),
});
// Later: disconnect
stream.close();
Persistent chat with real-time streaming via SSE.
getConversations(userId): Promise<ChatConversation[]>Get all conversations the user has participated in, sorted by most recent activity.
const conversations = await client.getConversations('user@example.com');
conversations.forEach(c => {
console.log(`${c.roomName}: ${c.messageCount} messages`);
});
getChatHistory(roomName, options?): Promise<ChatHistoryResponse>Get paginated message history for a room. Use cursor-based pagination for older messages.
const history = await client.getChatHistory('team-standup', { limit: 50 });
history.messages.forEach(m => {
console.log(`[${m.userId}] ${m.message}`);
});
// Load older messages
if (history.nextCursor) {
const older = await client.getChatHistory('team-standup', {
cursor: history.nextCursor,
limit: 50,
});
}
sendChatMessage(roomName, message, userId): Promise<{ messageId: string }>Send a message to a room. Returns the persisted message ID.
const { messageId } = await client.sendChatMessage(
'team-standup',
'Hello team!',
'user@example.com'
);
connectChatStream(roomName, handlers?): Promise<ChatStreamHandle>Open a real-time SSE stream for chat messages. Returns a handle with close() to disconnect.
const stream = await client.connectChatStream('team-standup', {
onSnapshot: (messages) => console.log('Recent messages:', messages),
onMessage: (msg) => console.log('New message:', msg.message),
onError: (err) => console.error('Stream error:', err),
});
// Later: disconnect
stream.close();
getChatStreamUrl(roomName): Promise<string>Get a signed SSE stream URL for chat messages. Useful when you need to manage the EventSource directly.
const url = await client.getChatStreamUrl('team-standup');
const eventSource = new EventSource(url);
For server-side admin operations (managing user profiles, organizations, API keys), use the dedicated @hiyve/admin package. It provides three layers of server-only enforcement to prevent accidental client-side usage.
npm install @hiyve/admin
Utility functions for converting emotion data from @hiyve/react-intelligence's mood analysis (MoodAnalysisProvider / useMoodAnalysis) into the sentiment format used by the cloud API.
emotionToSentiment(emotion): 'positive' | 'negative' | 'neutral'Maps an emotion string to a sentiment classification.
| Emotion | Sentiment |
|---|---|
happy, surprised |
positive |
angry, fearful, disgusted, sad |
negative |
neutral (and all others) |
neutral |
import { emotionToSentiment } from '@hiyve/cloud';
emotionToSentiment('happy'); // 'positive'
emotionToSentiment('angry'); // 'negative'
emotionToSentiment('neutral'); // 'neutral'
moodStateToSentimentEntry(participantId, moodState): SentimentEntryConverts a mood state object into a SentimentEntry for use with pushMoodToResponse.
import { moodStateToSentimentEntry } from '@hiyve/cloud';
const entry = moodStateToSentimentEntry('user@example.com', {
emotion: 'happy',
confidence: 0.92,
timestamp: Date.now(),
});
// { participant: 'user@example.com', sentiment: 'positive', confidence: 0.92, timestamp: ... }
await client.pushMoodToResponse('resp_abc123', 'user@example.com', [entry]);
The moodState parameter accepts any object matching the MoodStateLike interface, including the full MoodState type from @hiyve/react-intelligence.
Client-side validation helpers to catch invalid input before making API requests.
import {
validateLength,
validateRoomName,
validateTranscriptBatch,
validateSearchQuery,
MAX_QUERY_LENGTH,
MAX_TRANSCRIPT_LENGTH,
MAX_ROOM_NAME_LENGTH,
MAX_TRANSCRIPT_BATCH_SIZE,
} from '@hiyve/cloud';
| Function | Description |
|---|---|
validateLength(value, maxLength, fieldName) |
Throws if string exceeds max length |
validateRoomName(roomName) |
Throws if room name is empty or exceeds 256 characters |
validateTranscriptBatch(segments) |
Throws if batch exceeds 1000 segments |
validateSearchQuery(query) |
Throws if search query is empty or exceeds max length |
| Constant | Value |
|---|---|
MAX_QUERY_LENGTH |
32,000 characters |
MAX_TRANSCRIPT_LENGTH |
500,000 characters |
MAX_ROOM_NAME_LENGTH |
256 characters |
MAX_TRANSCRIPT_BATCH_SIZE |
1,000 segments |
import { CloudClient, ENVIRONMENT_URLS } from '@hiyve/cloud';
// Use production (default)
const client = new CloudClient({ cloudToken: 'ct_...' });
// Use development environment
const client = new CloudClient({
cloudToken: 'ct_...',
environment: 'development',
});
// Use a custom URL (overrides environment)
const client = new CloudClient({
cloudToken: 'ct_...',
baseUrl: 'https://custom-api.example.com',
});
| Environment | Description |
|---|---|
production (default) |
The live multi-tenant cloud (https://cloud.hiyve.io) |
development |
Not a sandbox — the same live endpoint as production. Only one cloud exists, so this alias reaches the live store and logs a warning. Pass baseUrl to target another stack. |
The correct URL for each environment is resolved automatically via ENVIRONMENT_URLS. You can override with baseUrl if needed. HTTPS is required for all URLs. HTTP is only permitted for localhost and 127.0.0.1 during development.
For React applications, use @hiyve/react-intelligence instead. It wraps CloudClient with React context and provides hooks like useIntelligence, useLiveContext, useAlerts, and useScheduling. When used with IdentityProvider and @hiyve/admin server middleware, cloud tokens are generated automatically -- no props needed.
import { IdentityProvider } from '@hiyve/react-identity';
import { CloudProvider } from '@hiyve/react-intelligence';
function App() {
return (
<IdentityProvider>
<CloudProvider>
<MeetingRoom />
</CloudProvider>
</IdentityProvider>
);
}
| Constant | Value | Description |
|---|---|---|
DEFAULT_BASE_URL |
ENVIRONMENT_URLS.production |
Default API base URL (production endpoint) |
DEFAULT_TIMEOUT |
30000 |
Default request timeout in milliseconds |
DEFAULT_ENVIRONMENT |
'production' |
Default cloud environment |
DEFAULT_MODEL |
'gpt-4o-mini' |
Default AI model |
DEFAULT_SUMMARY_MAX_LENGTH |
100 |
Default summary max length (words) |
DEFAULT_ENHANCE_STYLE |
'professional' |
Default note enhancement style |
DEFAULT_ALERT_SETTINGS |
{ enabled: true, ... } |
Default alert settings with medium sensitivity for all categories |
All types are exported from the package entry point. Key types by category:
Configuration: CloudConfig, CloudEnvironment, CloudTokenResponse, GenerateCloudTokenParams
AI Intelligence: QueryOptions, AIResponse, SummaryOptions, ActionItem, ActionItemsOptions, EnhanceOptions
Transcription & Sentiment: TranscriptSegment, SentimentEntry, SentimentContext, MoodStateLike, TranscriptionMoodData
Alerts: MeetingAlert, AlertSettings, AlertHistoryOptions
Scheduling: Meeting, MeetingParticipant, CreateMeetingOptions, GetMeetingsOptions, NotificationType
Search: SearchOptions, SearchResult, SearchResults, AskOptions, AskResponse
Analysis: AnalysisOptions, CustomAnalysisOptions, AnalysisResponse, DimensionScore, SalesScorecard, InterviewAssessment, SessionSummary, CoachingAnalysis, CustomAnalysis
Active Rooms: CloudActiveRoom, CloudAdvertiseRoomOptions, ActiveRoomsStreamHandle
Chat: ChatConversation, ChatHistoryResponse, ChatHistoryMessage, ChatStreamMessage, ChatStreamHandle
Pagination: Pagination
Proprietary - Hiyve SDK
@hiyve/cloud - Framework-agnostic HTTP client for Hiyve Cloud AI services
This package provides the core cloud client and types that can be used with any JavaScript framework. For React applications, use
@hiyve/react-intelligencewhich wraps this package with React context and hooks.Example