Tích hợp Webhook và Popup cuộc gọi
Đặc tả payload Webhook cuộc gọi ODS, từ điển tham số, chuỗi JSON mẫu và ý nghĩa ứng dụng Popup.
Tài liệu này mô tả cấu trúc dữ liệu JSON Webhook do tổng đài ODS gửi đến endpoint khách hàng cung cấp, từ điển tham số, chuỗi sự kiện mẫu và ý nghĩa của dữ liệu đối với Popup. Cách triển khai ứng dụng tiếp nhận nằm ngoài contract dịch vụ ODS.
Hợp đồng HTTP tiếp nhận
Hệ thống tổng đài ODS đóng vai trò là bên gửi (Sender), chủ động phát request HTTP POST đến địa chỉ endpoint do doanh nghiệp thiết lập (Receiver):
- Địa chỉ tiếp nhận: Endpoint URL công khai thuộc máy chủ của doanh nghiệp (ví dụ:
https://crm.example.com/api/webhook/ods-call). - Giao thức và Phương thức:
HTTP POST,Content-Type: application/json. - Trường định danh sự kiện: Trường
Event(chữ cái đầu viết hoa, mang một trong 5 giá trị:register,show,answer,hide,finish). Tuyệt đối không dùng trườngStatus. - Xác thực tùy chọn: Khách hàng có thể cung cấp một khóa đã thống nhất với ODS. Khóa được gửi trong field
apikeycủa body JSON; không dùngApiKey,X-API-Key,Authorizationvà không nhầm với RESTAuthKey. - Phản hồi xác nhận tiếp nhận:
- Endpoint trả HTTP status thuộc dải
2xxsau khi tiếp nhận body JSON. - HTTP
2xxchỉ xác nhận endpoint đã tiếp nhận request, không phản ánh trạng thái cuộc gọi và không phải ACK trước khi dữ liệu được tiếp nhận. - Contract hiện tại không áp đặt response JSON bắt buộc.
- Endpoint trả HTTP status thuộc dải
Bảng từ điển tham số Payload
Để thuận tiện tra cứu theo đúng nghiệp vụ phát triển, toàn bộ 25 tham số trong gói tin Webhook ODS được phân chia thành 4 nhóm chức năng bên dưới.
Nhận diện cuộc gọi và máy nhánh
Nhóm tham số cốt lõi phục vụ điều phối và đồng bộ cửa sổ cuộc gọi (Popup CRM):
| Tên trường | Kiểu dữ liệu | Ý nghĩa nghiệp vụ | Sự kiện xuất hiện | Lưu ý kỹ thuật |
|---|---|---|---|---|
Event | string | Tên sự kiện vòng đời | Mọi sự kiện | Giá trị: register, show, answer, hide, finish. Không dùng trường Status. |
LID | string | Mã phiên tổng thể (Linked ID) | Mọi sự kiện | Khóa liên kết xuyên suốt toàn bộ cuộc gọi. Mọi sự kiện cùng một cuộc gọi đều chung LID. |
Uid | string | Mã định danh nhánh kênh thoại (Unique ID) | show, answer, hide, finish | Có vai trò khác LID. Giá trị có thể bằng LID trong một số kịch bản, nhưng không được giả định luôn bằng nhau. |
Dir | string | Hướng cuộc gọi | Mọi sự kiện | Nhận giá trị: Inbound (gọi vào), Outbound (gọi ra), hoặc Local (gọi nội bộ). |
CID | string | Số điện thoại khách hàng | Mọi sự kiện | Luôn đại diện cho số khách hàng ở cả 2 chiều gọi (gọi vào là số người gọi, gọi ra là số người nhận). |
DID | string | Đầu số Hotline doanh nghiệp | Hầu hết sự kiện | Đầu số tổng đài đại diện của doanh nghiệp. |
Ext | string | Số máy nhánh nội bộ | show, answer, hide, finish | Số máy lẻ nhân viên tiếp nhận/thực hiện cuộc gọi (ví dụ: 101, 102). |
srcName | string | Tên hiển thị người gọi/nhân viên | Tùy kịch bản | Tên nhân viên (gọi ra) hoặc tên nhận diện theo danh bạ tổng đài (gọi vào). |
Mốc thời gian và tính cước
Nhóm tham số phục vụ đo lường thời lượng đàm thoại, kiểm tra SLA và đối soát cước viễn thông:
| Tên trường | Kiểu dữ liệu | Ý nghĩa nghiệp vụ | Sự kiện xuất hiện | Lưu ý kỹ thuật |
|---|---|---|---|---|
TimeCall | string | Thời điểm khởi tạo cuộc gọi | Mọi sự kiện | Định dạng YYYY-MM-DD HH:mm:ss theo giờ máy chủ tổng đài. |
TimeDial | string | false | Thời điểm máy nhánh bắt đầu đổ chuông | show, hide, finish | Định dạng YYYY-MM-DD HH:mm:ss. Có thể mang giá trị false nếu nhánh chưa kịp kích hoạt chuông. |
TimeAnswer | string | Thời điểm bắt đầu đàm thoại | answer, finish | Chỉ xuất hiện khi có người nhấc máy. Dùng làm mốc bắt đầu tính thời gian đàm thoại. |
TimeStopDial | string | Thời điểm máy nhánh dừng đổ chuông | hide | Định dạng YYYY-MM-DD HH:mm:ss. Ghi nhận chính xác lúc máy nhánh tắt chuông. |
TimeEnd | string | Thời điểm kết thúc cuộc gọi | finish | Định dạng YYYY-MM-DD HH:mm:ss. Đánh dấu thời điểm hai bên ngắt kết nối hoàn toàn. |
TimeBillSec | string | Thời điểm bắt đầu tính cước viễn thông | Inbound qua IVR/Queue | Chú ý: Là mốc thời gian timestamp (YYYY-MM-DD HH:mm:ss), không phải số giây thời lượng. |
BillSecBy | string | Nguyên nhân bắt đầu tính cước | Inbound qua IVR/Queue | Kịch bản kích hoạt tính cước viễn thông (ví dụ: IVR khi nghe lời chào, hoặc Queue). |
Kịch bản tổng đài và kênh viễn thông
Nhóm tham số kỹ thuật phục vụ phân tích luồng điều hướng VoIP và hàng đợi:
| Tên trường | Kiểu dữ liệu | Ý nghĩa nghiệp vụ | Sự kiện xuất hiện | Lưu ý kỹ thuật |
|---|---|---|---|---|
IVR | string | Tên kịch bản tương tác phím | Inbound qua IVR | Tên kịch bản lời chào tương tác (ví dụ: ivr-14). |
Queue | string | Số định danh hàng đợi | Inbound qua Queue | Số nhóm tiếp nhận cuộc gọi tập trung (ví dụ: 7800). |
Ch | string | Kênh nguồn Asterisk (Source Channel) | Hầu hết sự kiện | Định danh kênh viễn thông gốc (ví dụ: PJSIP/MOBI... hoặc PJSIP/7877...). |
ChDest | string | Kênh đích Asterisk (Destination Channel) | answer, finish, Outbound | Định danh kênh viễn thông kết nối đến đầu cuối đối phương. |
Call-ID | string | Khóa SIP Call-ID | Tùy sự kiện | Định danh bản tin SIP cấp thấp. Có thể là chuỗi rỗng "". |
DialExist | boolean | Đánh dấu nhánh từng được quay số | Inbound theo nhóm | Mang giá trị true khi tổng đài đã từng phát lệnh đổ chuông đến máy nhánh này. |
Kết quả xử lý, file ghi âm và bảo mật
Nhóm tham số phục vụ lưu trữ nhật ký CDR, tải file ghi âm và xác thực nguồn gửi:
| Tên trường | Kiểu dữ liệu | Ý nghĩa nghiệp vụ | Sự kiện xuất hiện | Lưu ý kỹ thuật |
|---|---|---|---|---|
Disposition | string | Kết quả cuộc gọi / trạng thái nhánh | hide, finish | Nhận các giá trị: ANSWERED, ANSWERED_ELSEWHERE, BUSY, NO ANSWER, FAILED. |
recordingfile | string | Tên file ghi âm gốc | answer, finish, Outbound | Tên file âm thanh trên máy chủ (ví dụ: in-089616...wav). Chưa đồng nghĩa file đã sẵn sàng tải. |
recordingfileURL | string | URL tải/nghe file ghi âm | finish (khi có nghe máy) | Đường dẫn HTTPS chính thức có mã bảo mật để tải hoặc nghe lại tệp ghi âm từ ODS. |
apikey | string | null | Khóa xác thực Webhook tùy chọn | Mọi sự kiện trong body | ODS gửi đúng field chữ thường apikey. Payload mẫu chưa cấu hình khóa có giá trị null; khi đã thống nhất khóa, ứng dụng tiếp nhận kiểm tra đúng giá trị đó. |
Dữ liệu JSON mẫu thực tế
Dưới đây là các chuỗi sự kiện JSON hoàn chỉnh dựa trên dữ liệu log thực tế từ hệ thống ODS (thông tin số thuê bao và định danh đã được ẩn danh):
1. Sự kiện register (Khách hàng kết nối vào tổng đài)
{
"Event": "register",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"Ch": "PJSIP/MOBI02873007788-00003968",
"apikey": null
}2. Sự kiện show (Đổ chuông máy nhánh 101 trong nhóm)
{
"Event": "show",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"TimeBillSec": "2026-10-15 14:57:31",
"BillSecBy": "IVR",
"IVR": "ivr-14",
"Queue": "7800",
"DialExist": true,
"TimeDial": "2026-10-15 14:57:39",
"Ext": "101",
"Uid": "1710000001.1002",
"srcName": "Nguyen Van A",
"Ch": "PJSIP/MOBI02873007788-00003968",
"apikey": null
}3. Sự kiện show (Đổ chuông máy nhánh 102 trong nhóm)
{
"Event": "show",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"TimeBillSec": "2026-10-15 14:57:31",
"BillSecBy": "IVR",
"IVR": "ivr-14",
"Queue": "7800",
"DialExist": true,
"TimeDial": "2026-10-15 14:57:39",
"Ext": "102",
"Uid": "1710000001.1003",
"srcName": "Nguyen Van A",
"Ch": "PJSIP/MOBI02873007788-00003968",
"apikey": null
}4. Sự kiện answer (Máy nhánh 101 nhấc máy tiếp nhận)
{
"Event": "answer",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"TimeBillSec": "2026-10-15 14:57:31",
"BillSecBy": "IVR",
"IVR": "ivr-14",
"Queue": "7800",
"DialExist": true,
"TimeAnswer": "2026-10-15 14:57:45",
"Ch": "PJSIP/MOBI02873007788-00003968",
"ChDest": "PJSIP/101-0000396a",
"Ext": "101",
"Uid": "1710000001.1002",
"recordingfile": "in-02873007788-0901234567-20261015-145730-1710000001.1001.wav",
"apikey": null
}5. Sự kiện hide (Máy nhánh 102 dừng chuông do máy khác đã nghe)
{
"Event": "hide",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"TimeBillSec": "2026-10-15 14:57:31",
"BillSecBy": "IVR",
"IVR": "ivr-14",
"Queue": "7800",
"DialExist": true,
"Ext": "102",
"TimeDial": "2026-10-15 14:57:39",
"TimeStopDial": "2026-10-15 14:57:45",
"Uid": "1710000001.1003",
"srcName": "Nguyen Van A",
"Disposition": "ANSWERED_ELSEWHERE",
"Ch": "PJSIP/MOBI02873007788-00003968",
"apikey": null
}6. Sự kiện finish (Cuộc gọi kết thúc, tổng kết cước và file ghi âm)
{
"Event": "finish",
"LID": "1710000001.1001",
"Dir": "Inbound",
"CID": "0901234567",
"DID": "02873007788",
"TimeCall": "2026-10-15 14:57:30",
"TimeBillSec": "2026-10-15 14:57:31",
"BillSecBy": "IVR",
"IVR": "ivr-14",
"Queue": "7800",
"DialExist": true,
"Ext": "101",
"TimeDial": "2026-10-15 14:57:39",
"TimeAnswer": "2026-10-15 14:57:45",
"TimeEnd": "2026-10-15 14:57:51",
"Uid": "1710000001.1002",
"srcName": "Nguyen Van A",
"Disposition": "ANSWERED",
"Ch": "PJSIP/MOBI02873007788-00003968",
"recordingfile": "in-02873007788-0901234567-20261015-145730-1710000001.1001.wav",
"recordingfileURL": "https://recording.example.com/Home?ID=encrypt_hash_token_string",
"apikey": null
}Ý nghĩa dữ liệu đối với Popup
Popup là một ứng dụng của payload Webhook. ODS cung cấp sự kiện và dữ liệu cuộc gọi; cách khách hàng tổ chức backend, giao diện hoặc lưu trữ không thuộc contract này.
- Khi nhận sự kiện
show:CIDcho biết số khách hàng vàExtcho biết máy nhánh liên quan; ứng dụng khách hàng có thể dùng hai giá trị này để chọn thông tin Popup phù hợp.
- Khi nhận sự kiện
answer:- Dữ liệu cho biết nhánh cuộc gọi đã được trả lời.
- Khi nhận sự kiện
hide:hidemô tả một nhánh ngừng đổ chuông. Trong cuộc gọi nhóm,hidecủa nhánh cóANSWERED_ELSEWHEREkhông đồng nghĩa toàn bộ cuộc gọi theoLIDđãfinish.
- Khi nhận sự kiện
finish:- Dữ liệu cho biết cuộc gọi kết thúc và có thể kèm thông tin tổng kết như
DispositionhoặcrecordingfileURL.
- Dữ liệu cho biết cuộc gọi kết thúc và có thể kèm thông tin tổng kết như
Giới hạn định danh thông báo
- Correlation cấp cuộc gọi:
LIDliên kết các sự kiện thuộc cùng một cuộc gọi. MộtLIDcó thể xuất hiện trong nhiều thông báo khác nhau. - Nhánh gọi:
Uidnhận diện nhánh kênh thoại vàExtnhận diện máy nhánh liên quan. Một cuộc gọi có thể có nhiềuUid/Ext; các field này không phải định danh duy nhất của thông báo. - Định danh duy nhất của thông báo: Payload hiện tại chưa có event ID duy nhất đã được xác nhận. Vì vậy không thể dùng
LID,LID + Exthoặc một công thức ghép field để khẳng định phổ quát rằng hai request là trùng nhau. - Ứng dụng tiếp nhận: Nếu cần chống xử lý trùng, khách hàng phải xác định và kiểm thử quy tắc theo kịch bản tích hợp thực tế. Tài liệu hiện tại không xác nhận retry, timeout, exactly-once hay thứ tự nhận tuyệt đối.
- Nhiều tổng đài: Khi một endpoint nhận dữ liệu từ nhiều nguồn tích hợp, cần phân vùng correlation theo nguồn tích hợp +
LID. Contract không tự thêm field nguồn mới vàapikey: nullkhông xác định được tổng đài gửi.
Ý nghĩa các giá trị kết quả cuộc gọi (Disposition)
Trường Disposition phản ánh kết quả xử lý của một nhánh hoặc của toàn bộ cuộc gọi:
| Giá trị | Ý nghĩa thực tế | Ngữ cảnh xuất hiện |
|---|---|---|
ANSWERED | Cuộc gọi đã được tiếp nhận và đàm thoại thành công. | Xuất hiện trong sự kiện finish khi có ít nhất một bên bắt máy. |
ANSWERED_ELSEWHERE | Cuộc gọi đã được một máy nhánh khác trong nhóm nhấc máy trả lời. | Xuất hiện trong sự kiện hide của các máy nhánh không kịp nhấc máy. Không phải cuộc gọi nhỡ của khách hàng. |
BUSY | Máy bận (người nhận từ chối cuộc gọi hoặc đang có cuộc gọi khác). | Xuất hiện trong hide hoặc finish. |
NO ANSWER | Hết thời gian đổ chuông nhưng không có người nhấc máy. | Xuất hiện trong hide hoặc finish của cuộc gọi nhỡ. |
FAILED | Cuộc gọi thất bại do lỗi kỹ thuật mạng hoặc thuê bao không liên lạc được. | Xuất hiện trong finish. |
[!NOTE] Phân định thời gian đàm thoại và cước viễn thông:
- Thời lượng đàm thoại thực tế của nhân viên tính bằng:
TimeEnd - TimeAnswer(tính theo giây).- Không lấy
TimeEnd - TimeCalllàm thời gian đàm thoại vì khoảng cách này bao gồm cả thời gian nghe lời chào IVR và thời gian chờ đổ chuông.
Xử lý sự cố thường gặp (Troubleshooting)
| Hiện tượng lỗi | Nguyên nhân gốc rễ | Hướng xử lý |
|---|---|---|
| Endpoint không nhận được request nào | 1. URL cấu hình sai hoặc server bị chặn firewall. 2. Chưa gửi thông tin cho ODS cấu hình kích hoạt. | - Kiểm tra server có nhận được request từ Internet qua công cụ kiểm thử (như Webhook.site). - Liên hệ bộ phận kỹ thuật ODS xác nhận trạng thái kích hoạt Webhook. |
| Lỗi HTTP 401 Unauthorized | Tích hợp đã dùng xác thực tùy chọn nhưng giá trị apikey trong body không khớp giá trị hai bên đã thống nhất. | Đối chiếu cấu hình với ODS và giá trị ứng dụng tiếp nhận đang kiểm tra. Không coi null là cách bỏ qua một khóa sai. |
Một cuộc gọi phát nhiều sự kiện show | Cuộc gọi đến nhóm máy nhánh (Queue / Ring Group) làm nhiều máy đổ chuông cùng lúc. | Đây là hành vi bình thường. Ứng dụng tiếp nhận dùng Ext để phân biệt máy nhánh liên quan đến từng thông báo. |
Không có trường Ext trong sự kiện register | Cuộc gọi vào mới tiếp nhận ở tầng đầu số/IVR, chưa định tuyến đến máy lẻ nào. | Không giả định mọi sự kiện đều có Ext; đối chiếu theo loại Event. |
Không có trường recordingfileURL | Cuộc gọi nhỡ (không có người bắt máy) hoặc cuộc gọi chưa kết thúc. | Link ghi âm chỉ được máy chủ ODS sinh ra duy nhất tại sự kiện finish đối với các cuộc gọi có trạng thái ANSWERED. |
| Popup hiển thị sai máy nhánh | Ứng dụng khách hàng ánh xạ sai Ext hoặc bỏ qua ngữ cảnh nhánh gọi. | Đối chiếu Ext, Uid, LID và Event của thông báo với kịch bản thực tế. |