voxnix
v1.0.19
Published
Universal Voice Widget SDK — SIP-based floating phone widget. Works in React, Vue, Angular, plain HTML, or any stack.
Readme
Voxnix
Universal Voice Widget SDK — SIP-based floating phone widget. Works in React, Vue, Angular, plain HTML, or any stack.
Current version: 1.0.12
What it does
- Floating phone button (fixed position, configurable placement)
- Auto-connects to your SIP/PABX server on init
- Shows dialer numpad immediately after registration
- Full call UI: incoming call, outgoing dialing, in-call controls (mute, hold, DTMF keypad)
- Agent status bar — Ready / Not Ready / AUX with optional custom AUX codes
- Fires callbacks for all call and agent-status events so your app can react
- Keyboard shortcuts: digits to dial,
Enterto call,Backspaceto delete,0–9/*/#for DTMF during a call
Installation
npm install voxnix
# or
yarn add voxnixQuick start (any framework)
import { OmnixWidget } from 'voxnix';
const widget = OmnixWidget.init({
auth: {
username: '1001',
pwd_pbx: 'secret',
pabx_host: 'sip.example.com',
port: 8089, // optional, default 8089
},
// Required for agent status API (Ready / AUX / Not Ready)
'X-CPAAS-Key-Fingerprint': 'your-cpaas-key-fingerprint-here',
placement: 'bottom-left', // 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right'
// Agent status options (optional)
autoReady: true, // auto-set agent to Ready after SIP register (default: true)
showAuxButton: false, // show AUX button in status bar (default: false)
auxOptions: [], // array of { code, label } — custom AUX codes
// Callbacks
onRegisterStatus: (status) => {
// status: 'REGISTERED' | { status: 'FAILED', reason: string }
console.log('SIP status:', status);
},
onAgentStatus: (status) => console.log('Agent:', status), // 'READY' | 'NOT_READY' | 'AUX'
onIncomingCall: (data) => console.log('Incoming:', data),
onCallAnswered: (data) => console.log('Answered:', data),
onCallEnded: (data) => console.log('Ended:', data),
onCallUnanswered: () => console.log('Missed call'),
});
// Imperative controls (optional — widget has its own UI)
widget.answer();
widget.hangup();
widget.reject();
widget.mute(true); // mute
widget.mute(false); // unmute
widget.hold(true); // hold
widget.hold(false); // resume
widget.dial('1002'); // programmatic dial
widget.destroy(); // unmount widgetConfig reference
Top-level options
| Option | Type | Default | Description |
|-----------------------------|--------------------------------------------------------------------|-----------------|-----------------------------------------------------------------|
| auth | object | — | SIP credentials (see auth object) |
| pabxConfig | { auth: object } | — | Alternative config shape — use pabxConfig.auth instead of auth |
| X-CPAAS-Key-Fingerprint | string | — | Required for agent status API calls. See section below |
| fingerprint | string | — | Alias for X-CPAAS-Key-Fingerprint |
| cpaasKeyFingerprint | string | — | Alias for X-CPAAS-Key-Fingerprint |
| placement | 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' | 'bottom-left' | Fixed position of the floating widget |
| offset | { bottom?, top?, left?, right? } | — | CSS pixel coords — overrides placement |
| container | string | HTMLElement | — | Mount target. Auto-creates <div id="omnix-widget-root"> if omitted |
| autoReady | boolean | true | Auto-call the Ready API right after SIP registration |
| showAuxButton | boolean | false | Show the AUX menu in the agent status bar |
| auxOptions | Array<{ code: string, label: string }> | [] | Custom AUX reason codes shown in the status dropdown |
| onRegisterStatus | (status) => void | — | 'REGISTERED' or { status: 'FAILED', reason: string } |
| onAgentStatus | (status: string) => void | — | 'READY' | 'NOT_READY' | 'AUX' |
| onIncomingCall | (data) => void | — | Fired on incoming SIP INVITE |
| onCallAnswered | (data) => void | — | Fired when call is established |
| onCallEnded | (data) => void | — | Fired on BYE / hangup |
| onCallUnanswered | () => void | — | Fired when incoming call is not answered (missed) |
auth object
| Field | Type | Description |
|-------------|----------|------------------------------------------|
| username | string | SIP username / extension number |
| user_pbx | string | Alias for username |
| pwd_pbx | string | SIP password |
| secret | string | Alias for pwd_pbx |
| pabx_host | string | SIP domain / WSS host (strips http(s)://) |
| pbxurl | string | Alias for pabx_host |
| port | number | WSS port (default 8089) |
X-CPAAS-Key-Fingerprint
This key is the API authentication credential used when the widget calls the CPaaS extension status endpoint:
POST https://api-cpaas.omnix.co.id/voice/extensions/status
Header: X-CPAAS-Key-Fingerprint: <your-fingerprint>The endpoint is hit automatically whenever the agent status changes (Ready / AUX / Not Ready). Without this key, status updates will silently fail and — if autoReady: true (default) — the widget will report FAILED registration even if the SIP connection itself succeeded.
Accepted config locations
The widget looks for the fingerprint in the following order, using the first match:
| Priority | Config key | Example |
|----------|------------|---------|
| 1 | 'X-CPAAS-Key-Fingerprint' (top-level) | { 'X-CPAAS-Key-Fingerprint': 'abc123', auth: { ... } } |
| 2 | fingerprint (top-level) | { fingerprint: 'abc123', auth: { ... } } |
| 3 | cpaasKeyFingerprint (top-level) | { cpaasKeyFingerprint: 'abc123', auth: { ... } } |
| 4 | env.fingerprint | { env: { fingerprint: 'abc123' }, auth: { ... } } |
| 5 | env.X_CPAAS_Key_Fingerprint | { env: { X_CPAAS_Key_Fingerprint: 'abc123' }, auth: { ... } } |
| 6 | pabxConfig['X-CPAAS-Key-Fingerprint'] | { pabxConfig: { 'X-CPAAS-Key-Fingerprint': 'abc123', auth: { ... } } } |
Recommended usage
// Option A — top-level key (simplest)
OmnixWidget.init({
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
autoReady: true,
});
// Option B — inside pabxConfig (if your backend returns this shape)
OmnixWidget.init({
pabxConfig: {
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
},
});
// Option C — via env object
OmnixWidget.init({
env: { fingerprint: 'your-fingerprint-here' },
auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
});Important: If the fingerprint is missing and
autoReady: true(default), the widget will emit{ status: 'FAILED', reason: 'Failed to hit extension ready API' }viaonRegisterStatuseven though the SIP connection itself was successful. SetautoReady: falseif you do not have a fingerprint yet.
Custom container / offset
OmnixWidget.init({
container: '#my-widget-slot', // CSS selector or DOM element
// — or —
offset: { bottom: 80, left: 10 }, // exact pixel position (bypasses placement)
// ... rest of config
});Instance API
OmnixWidget.init() returns an instance with these methods:
| Method | Description |
|----------------|------------------------------------------------------------|
| answer() | Answer the current incoming call |
| hangup() | End the active / outgoing call |
| reject() | Reject the current incoming call |
| mute(bool) | true = mute microphone, false = unmute |
| hold(bool) | true = put call on hold, false = resume |
| dial(number) | Programmatically dial a number string |
| destroy() | Unmount the widget and clean up the DOM container |
Agent status bar
When autoReady: false or showAuxButton: true a status bar appears at the bottom of the dialer.
Note: The agent status bar calls the CPaaS extension status API. You must provide
X-CPAAS-Key-Fingerprint(or one of its aliases) for these calls to succeed.
OmnixWidget.init({
auth: { ... },
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
autoReady: false, // agent starts as NOT_READY; must go Ready manually
showAuxButton: true, // show AUX dropdown
auxOptions: [
{ code: 'BRK', label: 'Break' },
{ code: 'LCH', label: 'Lunch' },
{ code: 'TRN', label: 'Training' },
],
onAgentStatus: (status) => console.log('Agent status changed:', status),
});| Status | Badge colour | Meaning |
|-------------|--------------|--------------------------------------|
| READY | 🟢 Green | Agent is available for calls |
| NOT_READY | ⚪ Gray | Agent is unavailable |
| AUX | 🟡 Amber | Agent is on a break / custom reason |
React integration
import { useEffect, useRef } from 'react';
import { OmnixWidget } from 'voxnix';
function App() {
const widgetRef = useRef(null);
useEffect(() => {
widgetRef.current = OmnixWidget.init({
auth: {
username: '1001',
pwd_pbx: 'secret',
pabx_host: 'sip.example.com',
},
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
placement: 'bottom-right',
autoReady: true,
showAuxButton: true,
auxOptions: [{ code: 'BRK', label: 'Break' }],
onIncomingCall: (data) => { /* navigate, show notification, etc. */ },
onCallAnswered: (data) => { /* routing logic */ },
onCallEnded: () => { /* cleanup */ },
onRegisterStatus: (s) => { /* update UI */ },
onAgentStatus: (s) => { /* s: 'READY' | 'NOT_READY' | 'AUX' */ },
});
return () => widgetRef.current?.destroy();
}, []);
return <div id="app">...</div>;
}Vue integration
import { OmnixWidget } from 'voxnix';
// inside setup() or options API
onMounted(() => {
const widget = OmnixWidget.init({
auth: { username: '1001', pwd_pbx: 'secret', pabx_host: 'sip.example.com' },
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
autoReady: false,
showAuxButton: true,
auxOptions: [{ code: 'BRK', label: 'Break' }],
});
onUnmounted(() => widget.destroy());
});Via CDN (plain HTML, no bundler)
<!-- Self-contained IIFE build (bundles React) -->
<script src="https://unpkg.com/voxnix/dist/voxnix.iife.js"></script>
<script>
OmnixWidget.init({
auth: {
username: '1001',
pwd_pbx: 'secret',
pabx_host: 'sip.example.com',
},
'X-CPAAS-Key-Fingerprint': 'your-fingerprint-here',
placement: 'bottom-right',
autoReady: true,
showAuxButton: true,
auxOptions: [
{ code: 'BRK', label: 'Break' },
{ code: 'LCH', label: 'Lunch' },
],
onRegisterStatus: (s) => console.log('SIP:', s),
onIncomingCall: (d) => console.log('Incoming:', d),
onAgentStatus: (s) => console.log('Agent:', s),
});
</script>If you need to use your own React instance, include
react@18andreact-dom@18UMD builds before the script tag and usedist/voxnix.js(ESM) ordist/voxnix.umd.cjs(CJS) instead.
Keyboard shortcuts
These shortcuts are active whenever the dialer widget is open:
| Key | State | Action |
|------------------|---------|-------------------------------|
| 0–9, *, # | Idle | Append digit to dial input |
| Backspace | Idle | Delete last digit |
| Enter | Idle | Dial the current number |
| 0–9, *, # | In-call | Send DTMF tone |
Build
# ESM + UMD (peer React — for npm consumers)
yarn build:lib
# Self-contained IIFE (bundles React — for CDN / script tag)
yarn build:iife
# Both outputs
yarn buildOutput files in dist/:
| File | Format | React | Use case |
|--------------------|--------|----------|-----------------------|
| voxnix.js | ESM | peer dep | import / bundlers |
| voxnix.umd.cjs | UMD | peer dep | CommonJS / require |
| voxnix.iife.js | IIFE | bundled | <script> / CDN |
Development
yarn install
yarn dev # Vite dev server with hot reload
yarn watch # Vite library watch modeChangelog
v1.0.12
- Rollback DevToast / console interceptor feature
v1.0.11 and earlier
- Agent status bar (Ready / Not Ready / AUX) with configurable
auxOptions onAgentStatuscallbackautoReadyandshowAuxButtonconfig options- Keyboard shortcut support for dialer and DTMF
- React 17 / 18 compatibility layer
- Dual-SIP auth shape support (
authandpabxConfig.auth)
