API methods
Class ZorioWebphone expose 12 phương thức chính. Bốn phương thức đầu nằm trên instance webphone (phone.*), tám phương thức điều khiển cuộc gọi nằm trên object cuộc gọi (call.* — trả về từ phone.call() hoặc payload event incoming_call).
Phương pháp gọi
Mọi method đều async trả về Promise. Anh chị await để bắt error và đảm bảo state sync trước khi gọi method tiếp theo.
1. Static factory
ZorioWebphone.connect(opts: ConnectOptions): Promise<ZorioWebphone>
ZorioWebphone.diagnose(opts: { apiBase: string; token: string }): Promise<DiagnoseResult>connect() bootstrap: gọi /api/webphone/sip-config → tạo UserAgent SIP.js → register → mount UI (nếu có mount). diagnose() chạy preflight HTTPS / Micro / WSS / STUN / TURN — gọi trước connect() khi debug.
interface ConnectOptions {
apiBase: string
token: string
mount?: string | HTMLElement
layout?: 'docked' | 'overlay' | 'fullscreen'
theme?: 'light' | 'dark' | 'auto'
locale?: 'vi' | 'en'
autoRegister?: boolean
ringtoneUrl?: string
audioOutputDeviceId?: string
audioInputDeviceId?: string
tokenResolver?: () => Promise<string>
debug?: boolean
onError?: (err: ZorioWebphoneError) => void
}2. Instance methods — connection & control
2.1 connect
Đã document ở phần Static factory phía trên. Trả về phone instance dùng cho các method còn lại.
2.2 disconnect
phone.disconnect(): Promise<void>Đóng toàn bộ resource: unregister SIP, đóng WebSocket, remove UI DOM (nếu embedded), clear audio element, cancel mọi reconnect timer. Bắt buộc gọi khi user logout app.
async function onLogout() {
await phone.disconnect()
redirectTo('/login')
}Pagehide tự xử lý
SDK đã tự addEventListener('pagehide', ...) để unregister sạch khi user đóng tab. Anh chị chỉ cần gọi disconnect() thủ công cho luồng logout chủ động.
2.3 call
phone.call(number: string, options?: CallOptions): Promise<ZorioCall>
interface CallOptions {
campaignId?: number
leadId?: number
customerId?: number
recording?: boolean
callerName?: string
meta?: Record<string, unknown>
}Khởi tạo cuộc gọi outbound. Trả về ZorioCall ngay sau khi SIP INVITE được gửi (state originating); cuộc gọi tiếp tục lifecycle qua events call_ringing → call_answered → call_hangup.
const call = await phone.call('0912345678', {
campaignId: 36,
leadId: 78912,
recording: true,
meta: { source: 'crm-quickdial' },
})
console.log('Call UUID:', call.uuid)Format số
Format số tuân thủ dial-plan Zorio PBX của khách hàng. Mặc định E.164 (+84912345678) hoặc số quốc nội (0912345678). Số không parse được sẽ throw ZorioWebphoneError với code = 'invalid_target'.
3. Call-control methods (trên ZorioCall)
Các method sau gọi trên object ZorioCall lấy được từ phone.call(), payload incoming_call, hoặc qua phone.calls.find(c => c.state === 'answered').
3.1 answer
call.answer(): Promise<void>Trả lời cuộc gọi inbound. Throw code = 'not_incoming' nếu gọi trên outbound. Tự attach micro track + remote audio.
phone.on('incoming_call', async ({ call }) => {
if (myUI.shouldAutoAnswer()) await call.answer()
})3.2 reject / hangup
call.hangup(): Promise<void>Kết thúc cuộc gọi ở bất kỳ state nào. SDK tự chọn SIP method phù hợp:
| State | SIP method |
|---|---|
originating (outbound chưa ring) | CANCEL |
ringing inbound (chưa answer) | REJECT (response 486 / 603) |
answered | BYE |
reject vs hangup
SDK expose chung qua call.hangup() — state-aware. Khi bạn muốn rõ semantic "từ chối cuộc đến", có thể alias:
const reject = (call: ZorioCall) => call.hangup()3.3 mute / unmute
call.mute(): Promise<void>
call.unmute(): Promise<void>Tắt/bật micro local. KHÔNG ảnh hưởng remote audio. State sync qua property call.muted: boolean và emit event muted / unmuted.
muteBtn.onclick = async () => {
const active = phone.calls.find(c => c.state === 'answered')
if (!active) return
if (active.muted) await active.unmute()
else await active.mute()
}3.4 hold / unhold
call.hold(): Promise<void>
call.unhold(): Promise<void>Tạm dừng cuộc gọi qua SIP re-INVITE với SDP a=sendonly. Hold đúng chuẩn nên callee sẽ nghe music-on-hold của Zorio PBX (nếu profile bật).
await call.hold()
// ... user xử lý việc khác
await call.unhold()3.5 sendDtmf
call.sendDtmf(digits: string): Promise<void>Gửi DTMF tones qua RFC 2833. Hỗ trợ ký tự 0-9, *, #, A-D. Ký tự khác throw code = 'invalid_dtmf'.
// Điều hướng IVR sau khi callee answer (đợi 1s cho IVR phát prompt)
phone.on('call_answered', async ({ call }) => {
await new Promise(r => setTimeout(r, 1000))
await call.sendDtmf('1') // chọn nhánh 1
await call.sendDtmf('2#') // chọn 2 rồi #
})3.6 transfer
call.transfer(target: string): Promise<void>Blind transfer — chuyển cuộc gọi sang target (extension/số điện thoại) không cần consult. State chuyển về closed sau khi SIP REFER được Zorio PBX chấp nhận.
// User bấm "Chuyển sang nhánh 2002"
await call.transfer('2002')v1.0 — chưa hỗ trợ
transfer() và phương thức consult attended transfer hiện throw code = 'not_implemented' trong v1.0. Đặt vào roadmap Phase 2. Đối tác cần transfer hôm nay có thể fallback qua *8 blind-transfer Zorio PBX feature code → bấm DTMF qua sendDtmf('*8' + target + '#').
4. Events subscription
phone.on() / phone.off() tuy không nằm trong 12 method chính nhưng là phần không thể thiếu:
phone.on<E extends EventName>(event: E, handler: (payload: EventPayload<E>) => void): void
phone.off<E extends EventName>(event: E, handler?: Function): voidXem chi tiết tất cả 12 events ở trang Events.
5. Properties read-only
Một số property tiện theo dõi state mà không cần wait event:
phone.status // 'connecting' | 'registered' | 'unregistered' | 'failed'
phone.extension // số máy nhánh SIP đã đăng ký
phone.user // UserProfile | null
phone.calls // ZorioCall[] — mọi cuộc đang active
// Trên ZorioCall:
call.uuid // UUID Zorio PBX (link với CDR)
call.direction // 'incoming' | 'outgoing'
call.state // 'originating' | 'ringing' | 'answered' | 'wrap_up' | 'closed'
call.number // số đối phương (E.164)
call.startedAt // Date — lúc khởi tạo
call.answeredAt // Date | null — lúc kết nối thành công
call.endedAt // Date | null
call.muted // boolean
call.onHold // boolean6. Error handling
Tất cả method throw ZorioWebphoneError:
class ZorioWebphoneError extends Error {
code: string
details?: Record<string, unknown>
}Mã lỗi thường gặp: bootstrap_failed, webphone_disabled, incomplete_sip_config, invalid_target, not_incoming, invalid_dtmf, audio_autoplay_blocked, reconnect_failed, not_implemented, not_bootstrapped.
try {
const call = await phone.call('xxx')
} catch (err) {
if (err.code === 'invalid_target') {
alert('Số gọi không hợp lệ')
} else {
console.error(err)
}
}7. Bảng tổng hợp 12 method chính
| # | Method | Phạm vi | Mục đích |
|---|---|---|---|
| 1 | connect | static | Bootstrap SDK + register SIP |
| 2 | disconnect | phone | Đóng sạch, dùng khi logout |
| 3 | call | phone | Khởi tạo outbound |
| 4 | hangup | call | Cancel/Bye cuộc đang đi |
| 5 | answer | call | Trả lời inbound |
| 6 | reject | call | Alias hangup khi inbound chưa answer |
| 7 | mute | call | Tắt micro |
| 8 | unmute | call | Bật micro |
| 9 | hold | call | Tạm dừng (re-INVITE sendonly) |
| 10 | unhold | call | Tiếp tục |
| 11 | sendDtmf | call | Gửi tone IVR |
| 12 | transfer | call | Blind transfer (Phase 2) |
Tham khảo thêm:
