# Tích hợp kênh thoại trên ứng dụng Web (Voice API)

* Hỗ trợ các loại ứng dụng web (web app) tích hợp tổng đài thoại CareSoft.
* Người dùng (user) của doanh nghiệp có thể thực hiện việc tiếp nhận cuộc gọi hay gọi điện ra ra cho khách hàng thông qua số hotline CareSoft.
* Popup thông tin khách hàng khi có cuộc gọi đến
* Công nghệ sử dụng WebRTC không cần cài đặt thêm phần mềm nghe gọi điện.
* Hỗ trợ tích hợp với các thiết bị nghe gọi như IP Phone, Softphone

## Chuẩn bị thông tin

1. Lấy mã xác thực từ giao diện admin  CareSoft. \
   Truy cập vào CareSoft bằng tài khoản admin mà CareSoft cấp cho khách hàng.&#x20;
2. Tại menu Admin --> Api --> Api Token. \
   \
   Ở dòng **Token voice api hiện tại** chọn **Tạo token mới**  nếu chưa có hoặc **Copy**  nếu đã có và lưu lại thành 1 cấu hình `{{apiVoiceAccessToken}}`&#x20;

<figure><img src="https://2193274687-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fw9FEMPuWidjTVayVtnJj%2Fuploads%2FB1jLabwd54SvgAuhDweM%2FAPI_VOICE_TOKEN.png?alt=media&#x26;token=25a903fa-0415-4031-bbf5-94a19661bb1e" alt=""><figcaption></figcaption></figure>

