npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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 MediaStream objects 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-ID header 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-sdk

If 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 extension
      • password?: string - SIP password
      • displayName?: string - Display name for the user
      • sipServerUrl: string - SIP server domain/IP
      • apiServerUrl: string - API server URL for additional services
      • apiKey: string - API key for authentication
      • ices?: 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 true if currently registered with the SIP server.
  • async makeCall(to: string): Promise<string | null>
    • Initiates an outbound call to the target extension or SIP URI.
    • Returns the callId on success, null on 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 to true.
  • unmute(callId: string): Promise<void>
    • Alias for mute(callId, false).
  • async redirect(config: RedirectOptions): Promise<boolean>
    • Redirects an active call via API (transfer/invite). Returns true if successful.
  • async transfer(callId: string, targetUri: string): Promise<boolean>
    • Transfers an active call using SIP REFER (if supported by server). Returns true if successful.
  • getLocalStream(callId: string): MediaStream | null
    • Gets the local MediaStream (microphone).
  • getRemoteStream(callId: string): MediaStream | null
    • Gets the remote MediaStream (audio from the other party).
  • async register(initialDisplayName: string): Promise<AgentRegistrationResult>
    • (Specific to KNM Holdings integration) Registers agent profile with an external API.
    • Returns AgentRegistrationResult:
      • success: boolean
      • data: { username: string, password: string, displayName: string } | null
      • message: string
      • code?: 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 build

This 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).

  1. Configure test.html with your SIP credentials.
  2. Serve the project directory (e.g., npx serve .).
  3. Open test.html in your browser.

Commit Message Convention

Follow Conventional Commits (e.g., feat: ..., fix: ..., BREAKING CHANGE: ...).