ahs-cti 1.0.0-beta.2 β†’ 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,639 +1,639 @@
1
- <div align="center">
2
-
3
- ## AHS CTI SDK
4
-
5
- [![npm version](https://img.shields.io/npm/v/ahs-cti.svg?color=blue&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
6
- [![npm downloads](https://img.shields.io/npm/dm/ahs-cti.svg?color=green&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
7
- [![bundle size](https://img.shields.io/bundlephobia/minzip/ahs-cti?color=orange&style=for-the-badge)](https://bundlephobia.com/package/ahs-cti)
8
-
9
- **A powerful, lightweight SDK for real-time call management with dynamic configuration and permission-based access control**
10
-
11
- **πŸ”‘AUTHORIZED ACCESS REQUIRED - API Key & Tenant IDπŸ”‘**
12
-
13
- </div>
14
-
15
- ### ⭐ **License Features**
16
-
17
- - **API Key Authentication** - Secure access through authorized API keys
18
- - **Tenant ID Management** - Multi-tenant support with isolated access
19
- - **Permission-Based Access Control** - Entitlement-driven feature enablement
20
- - **Cross-Platform Support** - Use across web, mobile, desktop, and server platforms
21
- - **Usage Monitoring** - Real-time tracking and compliance monitoring
22
- - **Flexible Deployment** - Deploy on authorized platforms with proper credentials
23
-
24
- ### 🚫 **Prohibited Activities**
25
-
26
- - **No Unauthorized Access** - Cannot use without valid credentials
27
- - **No Credential Sharing** - API keys and tenant IDs are confidential
28
- - **No Reverse Engineering** - Cannot decompile or reverse engineer
29
- - **No Platform Violations** - Must use only on authorized platforms
30
- - **No Usage Abuse** - Cannot exceed subscription limits
31
-
32
- ## πŸ“‘ Table of Contents
33
-
34
- - [✨ Key Features](#-key-features)
35
- - [πŸ“¦ Installation](#-installation)
36
- - [πŸš€ Quick Start](#-quick-start)
37
- - [1️⃣ Initialize the SDK](#1️⃣-initialize-the-sdk)
38
- - [2️⃣ Add Call Control Panel](#2️⃣-add-call-control-panel)
39
- - [3️⃣ Use Call Management Hooks](#3️⃣-use-call-management-hooks)
40
- - [βš™οΈ Dynamic Configuration](#️-dynamic-configuration)
41
- - [πŸ”§ SDK Config](#-sdk-config)
42
- - [🌐 URL Config](#-url-config)
43
- - [πŸ” Permission-Based Controls](#-permission-based-controls)
44
- - [πŸ“Š Configuration Merge Priority](#-configuration-merge-priority)
45
- - [πŸ“š API Reference](#-api-reference)
46
- - [πŸ”§ Core Functions](#-core-functions)
47
- - [πŸͺ React Hooks](#-react-hooks)
48
- - [πŸ”’ Permissions System](#-permissions-system)
49
- - [πŸ”Œ WebSocket Events](#-websocket-events)
50
- - [πŸ“¦ Data Types](#-data-types)
51
- - [🌍 Browser Support](#-browser-support)
52
- - [πŸ“„ License](#-license)
53
-
54
- ---
55
-
56
- ## ✨ Key Features
57
-
58
- ### 🎯 **Complete Call Control**
59
-
60
- - Inbound and outbound call management
61
- - Hold/Resume calls
62
- - Mute/Unmute functionality
63
- - Blind, Attended, and Warm call transfers
64
- - Multi-line conference calling (up to 5 lines)
65
- - Agent status management (Ready, Break, Wrapup)
66
- - End call with disposition tracking
67
- - Ringtone support for incoming calls
68
-
69
- ### βš™οΈ **Dynamic Configuration**
70
-
71
- - **Three-layer config merging** - Backend defaults, permission-based controls, and explicit SDK config
72
- - **Permission-based feature toggling** - Controls auto-disabled based on entitlements
73
- - **Runtime URL configuration** - All endpoints dynamically resolved from config
74
- - **Customizable UI** - Material-UI SxProps for button styling
75
-
76
- ### πŸ’Ύ **Smart State Management**
77
-
78
- - Real-time call timer
79
- - Persistent state storage via `@react-solutions/vault`
80
- - Singleton state pattern
81
- - Cross-component sync via observer pattern
82
- - Full TypeScript support
83
-
84
- ## πŸ“¦ Installation
85
-
86
- ```bash
87
- npm install ahs-cti
88
- ```
89
-
90
- ### πŸ”‘ Required Peer Dependencies
91
-
92
- ```bash
93
- npm install react react-dom axios @mui/material @mui/icons-material @emotion/react @emotion/styled @react-solutions/vault
94
- ```
95
-
96
- ---
97
-
98
- ## πŸš€ Quick Start
99
-
100
- ### 1️⃣ **Initialize the SDK**
101
-
102
- Initialize the SDK at the root of your application with the required credentials and optional dynamic configuration:
103
-
104
- ```tsx
105
- import { useEffect } from "react";
106
- import { initSDK } from "ahs-cti";
107
-
108
- function App() {
109
- useEffect(() => {
110
- const initialize = async () => {
111
- try {
112
- await initSDK({
113
- // Required credentials
114
- apiKey: "your-api-key",
115
- tenantId: "your-tenant-id",
116
- agentId: "your-agent-id",
117
-
118
- // Optional: URL configuration for API endpoints
119
- urlConfig: {
120
- },
121
-
122
- // Optional: Feature configuration overrides
123
- sdkConfig: {
124
- },
125
- });
126
- console.log("βœ… SDK initialized successfully");
127
- } catch (error) {
128
- console.error("❌ SDK initialization failed:", error);
129
- }
130
- };
131
-
132
- initialize();
133
- }, []);
134
-
135
- return <YourAppContent />;
136
- }
137
- ```
138
-
139
- ### 2️⃣ **Add Call Control Panel**
140
-
141
- Drop in the draggable call control panel anywhere in your app:
142
-
143
- ```tsx
144
- import { CallControlPanel } from "ahs-cti";
145
-
146
- export default function AgentDashboard() {
147
- const handleCallDataChange = (data) => {
148
- console.log("πŸ“ž Call data updated:", data);
149
- };
150
-
151
- return (
152
- <div>
153
- <h1>Agent Dashboard</h1>
154
- <CallControlPanel onDataChange={handleCallDataChange} />
155
- </div>
156
- );
157
- }
158
- ```
159
-
160
- ### 3️⃣ **Use Call Management Hooks**
161
-
162
- #### πŸ”΄ **Start a Call**
163
-
164
- ```tsx
165
- import { useClickToCall } from "ahs-cti";
166
-
167
- export default function CallButton() {
168
- const { handleStartCall, isLoading, isSuccess, isError, error } =
169
- useClickToCall();
170
-
171
- const startCall = () => {
172
- handleStartCall({ mobileNumber: "1234567890" });
173
- };
174
-
175
- return (
176
- <button onClick={startCall} disabled={isLoading}>
177
- {isLoading ? "πŸ”„ Calling..." : "πŸ“ž Start Call"}
178
- </button>
179
- );
180
- }
181
- ```
182
-
183
- #### πŸ”š **End a Call**
184
-
185
- ```tsx
186
- import { useEndCall } from "ahs-cti";
187
-
188
- export default function EndCallButton() {
189
- const { handleEndCall, isLoading } = useEndCall();
190
-
191
- const endCall = () => {
192
- handleEndCall({ disposition: "RES" });
193
- };
194
-
195
- return (
196
- <button onClick={endCall} disabled={isLoading}>
197
- {isLoading ? "πŸ”„ Ending..." : "πŸ”š End Call"}
198
- </button>
199
- );
200
- }
201
- ```
202
-
203
- #### πŸšͺ **Agent Logout**
204
-
205
- ```tsx
206
- import { useLogout } from "ahs-cti";
207
-
208
- export default function LogoutButton() {
209
- const { logout, isLoading } = useLogout();
210
-
211
- return (
212
- <button onClick={logout} disabled={isLoading}>
213
- {isLoading ? "πŸ”„ Logging out..." : "πŸšͺ Logout"}
214
- </button>
215
- );
216
- }
217
- ```
218
-
219
- #### πŸ“ž **Get Caller Data**
220
-
221
- The `useGetCallerData` hook provides reactive access to current call data independently of `CallControlPanel`:
222
-
223
- ```tsx
224
- import { useGetCallerData } from "ahs-cti";
225
-
226
- export default function CallStatusWidget() {
227
- const callerData = useGetCallerData();
228
-
229
- return (
230
- <div>
231
- <h3>Current Call Status</h3>
232
- {callerData.status === "ONCALL" ? (
233
- <div>
234
- <p>Active Call: {callerData.phone_number}</p>
235
- <p>Agent: {callerData.agent_id}</p>
236
- <p>Process: {callerData.process_name}</p>
237
- <p>Queue: {callerData.queue_name}</p>
238
- </div>
239
- ) : (
240
- <p>No active call</p>
241
- )}
242
- </div>
243
- );
244
- }
245
- ```
246
-
247
- #### πŸ”„ **Subscribe to SDK State**
248
-
249
- ```tsx
250
- import { useSDKState } from "ahs-cti";
251
-
252
- export default function AgentStatusDisplay() {
253
- const state = useSDKState();
254
-
255
- return (
256
- <div>
257
- <p>Agent: {state.agentId}</p>
258
- <p>Status: {state.agentStatus}</p>
259
- <p>Initialized: {state.isInitialized ? "Yes" : "No"}</p>
260
- <p>Hold: {state.hold ? "On Hold" : "Active"}</p>
261
- <p>Mute: {state.mute ? "Muted" : "Unmuted"}</p>
262
- </div>
263
- );
264
- }
265
- ```
266
-
267
- #### πŸ” **Get Authorization Token**
268
-
269
- Retrieve the access token for external API calls:
270
-
271
- ```tsx
272
- import { useGetAuthorizationToken } from "ahs-cti";
273
-
274
- export default function ExternalAPICall() {
275
- const token = useGetAuthorizationToken();
276
-
277
- const fetchData = async () => {
278
- const response = await fetch("https://api.example.com/data", {
279
- headers: { Authorization: `Bearer ${token}` },
280
- });
281
- // handle response
282
- };
283
-
284
- return <button onClick={fetchData}>Fetch External Data</button>;
285
- }
286
- ```
287
-
288
- ---
289
-
290
- ## βš™οΈ Dynamic Configuration
291
-
292
- The SDK supports a three-layer dynamic configuration system that determines which call control features are enabled.
293
-
294
- ### πŸ”§ SDK Config
295
-
296
- Feature toggle options passed via `sdkConfig` in `initSDK()`:
297
-
298
- ### 🎨 **Configuration Options**
299
-
300
- | Option | Type | Default | Description |
301
- | --------------------------- | --------- | ------- | --------------------------------------- |
302
- | `disableEndCallButton` | `boolean` | `false` | Disable the end call button |
303
- | `disabledDialButton` | `boolean` | `false` | Disable the dial/outbound call button |
304
- | `disableCallTransferButton` | `boolean` | `false` | Disable the call transfer button |
305
- | `disableBlindTransfer` | `boolean` | `false` | Disable blind transfer option |
306
- | `disableAttendedTransfer` | `boolean` | `false` | Disable attended transfer option |
307
- | `disableWarmTransfer` | `boolean` | `false` | Disable warm transfer option |
308
- | `disableConferenceButton` | `boolean` | `false` | Disable the conference call button |
309
- | `disableHoldButton` | `boolean` | `false` | Disable the hold functionality |
310
- | `disableMuteButton` | `boolean` | `false` | Disable the mute functionality |
311
- | `disabledMoreOptionsButton` | `boolean` | `false` | Disable the more options menu |
312
- | `disableSoftPhone` | `boolean` | `false` | Disable the embedded soft phone iframe |
313
- | `isDraggable` | `boolean` | `true` | Enable/disable panel dragging |
314
- | `enableSmsServices` | `boolean` | `false` | Enable SMS services |
315
- | `enableQueueName` | `boolean` | `false` | Show queue name in call data |
316
- | `enableRingtone` | `boolean` | `false` | Enable ringtone for incoming calls |
317
- | `auto_wrapup_time` | `number` | - | Auto wrapup time in seconds |
318
- | `break_time` | `number` | - | Break duration in seconds |
319
- | `disabled` | `SxProps` | - | Custom MUI styles for disabled states |
320
- | `enabled` | `SxProps` | - | Custom MUI styles for enabled states |
321
- | `outlined` | `SxProps` | - | Custom MUI styles for outlined states |
322
-
323
- ### 🌐 URL Config
324
-
325
- All API endpoints are dynamically resolved from the URL configuration:
326
-
327
- ```typescript
328
- interface URLConfig {
329
- id: string; // Configuration identifier
330
- baseURL: string; // Main API base URL
331
- coreBaseURL: string; // Core API URL for call control actions
332
- webSocketURL: string; // WebSocket URL for real-time events
333
- iframeURL: string; // Soft phone iframe URL
334
- iframeAPIURL: string; // Iframe API endpoint
335
- password: string; // Agent password for authentication
336
- accessToken?: string; // Pre-provided access token (optional)
337
- }
338
- ```
339
-
340
- If `baseURL` is not provided, the SDK falls back to `window.location.origin`.
341
-
342
- ### πŸ” Permission-Based Controls
343
-
344
- The SDK implements a **fail-closed** permission model. After authentication, the backend returns entitlements that automatically determine which controls are enabled:
345
-
346
- ```typescript
347
- // Entitlements structure from login response
348
- {
349
- calls: {
350
- enabled: boolean,
351
- items: [
352
- { code: "call_inbound", enabled: boolean },
353
- { code: "call_outbound", enabled: boolean }
354
- ]
355
- },
356
- call_transfer: {
357
- enabled: boolean,
358
- items: [
359
- { code: "call_transfer_blind", enabled: boolean },
360
- { code: "call_transfer_attended", enabled: boolean },
361
- { code: "call_transfer_warm", enabled: boolean }
362
- ]
363
- },
364
- call_conference: {
365
- enabled: boolean,
366
- items: [
367
- { code: "call_conference", enabled: boolean }
368
- ]
369
- }
370
- }
371
- ```
372
-
373
- If permissions have not been loaded, **all controls are disabled by default**.
374
-
375
- You can use the exported `SDK_PERMISSIONS` constants to reference permission codes:
376
-
377
- ```typescript
378
- import { SDK_PERMISSIONS } from "ahs-cti";
379
-
380
- // Permission categories
381
- SDK_PERMISSIONS.CALLS; // "calls"
382
- SDK_PERMISSIONS.CALL_TRANSFER; // "call_transfer"
383
- SDK_PERMISSIONS.CALL_CONFERENCE; // "call_conference"
384
-
385
- // Permission codes
386
- SDK_PERMISSIONS.CALL_INBOUND; // "call_inbound"
387
- SDK_PERMISSIONS.CALL_OUTBOUND; // "call_outbound"
388
- SDK_PERMISSIONS.TRANSFER_BLIND; // "call_transfer_blind"
389
- SDK_PERMISSIONS.TRANSFER_ATTENDED; // "call_transfer_attended"
390
- SDK_PERMISSIONS.TRANSFER_WARM; // "call_transfer_warm"
391
- SDK_PERMISSIONS.CONFERENCE; // "call_conference"
392
- ```
393
-
394
- ### πŸ“Š Configuration Merge Priority
395
-
396
- The SDK merges configuration from three sources (highest priority wins):
397
-
398
- ```
399
- 1. Explicit sdkConfig (passed to initSDK) <- Highest priority
400
- 2. Permission-based controls (from entitlements) <- Medium priority
401
- 3. Backend callControls (from login response) <- Lowest priority
402
- ```
403
-
404
- This means:
405
-
406
- - Backend defaults set the baseline configuration
407
- - Entitlements override backend defaults based on the agent's permissions
408
- - Explicit `sdkConfig` overrides everything, allowing host applications to force-enable or force-disable features
409
-
410
- ---
411
-
412
- ## πŸ“š API Reference
413
-
414
- ### πŸ”§ **Core Functions**
415
-
416
- #### `initSDK(params)`
417
-
418
- Initialize the SDK with credentials and configuration. **Must be called before using any components or hooks.** Returns a `Promise<void>`.
419
-
420
- ```typescript
421
- await initSDK({
422
- apiKey: string, // βœ… Required - API key for authentication
423
- tenantId: string, // βœ… Required - Tenant identifier
424
- agentId: string, // βœ… Required - Agent identifier
425
- sessionId?: string, // πŸ“Ž Optional - Session identifier
426
- urlConfig?: URLConfig, // πŸ“Ž Optional - URL configuration for endpoints
427
- sdkConfig?: SDKConfig, // πŸ“Ž Optional - Feature configuration overrides
428
- });
429
- ```
430
-
431
- #### `isSDKInitialized()`
432
-
433
- Check if the SDK has been successfully initialized.
434
-
435
- ```typescript
436
- const initialized: boolean = isSDKInitialized();
437
- ```
438
-
439
- #### `getSDKVersion()`
440
-
441
- Get the current SDK version.
442
-
443
- ```typescript
444
- const version: string = getSDKVersion();
445
- ```
446
-
447
- #### `CallControlPanel`
448
-
449
- Draggable control panel component for call management.
450
-
451
- ```tsx
452
- <CallControlPanel
453
- onDataChange={(data: CallData) => {
454
- // Called when call status, phone number, or any call data changes
455
- }}
456
- />
457
- ```
458
-
459
- ### πŸͺ **React Hooks**
460
-
461
- | Hook | Description | Returns |
462
- | --------------------------- | -------------------------------------------- | ----------------------------------------------------------- |
463
- | `useClickToCall()` | Initiate outbound calls | `{ handleStartCall, isLoading, isSuccess, isError, error }` |
464
- | `useEndCall()` | End active calls with disposition | `{ handleEndCall, isLoading, isSuccess, isError, error }` |
465
- | `useLogout()` | Agent logout | `{ logout, isLoading, isSuccess, isError, error }` |
466
- | `useGetCallerData()` | Reactive access to current call data | `CallData` |
467
- | `useSDKState()` | Subscribe to full SDK state changes | `SDKState` |
468
- | `useGetAuthorizationToken()`| Get the current access token | `string \| undefined` |
469
-
470
- ---
471
-
472
- ## πŸ”Œ WebSocket Events
473
-
474
- The SDK connects to a WebSocket for real-time call events:
475
-
476
- ```
477
- WebSocket URL: {urlConfig.webSocketURL}/api/v1/ws/agent/events?token={accessToken}
478
- ```
479
-
480
- **Connection behavior:**
481
- - Auto-reconnects with exponential backoff (base: 2s, max: 30s, up to 60 attempts)
482
- - Sends ping every 30 seconds to keep the connection alive
483
-
484
- **Event data structure:**
485
-
486
- ```typescript
487
- {
488
- agent_id: number;
489
- status: CallStatus; // IDLE, READY, ONCALL, RINGING, DIALING, WRAPUP, etc.
490
- type: string; // Call type
491
- event_time: string;
492
- phone_number: string;
493
- convox_id: string;
494
- process_id: number;
495
- process_name: string;
496
- hold: number; // 0 = not held, 1 = held
497
- mute: number; // 0 = not muted, 1 = muted
498
- mode: string; // 'manual' or automated
499
- queue_name: string;
500
- conferencestatus?: { // Present during conference calls
501
- line_1_status: string;
502
- line_1_phonenumber: string;
503
- line_2_status: string;
504
- line_2_phonenumber: string;
505
- // ... up to line 5
506
- }
507
- }
508
- ```
509
-
510
- ---
511
-
512
- ## πŸ“¦ Data Types
513
-
514
- ### `CallStatus` Enum
515
-
516
- ```typescript
517
- enum CallStatus {
518
- IDLE = "IDLE",
519
- READY = "READY",
520
- BREAK = "BREAK",
521
- ONCALL = "ONCALL",
522
- WRAPUP = "WRAPUP",
523
- RINGING = "RINGING",
524
- DIALING = "DIALING",
525
- MISSED = "MISSED",
526
- HOLD = "HOLD",
527
- UNHOLD = "UNHOLD",
528
- MUTE = "MUTE",
529
- UNMUTE = "UNMUTE",
530
- DISCONNECTED = "DISCONNECTED",
531
- CONFERENCE = "CONFERENCE",
532
- }
533
- ```
534
-
535
- ### `CallData`
536
-
537
- ```typescript
538
- interface CallData {
539
- mobileNumber?: string;
540
- callReferenceId?: string;
541
- convoxId?: string;
542
- type?: string;
543
- status: string;
544
- agent_id: number;
545
- phone_number?: string;
546
- event_time?: string;
547
- convox_id?: string;
548
- process_id?: string;
549
- process_name?: string;
550
- mode?: string;
551
- queue_name?: string;
552
- mute?: number; // 1 = muted, 0 = unmuted
553
- hold?: number; // 1 = on hold, 0 = not on hold
554
- }
555
- ```
556
-
557
- ### `ConferenceLineTypes`
558
-
559
- ```typescript
560
- type ConferenceLineTypes = {
561
- line: number; // Line number (1-5)
562
- status: string; // ONCALL, IDLE, LINE USED, etc.
563
- type: "external" | "internal" | "";
564
- phone: string;
565
- isMute: boolean;
566
- isHold: boolean;
567
- isCallStart: boolean;
568
- isMergeCall: boolean;
569
- };
570
- ```
571
-
572
- ### `StartCallPayload`
573
-
574
- ```typescript
575
- interface StartCallPayload {
576
- mobileNumber?: string;
577
- }
578
- ```
579
-
580
- ### `EndCallPayload`
581
-
582
- ```typescript
583
- interface EndCallPayload {
584
- disposition?: string; // Default: "RES"
585
- }
586
- ```
587
-
588
- ---
589
-
590
- ## πŸ“€ Exported Types
591
-
592
- ```typescript
593
- // Types
594
- export type { CallData, CallControlPanelProps, SDKConfig, URLConfig };
595
- export type { SDKPermissionState, ControlsFromPermissions, SDKPermissionContextValue };
596
- export type { RequestResult, RequestOptions };
597
- export type { UseGetRequest, UsePostRequest, UsePutRequest, UseDeleteRequest, UsePatchRequest };
598
-
599
- // Enum
600
- export { CallStatus };
601
-
602
- // Constants
603
- export { SDK_PERMISSIONS };
604
-
605
- // Functions
606
- export { initSDK, getSDKVersion, isSDKInitialized };
607
-
608
- // Hooks
609
- export { useLogout, useEndCall, useClickToCall, useGetCallerData, useGetAuthorizationToken, useSDKState };
610
-
611
- // Components
612
- export { CallControlPanel };
613
- ```
614
-
615
- ---
616
-
617
- ## 🌍 **Browser Support**
618
-
619
- <div align="left">
620
-
621
- | Browser | Version | Status |
622
- | ----------------------------------------------------------------------------- | ------- | ---------- |
623
- | ![Chrome](https://img.shields.io/badge/Chrome-60%2B-green?logo=google-chrome) | 60+ | βœ… Supported |
624
- | ![Firefox](https://img.shields.io/badge/Firefox-60%2B-green?logo=firefox) | 60+ | βœ… Supported |
625
- | ![Safari](https://img.shields.io/badge/Safari-12%2B-green?logo=safari) | 12+ | βœ… Supported |
626
- | ![Edge](https://img.shields.io/badge/Edge-79%2B-green?logo=microsoft-edge) | 79+ | βœ… Supported |
627
-
628
- </div>
629
-
630
- ---
631
-
632
- ## πŸ“„ **License**
633
-
634
- <div align="center">
635
-
636
- ![license](https://img.shields.io/badge/License-Achala-blue?style=for-the-badge)
637
- ![license](https://img.shields.io/badge/License-Proprietary-red?style=for-the-badge)
638
-
639
- </div>
1
+ <div align="center">
2
+
3
+ ## AHS CTI SDK
4
+
5
+ [![npm version](https://img.shields.io/npm/v/ahs-cti.svg?color=blue&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
6
+ [![npm downloads](https://img.shields.io/npm/dm/ahs-cti.svg?color=green&style=for-the-badge)](https://www.npmjs.com/package/ahs-cti)
7
+ [![bundle size](https://img.shields.io/bundlephobia/minzip/ahs-cti?color=orange&style=for-the-badge)](https://bundlephobia.com/package/ahs-cti)
8
+
9
+ **A powerful, lightweight SDK for real-time call management with dynamic configuration and permission-based access control**
10
+
11
+ **πŸ”‘AUTHORIZED ACCESS REQUIRED - API Key & Tenant IDπŸ”‘**
12
+
13
+ </div>
14
+
15
+ ### ⭐ **License Features**
16
+
17
+ - **API Key Authentication** - Secure access through authorized API keys
18
+ - **Tenant ID Management** - Multi-tenant support with isolated access
19
+ - **Permission-Based Access Control** - Entitlement-driven feature enablement
20
+ - **Cross-Platform Support** - Use across web, mobile, desktop, and server platforms
21
+ - **Usage Monitoring** - Real-time tracking and compliance monitoring
22
+ - **Flexible Deployment** - Deploy on authorized platforms with proper credentials
23
+
24
+ ### 🚫 **Prohibited Activities**
25
+
26
+ - **No Unauthorized Access** - Cannot use without valid credentials
27
+ - **No Credential Sharing** - API keys and tenant IDs are confidential
28
+ - **No Reverse Engineering** - Cannot decompile or reverse engineer
29
+ - **No Platform Violations** - Must use only on authorized platforms
30
+ - **No Usage Abuse** - Cannot exceed subscription limits
31
+
32
+ ## πŸ“‘ Table of Contents
33
+
34
+ - [✨ Key Features](#-key-features)
35
+ - [πŸ“¦ Installation](#-installation)
36
+ - [πŸš€ Quick Start](#-quick-start)
37
+ - [1️⃣ Initialize the SDK](#1️⃣-initialize-the-sdk)
38
+ - [2️⃣ Add Call Control Panel](#2️⃣-add-call-control-panel)
39
+ - [3️⃣ Use Call Management Hooks](#3️⃣-use-call-management-hooks)
40
+ - [βš™οΈ Dynamic Configuration](#️-dynamic-configuration)
41
+ - [πŸ”§ SDK Config](#-sdk-config)
42
+ - [🌐 URL Config](#-url-config)
43
+ - [πŸ” Permission-Based Controls](#-permission-based-controls)
44
+ - [πŸ“Š Configuration Merge Priority](#-configuration-merge-priority)
45
+ - [πŸ“š API Reference](#-api-reference)
46
+ - [πŸ”§ Core Functions](#-core-functions)
47
+ - [πŸͺ React Hooks](#-react-hooks)
48
+ - [πŸ”’ Permissions System](#-permissions-system)
49
+ - [πŸ”Œ WebSocket Events](#-websocket-events)
50
+ - [πŸ“¦ Data Types](#-data-types)
51
+ - [🌍 Browser Support](#-browser-support)
52
+ - [πŸ“„ License](#-license)
53
+
54
+ ---
55
+
56
+ ## ✨ Key Features
57
+
58
+ ### 🎯 **Complete Call Control**
59
+
60
+ - Inbound and outbound call management
61
+ - Hold/Resume calls
62
+ - Mute/Unmute functionality
63
+ - Blind, Attended, and Warm call transfers
64
+ - Multi-line conference calling (up to 5 lines)
65
+ - Agent status management (Ready, Break, Wrapup)
66
+ - End call with disposition tracking
67
+ - Ringtone support for incoming calls
68
+
69
+ ### βš™οΈ **Dynamic Configuration**
70
+
71
+ - **Three-layer config merging** - Backend defaults, permission-based controls, and explicit SDK config
72
+ - **Permission-based feature toggling** - Controls auto-disabled based on entitlements
73
+ - **Runtime URL configuration** - All endpoints dynamically resolved from config
74
+ - **Customizable UI** - Material-UI SxProps for button styling
75
+
76
+ ### πŸ’Ύ **Smart State Management**
77
+
78
+ - Real-time call timer
79
+ - Persistent state storage via `@react-solutions/vault`
80
+ - Singleton state pattern
81
+ - Cross-component sync via observer pattern
82
+ - Full TypeScript support
83
+
84
+ ## πŸ“¦ Installation
85
+
86
+ ```bash
87
+ npm install ahs-cti
88
+ ```
89
+
90
+ ### πŸ”‘ Required Peer Dependencies
91
+
92
+ ```bash
93
+ npm install react react-dom axios @mui/material @mui/icons-material @emotion/react @emotion/styled @react-solutions/vault
94
+ ```
95
+
96
+ ---
97
+
98
+ ## πŸš€ Quick Start
99
+
100
+ ### 1️⃣ **Initialize the SDK**
101
+
102
+ Initialize the SDK at the root of your application with the required credentials and optional dynamic configuration:
103
+
104
+ ```tsx
105
+ import { useEffect } from "react";
106
+ import { initSDK } from "ahs-cti";
107
+
108
+ function App() {
109
+ useEffect(() => {
110
+ const initialize = async () => {
111
+ try {
112
+ await initSDK({
113
+ // Required credentials
114
+ apiKey: "your-api-key",
115
+ tenantId: "your-tenant-id",
116
+ agentId: "your-agent-id",
117
+
118
+ // Optional: URL configuration for API endpoints
119
+ urlConfig: {
120
+ },
121
+
122
+ // Optional: Feature configuration overrides
123
+ sdkConfig: {
124
+ },
125
+ });
126
+ console.log("βœ… SDK initialized successfully");
127
+ } catch (error) {
128
+ console.error("❌ SDK initialization failed:", error);
129
+ }
130
+ };
131
+
132
+ initialize();
133
+ }, []);
134
+
135
+ return <YourAppContent />;
136
+ }
137
+ ```
138
+
139
+ ### 2️⃣ **Add Call Control Panel**
140
+
141
+ Drop in the draggable call control panel anywhere in your app:
142
+
143
+ ```tsx
144
+ import { CallControlPanel } from "ahs-cti";
145
+
146
+ export default function AgentDashboard() {
147
+ const handleCallDataChange = (data) => {
148
+ console.log("πŸ“ž Call data updated:", data);
149
+ };
150
+
151
+ return (
152
+ <div>
153
+ <h1>Agent Dashboard</h1>
154
+ <CallControlPanel onDataChange={handleCallDataChange} />
155
+ </div>
156
+ );
157
+ }
158
+ ```
159
+
160
+ ### 3️⃣ **Use Call Management Hooks**
161
+
162
+ #### πŸ”΄ **Start a Call**
163
+
164
+ ```tsx
165
+ import { useClickToCall } from "ahs-cti";
166
+
167
+ export default function CallButton() {
168
+ const { handleStartCall, isLoading, isSuccess, isError, error } =
169
+ useClickToCall();
170
+
171
+ const startCall = () => {
172
+ handleStartCall({ mobileNumber: "1234567890" });
173
+ };
174
+
175
+ return (
176
+ <button onClick={startCall} disabled={isLoading}>
177
+ {isLoading ? "πŸ”„ Calling..." : "πŸ“ž Start Call"}
178
+ </button>
179
+ );
180
+ }
181
+ ```
182
+
183
+ #### πŸ”š **End a Call**
184
+
185
+ ```tsx
186
+ import { useEndCall } from "ahs-cti";
187
+
188
+ export default function EndCallButton() {
189
+ const { handleEndCall, isLoading } = useEndCall();
190
+
191
+ const endCall = () => {
192
+ handleEndCall({ disposition: "RES" });
193
+ };
194
+
195
+ return (
196
+ <button onClick={endCall} disabled={isLoading}>
197
+ {isLoading ? "πŸ”„ Ending..." : "πŸ”š End Call"}
198
+ </button>
199
+ );
200
+ }
201
+ ```
202
+
203
+ #### πŸšͺ **Agent Logout**
204
+
205
+ ```tsx
206
+ import { useLogout } from "ahs-cti";
207
+
208
+ export default function LogoutButton() {
209
+ const { logout, isLoading } = useLogout();
210
+
211
+ return (
212
+ <button onClick={logout} disabled={isLoading}>
213
+ {isLoading ? "πŸ”„ Logging out..." : "πŸšͺ Logout"}
214
+ </button>
215
+ );
216
+ }
217
+ ```
218
+
219
+ #### πŸ“ž **Get Caller Data**
220
+
221
+ The `useGetCallerData` hook provides reactive access to current call data independently of `CallControlPanel`:
222
+
223
+ ```tsx
224
+ import { useGetCallerData } from "ahs-cti";
225
+
226
+ export default function CallStatusWidget() {
227
+ const callerData = useGetCallerData();
228
+
229
+ return (
230
+ <div>
231
+ <h3>Current Call Status</h3>
232
+ {callerData.status === "ONCALL" ? (
233
+ <div>
234
+ <p>Active Call: {callerData.phone_number}</p>
235
+ <p>Agent: {callerData.agent_id}</p>
236
+ <p>Process: {callerData.process_name}</p>
237
+ <p>Queue: {callerData.queue_name}</p>
238
+ </div>
239
+ ) : (
240
+ <p>No active call</p>
241
+ )}
242
+ </div>
243
+ );
244
+ }
245
+ ```
246
+
247
+ #### πŸ”„ **Subscribe to SDK State**
248
+
249
+ ```tsx
250
+ import { useSDKState } from "ahs-cti";
251
+
252
+ export default function AgentStatusDisplay() {
253
+ const state = useSDKState();
254
+
255
+ return (
256
+ <div>
257
+ <p>Agent: {state.agentId}</p>
258
+ <p>Status: {state.agentStatus}</p>
259
+ <p>Initialized: {state.isInitialized ? "Yes" : "No"}</p>
260
+ <p>Hold: {state.hold ? "On Hold" : "Active"}</p>
261
+ <p>Mute: {state.mute ? "Muted" : "Unmuted"}</p>
262
+ </div>
263
+ );
264
+ }
265
+ ```
266
+
267
+ #### πŸ” **Get Authorization Token**
268
+
269
+ Retrieve the access token for external API calls:
270
+
271
+ ```tsx
272
+ import { useGetAuthorizationToken } from "ahs-cti";
273
+
274
+ export default function ExternalAPICall() {
275
+ const token = useGetAuthorizationToken();
276
+
277
+ const fetchData = async () => {
278
+ const response = await fetch("https://api.example.com/data", {
279
+ headers: { Authorization: `Bearer ${token}` },
280
+ });
281
+ // handle response
282
+ };
283
+
284
+ return <button onClick={fetchData}>Fetch External Data</button>;
285
+ }
286
+ ```
287
+
288
+ ---
289
+
290
+ ## βš™οΈ Dynamic Configuration
291
+
292
+ The SDK supports a three-layer dynamic configuration system that determines which call control features are enabled.
293
+
294
+ ### πŸ”§ SDK Config
295
+
296
+ Feature toggle options passed via `sdkConfig` in `initSDK()`:
297
+
298
+ ### 🎨 **Configuration Options**
299
+
300
+ | Option | Type | Default | Description |
301
+ | --------------------------- | --------- | ------- | --------------------------------------- |
302
+ | `disableEndCallButton` | `boolean` | `false` | Disable the end call button |
303
+ | `disabledDialButton` | `boolean` | `false` | Disable the dial/outbound call button |
304
+ | `disableCallTransferButton` | `boolean` | `false` | Disable the call transfer button |
305
+ | `disableBlindTransfer` | `boolean` | `false` | Disable blind transfer option |
306
+ | `disableAttendedTransfer` | `boolean` | `false` | Disable attended transfer option |
307
+ | `disableWarmTransfer` | `boolean` | `false` | Disable warm transfer option |
308
+ | `disableConferenceButton` | `boolean` | `false` | Disable the conference call button |
309
+ | `disableHoldButton` | `boolean` | `false` | Disable the hold functionality |
310
+ | `disableMuteButton` | `boolean` | `false` | Disable the mute functionality |
311
+ | `disabledMoreOptionsButton` | `boolean` | `false` | Disable the more options menu |
312
+ | `disableSoftPhone` | `boolean` | `false` | Disable the embedded soft phone iframe |
313
+ | `isDraggable` | `boolean` | `true` | Enable/disable panel dragging |
314
+ | `enableSmsServices` | `boolean` | `false` | Enable SMS services |
315
+ | `enableQueueName` | `boolean` | `false` | Show queue name in call data |
316
+ | `enableRingtone` | `boolean` | `false` | Enable ringtone for incoming calls |
317
+ | `auto_wrapup_time` | `number` | - | Auto wrapup time in seconds |
318
+ | `break_time` | `number` | - | Break duration in seconds |
319
+ | `disabled` | `SxProps` | - | Custom MUI styles for disabled states |
320
+ | `enabled` | `SxProps` | - | Custom MUI styles for enabled states |
321
+ | `outlined` | `SxProps` | - | Custom MUI styles for outlined states |
322
+
323
+ ### 🌐 URL Config
324
+
325
+ All API endpoints are dynamically resolved from the URL configuration:
326
+
327
+ ```typescript
328
+ interface URLConfig {
329
+ id: string; // Configuration identifier
330
+ baseURL: string; // Main API base URL
331
+ coreBaseURL: string; // Core API URL for call control actions
332
+ webSocketURL: string; // WebSocket URL for real-time events
333
+ iframeURL: string; // Soft phone iframe URL
334
+ iframeAPIURL: string; // Iframe API endpoint
335
+ password: string; // Agent password for authentication
336
+ accessToken?: string; // Pre-provided access token (optional)
337
+ }
338
+ ```
339
+
340
+ If `baseURL` is not provided, the SDK falls back to `window.location.origin`.
341
+
342
+ ### πŸ” Permission-Based Controls
343
+
344
+ The SDK implements a **fail-closed** permission model. After authentication, the backend returns entitlements that automatically determine which controls are enabled:
345
+
346
+ ```typescript
347
+ // Entitlements structure from login response
348
+ {
349
+ calls: {
350
+ enabled: boolean,
351
+ items: [
352
+ { code: "call_inbound", enabled: boolean },
353
+ { code: "call_outbound", enabled: boolean }
354
+ ]
355
+ },
356
+ call_transfer: {
357
+ enabled: boolean,
358
+ items: [
359
+ { code: "call_transfer_blind", enabled: boolean },
360
+ { code: "call_transfer_attended", enabled: boolean },
361
+ { code: "call_transfer_warm", enabled: boolean }
362
+ ]
363
+ },
364
+ call_conference: {
365
+ enabled: boolean,
366
+ items: [
367
+ { code: "call_conference", enabled: boolean }
368
+ ]
369
+ }
370
+ }
371
+ ```
372
+
373
+ If permissions have not been loaded, **all controls are disabled by default**.
374
+
375
+ You can use the exported `SDK_PERMISSIONS` constants to reference permission codes:
376
+
377
+ ```typescript
378
+ import { SDK_PERMISSIONS } from "ahs-cti";
379
+
380
+ // Permission categories
381
+ SDK_PERMISSIONS.CALLS; // "calls"
382
+ SDK_PERMISSIONS.CALL_TRANSFER; // "call_transfer"
383
+ SDK_PERMISSIONS.CALL_CONFERENCE; // "call_conference"
384
+
385
+ // Permission codes
386
+ SDK_PERMISSIONS.CALL_INBOUND; // "call_inbound"
387
+ SDK_PERMISSIONS.CALL_OUTBOUND; // "call_outbound"
388
+ SDK_PERMISSIONS.TRANSFER_BLIND; // "call_transfer_blind"
389
+ SDK_PERMISSIONS.TRANSFER_ATTENDED; // "call_transfer_attended"
390
+ SDK_PERMISSIONS.TRANSFER_WARM; // "call_transfer_warm"
391
+ SDK_PERMISSIONS.CONFERENCE; // "call_conference"
392
+ ```
393
+
394
+ ### πŸ“Š Configuration Merge Priority
395
+
396
+ The SDK merges configuration from three sources (highest priority wins):
397
+
398
+ ```
399
+ 1. Explicit sdkConfig (passed to initSDK) <- Highest priority
400
+ 2. Permission-based controls (from entitlements) <- Medium priority
401
+ 3. Backend callControls (from login response) <- Lowest priority
402
+ ```
403
+
404
+ This means:
405
+
406
+ - Backend defaults set the baseline configuration
407
+ - Entitlements override backend defaults based on the agent's permissions
408
+ - Explicit `sdkConfig` overrides everything, allowing host applications to force-enable or force-disable features
409
+
410
+ ---
411
+
412
+ ## πŸ“š API Reference
413
+
414
+ ### πŸ”§ **Core Functions**
415
+
416
+ #### `initSDK(params)`
417
+
418
+ Initialize the SDK with credentials and configuration. **Must be called before using any components or hooks.** Returns a `Promise<void>`.
419
+
420
+ ```typescript
421
+ await initSDK({
422
+ apiKey: string, // βœ… Required - API key for authentication
423
+ tenantId: string, // βœ… Required - Tenant identifier
424
+ agentId: string, // βœ… Required - Agent identifier
425
+ sessionId?: string, // πŸ“Ž Optional - Session identifier
426
+ urlConfig?: URLConfig, // πŸ“Ž Optional - URL configuration for endpoints
427
+ sdkConfig?: SDKConfig, // πŸ“Ž Optional - Feature configuration overrides
428
+ });
429
+ ```
430
+
431
+ #### `isSDKInitialized()`
432
+
433
+ Check if the SDK has been successfully initialized.
434
+
435
+ ```typescript
436
+ const initialized: boolean = isSDKInitialized();
437
+ ```
438
+
439
+ #### `getSDKVersion()`
440
+
441
+ Get the current SDK version.
442
+
443
+ ```typescript
444
+ const version: string = getSDKVersion();
445
+ ```
446
+
447
+ #### `CallControlPanel`
448
+
449
+ Draggable control panel component for call management.
450
+
451
+ ```tsx
452
+ <CallControlPanel
453
+ onDataChange={(data: CallData) => {
454
+ // Called when call status, phone number, or any call data changes
455
+ }}
456
+ />
457
+ ```
458
+
459
+ ### πŸͺ **React Hooks**
460
+
461
+ | Hook | Description | Returns |
462
+ | --------------------------- | -------------------------------------------- | ----------------------------------------------------------- |
463
+ | `useClickToCall()` | Initiate outbound calls | `{ handleStartCall, isLoading, isSuccess, isError, error }` |
464
+ | `useEndCall()` | End active calls with disposition | `{ handleEndCall, isLoading, isSuccess, isError, error }` |
465
+ | `useLogout()` | Agent logout | `{ logout, isLoading, isSuccess, isError, error }` |
466
+ | `useGetCallerData()` | Reactive access to current call data | `CallData` |
467
+ | `useSDKState()` | Subscribe to full SDK state changes | `SDKState` |
468
+ | `useGetAuthorizationToken()`| Get the current access token | `string \| undefined` |
469
+
470
+ ---
471
+
472
+ ## πŸ”Œ WebSocket Events
473
+
474
+ The SDK connects to a WebSocket for real-time call events:
475
+
476
+ ```
477
+ WebSocket URL: {urlConfig.webSocketURL}/api/v1/ws/agent/events?token={accessToken}
478
+ ```
479
+
480
+ **Connection behavior:**
481
+ - Auto-reconnects with exponential backoff (base: 2s, max: 30s, up to 60 attempts)
482
+ - Sends ping every 30 seconds to keep the connection alive
483
+
484
+ **Event data structure:**
485
+
486
+ ```typescript
487
+ {
488
+ agent_id: number;
489
+ status: CallStatus; // IDLE, READY, ONCALL, RINGING, DIALING, WRAPUP, etc.
490
+ type: string; // Call type
491
+ event_time: string;
492
+ phone_number: string;
493
+ convox_id: string;
494
+ process_id: number;
495
+ process_name: string;
496
+ hold: number; // 0 = not held, 1 = held
497
+ mute: number; // 0 = not muted, 1 = muted
498
+ mode: string; // 'manual' or automated
499
+ queue_name: string;
500
+ conferencestatus?: { // Present during conference calls
501
+ line_1_status: string;
502
+ line_1_phonenumber: string;
503
+ line_2_status: string;
504
+ line_2_phonenumber: string;
505
+ // ... up to line 5
506
+ }
507
+ }
508
+ ```
509
+
510
+ ---
511
+
512
+ ## πŸ“¦ Data Types
513
+
514
+ ### `CallStatus` Enum
515
+
516
+ ```typescript
517
+ enum CallStatus {
518
+ IDLE = "IDLE",
519
+ READY = "READY",
520
+ BREAK = "BREAK",
521
+ ONCALL = "ONCALL",
522
+ WRAPUP = "WRAPUP",
523
+ RINGING = "RINGING",
524
+ DIALING = "DIALING",
525
+ MISSED = "MISSED",
526
+ HOLD = "HOLD",
527
+ UNHOLD = "UNHOLD",
528
+ MUTE = "MUTE",
529
+ UNMUTE = "UNMUTE",
530
+ DISCONNECTED = "DISCONNECTED",
531
+ CONFERENCE = "CONFERENCE",
532
+ }
533
+ ```
534
+
535
+ ### `CallData`
536
+
537
+ ```typescript
538
+ interface CallData {
539
+ mobileNumber?: string;
540
+ callReferenceId?: string;
541
+ convoxId?: string;
542
+ type?: string;
543
+ status: string;
544
+ agent_id: number;
545
+ phone_number?: string;
546
+ event_time?: string;
547
+ convox_id?: string;
548
+ process_id?: string;
549
+ process_name?: string;
550
+ mode?: string;
551
+ queue_name?: string;
552
+ mute?: number; // 1 = muted, 0 = unmuted
553
+ hold?: number; // 1 = on hold, 0 = not on hold
554
+ }
555
+ ```
556
+
557
+ ### `ConferenceLineTypes`
558
+
559
+ ```typescript
560
+ type ConferenceLineTypes = {
561
+ line: number; // Line number (1-5)
562
+ status: string; // ONCALL, IDLE, LINE USED, etc.
563
+ type: "external" | "internal" | "";
564
+ phone: string;
565
+ isMute: boolean;
566
+ isHold: boolean;
567
+ isCallStart: boolean;
568
+ isMergeCall: boolean;
569
+ };
570
+ ```
571
+
572
+ ### `StartCallPayload`
573
+
574
+ ```typescript
575
+ interface StartCallPayload {
576
+ mobileNumber?: string;
577
+ }
578
+ ```
579
+
580
+ ### `EndCallPayload`
581
+
582
+ ```typescript
583
+ interface EndCallPayload {
584
+ disposition?: string; // Default: "RES"
585
+ }
586
+ ```
587
+
588
+ ---
589
+
590
+ ## πŸ“€ Exported Types
591
+
592
+ ```typescript
593
+ // Types
594
+ export type { CallData, CallControlPanelProps, SDKConfig, URLConfig };
595
+ export type { SDKPermissionState, ControlsFromPermissions, SDKPermissionContextValue };
596
+ export type { RequestResult, RequestOptions };
597
+ export type { UseGetRequest, UsePostRequest, UsePutRequest, UseDeleteRequest, UsePatchRequest };
598
+
599
+ // Enum
600
+ export { CallStatus };
601
+
602
+ // Constants
603
+ export { SDK_PERMISSIONS };
604
+
605
+ // Functions
606
+ export { initSDK, getSDKVersion, isSDKInitialized };
607
+
608
+ // Hooks
609
+ export { useLogout, useEndCall, useClickToCall, useGetCallerData, useGetAuthorizationToken, useSDKState };
610
+
611
+ // Components
612
+ export { CallControlPanel };
613
+ ```
614
+
615
+ ---
616
+
617
+ ## 🌍 **Browser Support**
618
+
619
+ <div align="left">
620
+
621
+ | Browser | Version | Status |
622
+ | ----------------------------------------------------------------------------- | ------- | ---------- |
623
+ | ![Chrome](https://img.shields.io/badge/Chrome-60%2B-green?logo=google-chrome) | 60+ | βœ… Supported |
624
+ | ![Firefox](https://img.shields.io/badge/Firefox-60%2B-green?logo=firefox) | 60+ | βœ… Supported |
625
+ | ![Safari](https://img.shields.io/badge/Safari-12%2B-green?logo=safari) | 12+ | βœ… Supported |
626
+ | ![Edge](https://img.shields.io/badge/Edge-79%2B-green?logo=microsoft-edge) | 79+ | βœ… Supported |
627
+
628
+ </div>
629
+
630
+ ---
631
+
632
+ ## πŸ“„ **License**
633
+
634
+ <div align="center">
635
+
636
+ ![license](https://img.shields.io/badge/License-Achala-blue?style=for-the-badge)
637
+ ![license](https://img.shields.io/badge/License-Proprietary-red?style=for-the-badge)
638
+
639
+ </div>