3. Thư viện JWT phù hợp với ngôn ngữ lập trình của bạn để tạo mã token cho mỗi cuộc gọi. Xem thêm ở  [www.jwt.io](https://jwt.io/)

{% hint style="info" %}
**LƯU Ý:** Khi  bấm nút tạo mới hệ thống sẽ thay thế `apiVoiceAccessToken`cũ bằng `apiVoiceAccessToken` mới khiến các ứng dụng đang tích hợp sẽ mất quyền truy cập.  Vui lòng cập nhật token ở các ứng dụng theo token mới nếu bạn bấm "Tạo token mới" để đảm bảo hệ thống hoạt động liền mạch&#x20;
{% endhint %}

## Cách thức tích hợp

1. Chuẩn bị payload để tạo mã token cuộc gọi

```json
{
  "ipphone": "{{agent_id}}",
  "expired": "2017-07-30 00:00:00"
}
```

**Trong đó:**&#x20;

* `IpPhone` Là số Iphone của Chuyên viên tương ứng (Xem thêm thông tin chuyên viên  ở [chuyen-vien](https://docs.caresoft.vn/danh-muc/restful-api-cua-caresoft/chuyen-vien "mention"))
* `expired` Thời hạn hết hạn của token

2. Lập trình với thư viện JWT để tạo ra chuỗi token  trong hình dưới là cách thức gen  token sử dụng trực tiếp từ trang web jwt.io.  Xem các hướng dẫn và thư viện tương thích trong mục "[Libraries](https://jwt.io/libraries)" của JWT&#x20;

*Ví dụ: Tạo chuỗi token đăng nhập vào hệ thống Caresoft sử dụng giao diện JWT.io*

<figure><img src="https://2193274687-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fw9FEMPuWidjTVayVtnJj%2Fuploads%2FrilrknHfyUk8ntczUJL5%2FJWT_voice.png?alt=media&#x26;token=b2ff5c3d-c0d2-4617-b83e-20dde3783e5e" alt=""><figcaption></figcaption></figure>

Copy chuỗi token JWT lưu thành biến {{token}} \
Ví dụ ở trường hợp trên sẽ là  `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9....e_ZYyfTxg3EDG6vrKxhbZXPx9tQXrLcB5o`

3. Sau khi gen token, CRM gọi  API service bên dưới để login vào Caresoft để đăng ký token với hệ thống.&#x20;

{% code title="Payload body đăng nhập" %}

```json
 {
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.....g3EDG6vrKxhbZXPx9tQXrLcB5o"
  }
```

{% endcode %}

## Đăng nhập VoiceAPI

<mark style="color:green;">`POST`</mark> `https://capi.caresoft.vn/{domain}/thirdParty/login`

#### Request Body

| Name                                    | Type   | Description                             |
| --------------------------------------- | ------ | --------------------------------------- |
| token<mark style="color:red;">\*</mark> | String | {{token}}  Chuỗi token nhận được ở trên |

{% tabs %}
{% tab title="200: OK Thành công" %}

```json
{
    "code": "ok",
    "user": {
        "accountId": 1223,
        "account_id": 1223,
        "userId": "1",
        "email": "sample@gmail.com",
        "name": "Sale Department",
        "username": "Sale Department",
        "agentId": "5000",
        "agent_id": "5000",
        "phone_no": "0864894596",
        "ipAddress": "222.252.98.36",
        "groupId": "10001",
        "role": "1",
        "active": "1"
    }
}
```

{% endtab %}

{% tab title="401: Unauthorized " %}

{% endtab %}

{% tab title="400: Bad Request " %}

{% endtab %}

{% tab title="500: Internal Server Error " %}

{% endtab %}
{% endtabs %}

Mẫu minh họa gọi hàm login từ postman

<figure><img src="https://2193274687-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fw9FEMPuWidjTVayVtnJj%2Fuploads%2FxKLWnUnM4qKnnOKyCPa1%2FLogin.png?alt=media&#x26;token=926c49a3-337d-452e-9b1e-39cb1da65129" alt=""><figcaption></figcaption></figure>

### Lập trình tích hợp tiếp nhận cuộc gọi CareSoft vào CRM

{% hint style="info" %}
**Chú ý:** Nếu bạn lập trình trên localhost, cài extension "Allow Cross Domain" trên Chrome để không bị lỗi Cross Domain.
{% endhint %}

* Import các lib javascript đã được CareSoft cung cấp sẵn vào CRM (trong đó custom.js là file js chứa các hàm CareSoft định nghĩa sẵn để hỗ trợ thông báo, xử lý giao diện, tương tác tuỳ theo nhu cầu của từng CRM)

  ```
  	<script src="https://capi.caresoft.vn/js/embed/jquery.min.js" type="text/javascript"></script>    
  	<script src="https://capi.caresoft.vn/js/embed/jssip-3.2.10.js" type="text/javascript"></script>
  	<script src="https://capi.caresoft.vn/js/embed/init-3.0.7.js" type="text/javascript"></script>
  	<script src="https://capi.caresoft.vn/js/embed/web.push.js" type="text/javascript"></script>
  	<script src="https://capi.caresoft.vn/js/embed/cs_const.js" type="text/javascript"></script>
  	<script src="https://capi.caresoft.vn/js/embed/cs_voice.js" type="text/javascript"></script>
  	<script src="custom.js" type="text/javascript"></script>
  ```
* Copy dòng sau giữa thẻ body:

  ```
  	<video id="my-video" style="display: none;" autoplay playsinline muted></video>
  	<video id="peer-video" style="display: none;" autoplay playsinline></video>
  ```
* Thực hiện việc kết nối với tổng đài CareSoft qua hàm sau:

  ```
    csInit(token, domain);
  ```

Trong đó token là token đã được tạo ra ở bước 1

* Tại một thời điểm, chỉ 1 tab có quyền gọi và tiếp nhận cuộc gọi. Để cho 1 tab bật quyền tiếp nhận cuộc gọi, gọi hàm **csEnableCall();**
* Hệ thống hỗ trợ 2 loại thiết bị nghe gọi:
* Nghe gọi trên trình duyệt
* Nghe gọi qua softphone, IP Phone

Tuỳ thuộc vào loại thiết bị sẽ sử dụng mà chọn thiết bị bằng cách gọi hàm sau:

```
         changeDevice(type);
```

Trong đó: \
\-  `type = 1`: nghe gọi trên trình duyệt \
\- `type = 2`: nghe gọi qua softphone, IP Phone

Trong trường hợp muốn tắt trình duyệt đi mà vẫn tiếp nhận được cuộc gọi qua ipphone/softphone, chuyển `type = 4`.

Trong trường hợp muốn tắt trình duyệt đi mà vẫn tiếp nhận được cuộc gọi qua GSM, chuyển \
`type = 3` và truyền vào số điện thoại di động sẽ tiếp nhận cuộc gọi.

```
           changeDevice(3, '093xxxxxxxx');
```

Sau khi F5, hoặc đăng xuất, không sử dụng nữa thì phải huỷ đăng ký gọi và chuyển về thiết bị gọi mặc định để có thể tiếp tục sử dụng bình thường bằng cách sử dụng đoạn code sau:

```
          $(window).bind('unload', function () {
             csUnregister();
                 if (csVoice.enableVoice) {
                         reConfigDeviceType();
                 }
            });
```

Sau khi kết nối thành công với tổng đài, ngay lúc này đã có thể nhận và gọi điện tới khách hàng. Các hàm dùng để tương tác với hệ thống:

<table><thead><tr><th width="79.33333333333331">STT</th><th width="330">Hàm</th><th>Mô tả</th></tr></thead><tbody><tr><td>1</td><td><code>csEnableCall();</code></td><td>CareSoft chỉ cho phép tại 1 thời điểm có 1 tab được quyền gọi. Vì thế nếu bật nhiều tab hoặc đăng nhập tại nhiều nơi thì cũng chỉ có duy nhất 1 tab được sử dụng chức năng thoại. Tab đầu tiên đăng nhập vào hệ thống sẽ có quyền thoại này. Các tab về sau muốn dùng thì sử dụng hàm này</td></tr><tr><td>2</td><td><code>changeCallStatus();</code></td><td>CareSoft có hỗ trợ trạng thái tiếp nhận cuộc gọi là On/Off. Nếu để trạng thái là On thì cuộc gọi sẽ đổ về. Ngược lại thì sẽ không đổ về. Để chuyển qua lại 2 trạng thái thì sẽ dùng hàm này</td></tr><tr><td>3</td><td><code>csCallout(number,calloutId);</code></td><td>Gọi điện thoại tới số điện thoại của khách hàng (<code>number</code>là số điện thoại cần gọi, <code>calloutId</code> là id dịch vụ gọi ra (optional, nếu không truyền vào sẽ gọi theo dịch vụ mặc định))</td></tr><tr><td>4</td><td><code>onAcceptCall();</code></td><td>Tiếp nhận cuộc gọi đến</td></tr><tr><td>5</td><td><code>endCall();</code></td><td>Kết thúc cuộc gọi</td></tr><tr><td>6</td><td><code>holdCall();</code></td><td>Hold/Unhold cuộc gọi</td></tr><tr><td>7</td><td><code>muteCall();</code></td><td>Mute/Unmute cuộc gọi. Sự kiện này được gọi khi cuộc gọi bị Mute. Hàm này chỉ hoạt động cho cuộc gọi nhận từ khách hàng, không hoạt động cho cuộc gọi ra</td></tr><tr><td>8</td><td><code>changeDevice(type)</code></td><td>Thay đổi thiết bị tiếp nhận cuộc gọi</td></tr><tr><td>9</td><td><code>reConfigDeviceType()</code></td><td>Đối với nhân viên có vai trò Extension, Mobile, Softphone, khi đăng nhập vào để gọi điện sẽ gây bất đồng bộ khiến không thể tiếp nhận cuộc gọi trên các thiết bị mặc định được nữa. Gọi hàm này khi đăng xuất hoặc F5 web tích hợp để chuyển thiết bị nhân viên này về mặc định và tiếp nhận cuộc gọi như bình thường</td></tr><tr><td>10</td><td><code>getTransferAgent()</code></td><td>Lấy thông tin agent đang online</td></tr><tr><td>11</td><td><code>csTransferCallAgent(ipphone)</code></td><td>Chuyển cuộc gọi qua agent khác, <code>ipphone</code> số ipphone của agent muốn chuyển,Chú ý: agent phải đang trong cuộc gọi mới có thể chuyển cuộc gọi</td></tr><tr><td>12</td><td><code>csTransferCallAcd(queueId)</code></td><td>Chuyển cuộc gọi qua nhánh acd khác, <code>queueId</code> id của nhánh acd muốn chuyển,Chú ý: agent phải đang trong cuộc gọi mới có thể chuyển cuộc gọi</td></tr><tr><td>13</td><td><code>responseTransferAgent(action)</code></td><td>Tiếp nhận cuộc gọi được chuyển,<code>action=1</code> đồng ý,<code>action=0</code> từ chối</td></tr><tr><td>14</td><td><code>transferSurvey(survey)</code></td><td><p>Chuyển cuộc gọi sang khảo sát (after call survey). Chỉ hoạt động khi đang trong cuộc gọi và sau khi chuyển thành công cuộc gọi sẽ ngắt. <br>survey : <code>{</code></p><p><code>id: id của survey,</code></p><p><code>sipurl : sipurl</code></p><p><code>}</code></p></td></tr></tbody></table>

> Ngoài ra, khi muốn thay đổi giao diện hoặc xử lý thông tin mỗi khi có một sự kiện của cuộc gọi, CareSoft hỗ trợ các hàm sau

{% hint style="info" %}
**Chú ý:** Tất cả các hàm dưới đây đều phải được implement để đảm bảo tính đúng đắn của chương trình
{% endhint %}

<table><thead><tr><th width="98">STT</th><th width="284">Hàm</th><th>Mô tả</th></tr></thead><tbody><tr><td>1</td><td><code>csCallRinging(phone)</code></td><td>Sự kiện này được gọi khi có cuộc gọi đến nhân viên (phone là số điện thoại gọi đến)</td></tr><tr><td>2</td><td><code>csAcceptCall();</code></td><td>Sự kiện này được gọi khi nhân viên tiếp nhận cuộc gọi</td></tr><tr><td>3</td><td><code>csEndCall();</code></td><td>Sự kiện này được gọi khi cuộc gọi kết thúc.</td></tr><tr><td>4</td><td><code>csMuteCall();</code></td><td>Sự kiện này được gọi khi cuộc gọi bị Mute. Hàm này chỉ hoạt động cho cuộc gọi nhận từ khách hàng, không hoạt động cho cuộc gọi ra</td></tr><tr><td>5</td><td><code>csUnMuteCall();</code></td><td>Sự kiện này được gọi khi cuộc gọi bị unMute. Hàm này chỉ hoạt động cho cuộc gọi nhận từ khách hàng, không hoạt động cho cuộc gọi ra</td></tr><tr><td>6</td><td><code>csHoldCall();</code></td><td>Sự kiện này được gọi khi cuộc gọi bị Hold</td></tr><tr><td>7</td><td><code>csUnHoldCall();</code></td><td>Sự kiện này được gọi khi cuộc gọi bị UnHold</td></tr><tr><td>8</td><td><code>showCalloutInfo(number)</code></td><td>Sự kiện này được gọi khi có nhân viên gọi ra ngoài cho khách hàng (number là số gọi đi)</td></tr><tr><td>9</td><td><code>showCalloutError(errorCode, sipCode)</code></td><td>Sự kiện này được gọi khi có xảy ra lỗi lúc gọi đến khách hàng (errorCode, sipCode sẽ được mô tả trong bảng dưới)</td></tr><tr><td>10</td><td><code>csShowEnableVoice(isEnable)</code></td><td>Sự kiện này xảy ra khi một tab này được kích hoạt hay bị tắt chức năng thoại</td></tr><tr><td>11</td><td><code>csShowCallStatus(status)</code></td><td>Sự kiện này xảy ra khi trạng thái cuộc gọi bị thay đổi</td></tr><tr><td>12</td><td><code>csCustomerAccept()</code></td><td>Sự kiện này được gọi khi khách hàng nghe cuộc gọi đối với cuộc gọi ra</td></tr><tr><td>13</td><td><code>csShowDeviceType(type)</code></td><td>Sự kiện này được gọi khi thông tin loại thiết bị đang dùng để tiếp nhận cuộc gọi thay đổi (1: gọi bằng trình duyệt, 2: nhận cuộc gọi qua IP phone nhưng phải đăng nhập vào mới tiếp nhận được, 4: nhận cuộc gọi qua IP phone nhưng không cần đăng nhập vào)</td></tr><tr><td>14</td><td><code>csCurrentCallId(callId)</code></td><td>Sự kiện xảy ra khi có cuộc gọi đang diễn ra và trả về callId của cuộc gọi</td></tr><tr><td>15</td><td><code>csInitError(errorCode)</code></td><td>Sự kiện này xảy ra khi thiết lập thông số không thành công và sẽ trả về mã lỗi(token lỗi, lỗi kết nối,…)</td></tr><tr><td>16</td><td><code>csInitComplete()</code></td><td>Sự kiện được gọi khi thiết lập thành công mọi thông số và đã sẵn sang để bắt đầu nhận hay tiếp nhận cuôc gọi <br><strong>Lưu ý</strong> : Trong trường hợp muốn kích hoạt thoại luôn thì sẽ gọi hàm <strong>csEnableCall()</strong> trong hàm này</td></tr><tr><td>17</td><td><code>csListTransferAgent(listTransferAgent)</code></td><td>Sự kiện được gọi khi lấy thành công list agent đang online</td></tr><tr><td>18</td><td><code>csTransferCallError(error, tranferedAgentInfo)</code></td><td>Sự kiện được gọi khi chuyển cuộc gọi thất bại. <code>error</code> là mã lỗi trả về, <code>tranferedAgentInfo</code> là thông tin agent được chuyển (có thể null)</td></tr><tr><td>19</td><td><code>csTransferCallSuccess(tranferedAgentInfo)</code></td><td>Sự kiện được gọi khi chuyển cuộc gọi thành công. <code>tranferedAgentInfo</code> là thông tin agent được chuyển</td></tr><tr><td>20</td><td><code>csNewCallTransferRequest(transferCall)</code></td><td>Sự kiện được gọi khi có một cuộc gọi được chuyển</td></tr><tr><td>21</td><td><code>csTransferCallResponse(status)</code></td><td>Sự kiện được gọi khi người được chuyển cuộc gọi bấm từ chối hoặc tiếp nhận yêu cầu. <code>status=OK</code> tiếp nhận,<code>status=NOK</code> từ chối</td></tr><tr><td>22</td><td><code>csTransferSurveyResponse(status)</code></td><td>Sự kiện được gọi khi nhận được kết quả gửi khảo sát (<code>status=true</code> thành công, <code>status=false</code> thất bại)</td></tr><tr><td>23</td><td><code>csNotifyReconnecting(retry,maxRetry)</code></td><td>Sự kiện được gọi khi socket bị mất kết nối và đang kết nối lại <br><code>retry</code> Lần retry hiện tại<br><code>maxRetry</code> Số lần retry tối đa</td></tr><tr><td>24</td><td><code>csOndisconnected()</code></td><td>Sự kiện được gọi khi socket đã thất bại quá số lần retry tối đa ở function<code>csNotifyReconnecting</code></td></tr></tbody></table>

{% hint style="info" %}
**Chú ý:** csTransferCallError,csTransferCallSuccess,csNewCallTransferRequest chỉ áp dụng cho cuộc gọi chuyển từ agent sang agent , không áp dụng cho cuộc gọi chuyển nhánh, cuộc gọi chuyển nhánh sẽ hoạt động như 1 cuộc gọi vào bình thường
{% endhint %}

**Các hàm này đã được để trong file custom.js**

### Phụ lục: Bảng mã lỗi khi gọi ra

<table><thead><tr><th width="85">STT</th><th width="242">Error Code</th><th width="140">Sip Code</th><th>Mô tả</th></tr></thead><tbody><tr><td>1</td><td>IPCC_NOT_CONNECT_CUSTOMER</td><td></td><td>Gọi ra thất bại, chi tiết trong các mã sip Code bên dưới</td></tr><tr><td>2</td><td></td><td>486</td><td>Khách hàng đang bận</td></tr><tr><td>3</td><td></td><td>408</td><td>Khách hàng không nghe máy hoặc có lỗi xảy ra</td></tr><tr><td>4</td><td></td><td>403</td><td>Lỗi đầu số, xin vui lòng liên hệ đơn vị cung cấp đầu số để được hỗ trợ</td></tr><tr><td>5</td><td></td><td>487</td><td>Khách hàng không nghe máy</td></tr><tr><td>6</td><td></td><td>480</td><td>Khách hàng không nghe máy</td></tr><tr><td>7</td><td></td><td>404</td><td>Không kết nối được đến số thuê bao, xin vui lòng liên hệ đơn vị cung cấp đầu số để được hỗ trợ</td></tr><tr><td>8</td><td></td><td>500</td><td>Có lỗi xảy ra, xin vui lòng liên hệ đơn vị cung cấp đầu số để được hỗ trợ</td></tr><tr><td>9</td><td></td><td>600 || 603</td><td>Khách không nghe máy hoặc đang bận</td></tr><tr><td>10</td><td></td><td>703</td><td>Số gọi ra bị Caresoft chặn</td></tr><tr><td>11</td><td></td><td>786</td><td>Lỗi trạng thái nghe gọi, gen token mới hoặc on/off lại trạng thái</td></tr><tr><td>12</td><td>Các mã khác</td><td></td><td>Có lỗi xảy ra</td></tr><tr><td>13</td><td>IPCC_NOT_CONNECT_AGENT_ID</td><td></td><td>Không thể kết nối được đến thiết bị nghe gọi của tư vấn viênkiểm tra lại IP Phone/SoftPhone và đường truyền Internet</td></tr><tr><td>14</td><td>CALLOUT_AGENT_BUSY</td><td></td><td>Busy. Vui lòng đăng xuất và đăng nhập lại để tiếp tục sử dụng.</td></tr><tr><td>15</td><td>CALLOUT_PERMISSION_DENY</td><td></td><td>Không có quyền gọi ra. Liên hệ Admin để cấu hình</td></tr><tr><td>16</td><td>ERROR_CALLOUT_CONNECT</td><td></td><td>Có lỗi xảy ra</td></tr><tr><td>17</td><td>ERROR_CALLOUT_NUMBER_BLOCKED</td><td></td><td>Số điện thoại gọi ra bị chặn theo cấu hình</td></tr></tbody></table>

**Bảng mã lỗi khi chuyển cuộc gọi**

<table><thead><tr><th width="74">STT</th><th width="253">Error Code</th><th>Mô tả</th></tr></thead><tbody><tr><td>1</td><td>NOT_CHOOSE_TYPE_TRANSFER</td><td>Chưa chọn loại chuyển cuộc gọi</td></tr><tr><td>2</td><td>TRANSFER_CALL_FAILED</td><td>Chuyển cuộc gọi thất bại</td></tr><tr><td>3</td><td>NOT_IN_A_CALL</td><td>Không trong cuộc gọi</td></tr><tr><td>4</td><td>COULD_NOT_GET_CALL_INFO</td><td>Không thể lấy thông tin cuộc gọi</td></tr></tbody></table>

**Các thuộc tính trong csVoice**

<table><thead><tr><th width="86">STT</th><th width="224">Thuộc tính</th><th width="122">Kiểu dữ liệu</th><th>Mô tả</th></tr></thead><tbody><tr><td>1</td><td><code>isCallout</code></td><td><code>bool</code></td><td>Có phải là cuộc gọi ra hay không</td></tr><tr><td>2</td><td><code>deviceType</code></td><td><code>int</code></td><td>thiết bị hiện tại, có 5 loại : <br><code>1</code> = web<br><code>2</code> = ipphone/softphone (phải đăng nhập)<br><code>3</code> = divert di động <br><code>4</code> =  ipphone/softphone (không cần đăng nhập)<br><code>8</code> = Ứng dụng CareSoft</td></tr><tr><td>2</td><td><code>enableVoice</code></td><td><code>bool</code></td><td>Trạng thái kích hoạt thoại</td></tr><tr><td>3</td><td><code>isMute</code></td><td><code>bool</code></td><td>Đang mute cuộc gọi hay không</td></tr><tr><td>4</td><td><code>isHold</code></td><td><code>bool</code></td><td>Đang hold cuộc gọi hay không</td></tr><tr><td>5</td><td><code>getCalloutServices</code></td><td><code>func</code></td><td>Trả về danh sách các dịch vụ gọi ra mà agent hiện tại được gán quyền. Danh sách trả về gồm 1 mảng object có cấu trúc sau: <br><code>{</code><br> <code>"callout_id": Id dịch vụ,</code><br> <code>"agent_id":ipphone của agent hiện tại,</code><br> <code>"is_default":có phải dịch vụ mặc định không,</code><br> <code>"descriptions":Mô tả</code><br> <code>}</code> </td></tr></tbody></table>

{% hint style="info" %}
**Tham khảo**: \
Code demo  <https://gitlab.com/caresoftpublic/Demo-Embed-VoiceAPI>
{% endhint %}
