@kmholding/callcenter-sdk
v1.0.1
Published
A TypeScript SDK for integrating SIP (Session Initiation Protocol) calling features into web applications, built on top of [SIP.js](https://sipjs.com/). This SDK provides a simplified interface for making and receiving calls, managing call states, and han
Downloads
28
Readme
Phone SDK
A TypeScript SDK for integrating SIP (Session Initiation Protocol) calling features into web applications, built on top of SIP.js. This SDK provides a simplified interface for making and receiving calls, managing call states, and handling common telephony operations.
Features
- SIP Registration: Connect to your SIP server.
- Outbound Calls: Initiate calls to SIP URIs or extensions.
- Inbound Calls: Receive and manage incoming calls.
- Call Controls:
- Accept / Reject incoming calls.
- Hangup active calls.
- Mute / Unmute local audio.
- Transfer calls (REFER) or Invite (API).
- Event-Driven: Attach listeners to various call and SDK events.
- Stream Management: Exposes methods to get local and remote
MediaStreamobjects for attaching to HTML<audio>elements. - Agent Profile Registration: Includes a method to interact with a backend API for agent profile setup before SIP registration (customizable).
- Custom Call ID: Uses
KNM-Call-IDheader for incoming calls and generates UUID for outgoing calls, ensuring consistent call tracking across the system.
Installation
npm install sip.js @kmholding/callcenter-sdk
# or
yarn add sip.js @kmholding/callcenter-sdkIf you are using the SDK directly from its source or a local build, you'll need to ensure sip.js and tslib are also available.
Getting Started
1. Initialization
First, import and instantiate the PhoneSdk:
import { PhoneSdk } from '@kmholding/callcenter-sdk'
const sdk = new PhoneSdk()Or for UMD (if loaded via a <script> tag, assuming UMD name KnM):
<script src="https://github.com/onsip/SIP.js/releases/download/0.21.2/sip-0.21.2.min.js"></script>
<script src="path/to/your/bundle.umd.min.js"></script>
<script>
const PhoneSdk = window.KnM.PhoneSdk
const sdk = new PhoneSdk()
</script>2. Agent Profile Registration (Optional, Specific to KNM Holdings)
If your system requires registering an agent profile with a backend API before using SIP:
async function registerProfile() {
try {
const agentDisplayName = 'Your Agent Name'
const result = await sdk.register(agentDisplayName)
if (result.success && result.data) {
console.log('Agent profile registered successfully!')
console.log('SIP Extension:', result.data.username)
console.log('SIP Password:', result.data.password)
console.log('SIP Display Name:', result.data.displayName)
// Use these credentials to initialize SIP in the next step
return result.data
} else {
console.error('Agent profile registration failed:', result.message)
}
} catch (error) {
console.error('Error during agent profile registration:', error)
}
return null
}3. SIP Initialization
Connect to your SIP server (typically after successful agent profile registration):
async function initSIP(sipCredentials) {
const config = {
extension: sipCredentials.username, // e.g., "1007"
password: sipCredentials.password, // e.g., "yourSecretPassword"
displayName: sipCredentials.displayName, // e.g., "Agent 1007"
sipServerUrl: 'your.sipserver.com', // Your SIP domain or IP
apiServerUrl: 'https://api.yourserver.com', // Your API server URL
apiKey: 'your-api-key' // Your API key for authentication
}
try {
await sdk.init(config)
console.log('SIP SDK Initialized and attempting to register.')
} catch (error) {
console.error('SIP SDK Initialization failed:', error)
}
}
// Example flow:
registerProfile().then((creds) => {
if (creds) {
initSIP(creds)
}
})4. Attaching Event Listeners
The SDK is event-driven. Listen to events to handle call states and SDK status:
sdk.on('onRegister', (event) => {
console.log('Registration State:', event.state, 'Registered:', event.registered)
if (event.registered) {
// Ready to make/receive calls
}
})
sdk.on('onCall', (event) => {
if (event.type === 'incomingCall') {
console.log('Incoming call from:', event.caller, 'Call ID:', event.callId)
// Prompt user to accept or reject
// Example: sdk.acceptCall(event.callId);
} else if (event.type === 'callEstablished') {
console.log('Call established with:', event.callee, 'Call ID:', event.callId)
// Attach remote audio stream
const remoteStream = sdk.getRemoteStream(event.callId)
if (remoteStream) {
const remoteAudioElement = document.getElementById('remoteAudio')
remoteAudioElement.srcObject = remoteStream
remoteAudioElement.play().catch((e) => console.warn('Autoplay failed:', e))
}
} else if (event.type === 'endCall') {
console.log('Call ended:', event.callId, 'Reason:', event.reason)
const remoteAudioElement = document.getElementById('remoteAudio')
if (remoteAudioElement) remoteAudioElement.srcObject = null
} else if (event.type === 'localMute' || event.type === 'localUnmute') {
console.log('Local audio mute/unmute:', event.type, 'Call ID:', event.callId)
}
})
sdk.on('onHangup', (event) => {
if (event.type === 'remoteHangupCall') {
console.log('Remote party hung up:', event.caller, 'Call ID:', event.callId)
} else if (event.type === 'localHangupCall') {
console.log('Local user hung up:', event.caller, 'Call ID:', event.callId)
}
})
sdk.on('onError', (event) => {
console.error('SDK Error:', event.message, event)
})5. Making a Call
async function makeCallTo(targetExtension) {
if (!sdk.isRegistered()) {
console.error('Not registered. Cannot make call.')
return
}
try {
const callId = await sdk.makeCall(targetExtension) // e.g., "1008"
if (callId) {
console.log('Call initiated with ID:', callId)
} else {
console.log('Failed to initiate call.')
}
} catch (error) {
console.error('Error making call:', error)
}
}6. Call Controls
// Assume 'currentCallId' is the ID of the active or ringing call
// Accept an incoming call
sdk.acceptCall(currentCallId)
// Reject an incoming call
sdk.rejectCall(currentCallId, 'Busy')
// Hangup an active call
sdk.hangupCall(currentCallId)
// Mute local audio
sdk.mute(currentCallId) // Mutes
sdk.mute(currentCallId, true) // Explicitly mutes
// Unmute local audio
sdk.unmute(currentCallId) // Unmutes (alias for sdk.mute(currentCallId, false))
// Transfer or invite a call (API-based)
sdk.redirect({
type: 'transfer', // or 'invite'
callId: currentCallId,
targetUri: 'target_extension_or_uri'
})
// Transfer using SIP REFER (if supported by server)
sdk.transfer(currentCallId, 'target_extension_or_uri')API Reference
PhoneSdk Class
Constructor
new PhoneSdk()
Methods
async init(config: SDKConfig): Promise<void>- Initializes the UserAgent and attempts SIP registration.
config:extension: string- SIP extensionpassword?: string- SIP passworddisplayName?: string- Display name for the usersipServerUrl: string- SIP server domain/IPapiServerUrl: string- API server URL for additional servicesapiKey: string- API key for authenticationices?: ICE[]- Optional ICE servers
async disconnect(): Promise<void>- Unregisters and stops the UserAgent.
on<K extends SDKEventsName>(eventName: K, handler: (event: SDKEventsMap[K]) => void): void- Attaches an event listener. See Event Reference below for event names.
off<K extends SDKEventsName>(eventName: K, handler: (event: SDKEventsMap[K]) => void): void- Removes an event listener.
isRegistered(): boolean- Returns
trueif currently registered with the SIP server.
- Returns
async makeCall(to: string): Promise<string | null>- Initiates an outbound call to the target extension or SIP URI.
- Returns the
callIdon success,nullon failure.
async acceptCall(callId: string): Promise<void>- Accepts an incoming call.
async rejectCall(callId: string, reason?: string): Promise<void>- Rejects an incoming call.
async hangupCall(callId: string, reason?: string): Promise<void>- Hangs up an active call.
async mute(callId: string, muteAudio?: boolean): Promise<void>- Mutes (
true) or unmutes (false) the local audio for the call. Defaults totrue.
- Mutes (
unmute(callId: string): Promise<void>- Alias for
mute(callId, false).
- Alias for
async redirect(config: RedirectOptions): Promise<boolean>- Redirects an active call via API (transfer/invite). Returns
trueif successful.
- Redirects an active call via API (transfer/invite). Returns
async transfer(callId: string, targetUri: string): Promise<boolean>- Transfers an active call using SIP REFER (if supported by server). Returns
trueif successful.
- Transfers an active call using SIP REFER (if supported by server). Returns
getLocalStream(callId: string): MediaStream | null- Gets the local
MediaStream(microphone).
- Gets the local
getRemoteStream(callId: string): MediaStream | null- Gets the remote
MediaStream(audio from the other party).
- Gets the remote
async register(initialDisplayName: string): Promise<AgentRegistrationResult>- (Specific to KNM Holdings integration) Registers agent profile with an external API.
- Returns
AgentRegistrationResult:success: booleandata: { username: string, password: string, displayName: string } | nullmessage: stringcode?: string
Event Reference
You can listen to these events via sdk.on(eventName, handler):
| Event Name | Payload Type | Description |
| ------------ | ----------------- | ------------------------------------------------------------------------------------------------------------------------ |
| onError | OnErrorEvent | SDK or SIP error |
| onCall | OnCallEvent | Call states (outgoing, incoming, established, mute, unmute, ended, ...). See the type field in the payload for details |
| onHangup | OnHangupEvent | Call ended (local or remote) |
| onInvite | OnInviteEvent | Events related to INVITE/transfer via API |
| onTransfer | OnTransferEvent | Events related to transfer via API |
| onRegister | OnRegisterEvent | SIP registration state |
Possible type values for each event:
OnCallEvent type values:
| Type | Description |
| ------------------------ | -------------------------------------- |
| incomingCall | Incoming call received |
| localAcceptCall | Local user accepted the call |
| localNotAcceptCall | Local user did not accept the call |
| localSucceedCreateCall | Outgoing call created successfully |
| remoteRingingCall | Remote party is ringing |
| remoteAcceptCall | Remote party accepted the call |
| remoteNotAcceptCall | Remote party did not accept the call |
| callEstablished | Call is established and media can flow |
| localMute | Local audio muted |
| localUnmute | Local audio unmuted |
| endCall | Call has ended |
OnHangupEvent type values:
| Type | Description |
| ------------------ | ----------------------------- |
| localHangupCall | Local user hung up the call |
| remoteHangupCall | Remote party hung up the call |
OnInviteEvent type values:
| Type | Description |
| ----------------------- | ------------------------------------- |
| localStartInvite | Local user started an invite |
| localAcceptInvite | Local user accepted an invite |
| remoteAcceptInvite | Remote party accepted an invite |
| localNotAcceptInvite | Local user did not accept an invite |
| remoteNotAcceptInvite | Remote party did not accept an invite |
OnTransferEvent type values:
| Type | Description |
| -------------------- | ----------------------------- |
| localStartTransfer | Local user started a transfer |
Example payloads for some events:
// onCall event
{
callId: '...',
type: 'incomingCall' | 'localAcceptCall' | 'remoteAcceptCall' | 'callEstablished' | 'localMute' | 'localUnmute' | 'endCall',
caller: '...',
callee: '...',
code: '...',
reason: '...'
}
// onHangup event
{
callId: '...',
type: 'localHangupCall' | 'remoteHangupCall',
caller: '...',
callee: '...',
code: '...',
reason: '...'
}
// onError event
{
message: '...',
code: '...',
error: {},
callId: '...',
targetUri: '...'
}Custom Headers
The SDK uses the custom header KNM-Call-ID to identify calls. This ID is generated for outgoing calls and read from incoming INVITE requests. Make sure your SIP infrastructure (PBX, SBC, ...) allows this header to pass through if you need backend synchronization.
Logging
The SDK uses an internal logger (utils/logger.ts). By default, sip.js logging is disabled (logBuiltinEnabled: false). SDK logs will appear in the browser console.
Development
Prerequisites / Peer Dependencies
- Node.js and npm/yarn
- A SIP server for testing
Build
npm install
npm run buildThis will build the SDK into the dist/ directory.
Testing
You can use the test.html file to test the UMD bundle (dist/bundle.umd.js).
- Configure
test.htmlwith your SIP credentials. - Serve the project directory (e.g.,
npx serve .). - Open
test.htmlin your browser.
Commit Message Convention
Follow Conventional Commits (e.g., feat: ..., fix: ..., BREAKING CHANGE: ...).
