Skip to content

Scripts + DTMF actions

Quản lý kịch bản TTS (template + voice + DTMF actions) cho AutoCall. Bao gồm CRUD scripts, enum DTMF action types, danh sách queue helper và preview TTS.

Tạo script

http
POST /api/autocall/scripts

Body:

json
{
  "name": "Chăm sóc chuẩn",
  "description": "Template cho campaign chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
  "template_text": "Xin chào {{salutation_name}}, chúng tôi xin nhắc bạn về lịch hẹn ngày {{callback_date}}. Vui lòng bấm phím 1 để xác nhận tham gia, phím 2 để chuyển nhân viên hỗ trợ, phím 9 để bỏ qua.",
  "voice_id": "voice_id_premium_02",
  "tts_tier": "B",
  "max_repeat_per_attempt": 1,
  "status": "draft",
  "dtmf_actions": [
    {
      "digit": "1",
      "label": "Xác nhận thanh toán",
      "action_type": "playback_then_hangup",
      "target_audio_url": "/media/thank_you.wav"
    },
    {
      "digit": "2",
      "label": "Chuyển nhân viên",
      "action_type": "queue",
      "target_queue_name": "support@default"
    },
    {
      "digit": "9",
      "label": "Bỏ qua",
      "action_type": "hangup"
    }
  ]
}

Fields:

FieldRequiredTypeMô tả
namestring, max:255Tên script
template_texttextTemplate với placeholder
voice_idstringID giọng đọc — PHẢI lấy từ GET /api/autocall/voices (Voices). Hệ thống tự routing đến provider tương ứng. Truyền voice_id không tồn tại trong thư viện voice khả dụng -> 422.
tts_tierKhôngenum (A/B, default B)A = premium, render realtime từ nhà cung cấp TTS cao cấp với key của khách hàng, tính theo char usage. B = local unlimited, dùng thư viện voice tiếng Việt khả dụng sẵn, không phí, throughput cao. Khuyến nghị default B cho khối lượng lớn.
max_repeat_per_attemptKhônginteger (1-5, default 1)Số lần phát lại audio nếu user không bấm DTMF
statusKhôngenum (draft/active/archived, default draft)Chỉ script active mới được engine sử dụng cho campaign chạy thực tế
dtmf_actions[]Khôngarray max:12Cấu hình phím bấm, mỗi item gồm digit/label/action_type/target_*

Field voice_provider KHÔNG có trong request body

Khách hàng KHÔNG truyền. Hệ thống tự routing theo voice_id -> tier tương ứng (A premium / B local). Response server vẫn TRẢ field này — read-only, cho biết provider đã được sử dụng.

Error 422 voice_id không tồn tại:

json
{
  "message": "The given data was invalid.",
  "errors": {
    "voice_id": [
      "voice_id 'voice_id_premium_01' không tồn tại trong thư viện voice khả dụng. Lấy danh sách voice hợp lệ qua GET /api/autocall/voices."
    ]
  }
}

DTMF action types

action_typeField bắt buộc kèm theoHành vi
playbacktarget_audio_urlPhát file rồi chờ DTMF tiếp
playback_then_hanguptarget_audio_urlPhát file rồi ngắt máy
switch_scripttarget_script_idLoad script khác, replay từ đầu
queuetarget_queue_name (= name định danh queue)Chuyển vào queue, agent live tiếp nhận
hangupNgắt máy ngay
repeat_scriptReplay full audio script hiện tại

Lấy enum động qua API

Thay vì hardcode 6 action type ở client, KH có thể fetch enum đầy đủ từ GET /api/autocall/dtmf-action-types (xem mục List action types). Tương tự, danh sách target_queue_name lấy từ GET /api/autocall/queues (xem List queue).

Validate ràng buộc tham chiếu (server-side)

  • playback / playback_then_hangup -> target_audio_url PHẢI khớp file_path của 1 media file thuộc tài khoản (lấy từ GET /api/media-files). Truyền URL ngoài -> 422.
  • switch_script -> target_script_id PHẢI là script tồn tại trong tài khoản (lấy từ GET /api/autocall/scripts). Truyền ID rác -> 422.
  • queue -> target_queue_name PHẢI khớp name của queue active (lấy từ GET /api/autocall/queues). Sai -> 422.

Error 422 sample:

json
{
  "message": "The given data was invalid.",
  "errors": {
    "dtmf_actions.0.target_audio_url": [
      "target_audio_url '/data/wrong/path.wav' không tồn tại trong Media Files của khách hàng. Upload file qua POST /api/media-files trước, rồi dùng file_path trả về."
    ]
  }
}

List action types cho DTMF (enum + label)

http
GET /api/autocall/dtmf-action-types

Mục đích: trả về enum 6 action type kèm label tiếng Việt + danh sách field bắt buộc kèm theo. KH integration dùng để build dropdown "Hành vi khi bấm phím" trong UI cấu hình DTMF mà không cần hardcode enum.

Response 200:

json
{
  "data": [
    {
      "code": "playback",
      "label": "Phát audio",
      "description": "Phát file âm thanh URL chỉ định, sau đó tiếp tục kịch bản.",
      "required_fields": ["target_audio_url"]
    },
    {
      "code": "playback_then_hangup",
      "label": "Phát audio rồi cúp máy",
      "description": "Phát file âm thanh xong tự động cúp máy.",
      "required_fields": ["target_audio_url"]
    },
    {
      "code": "switch_script",
      "label": "Chuyển sang kịch bản khác",
      "description": "Chuyển cuộc gọi sang kịch bản AutoCall khác (chuỗi IVR).",
      "required_fields": ["target_script_id"]
    },
    {
      "code": "queue",
      "label": "Chuyển vào hàng đợi agent",
      "description": "Chuyển cuộc gọi vào hàng đợi để agent live tiếp nhận.",
      "required_fields": ["target_queue_name"]
    },
    {
      "code": "repeat_script",
      "label": "Lặp lại kịch bản",
      "description": "Phát lại kịch bản hiện tại từ đầu (cho khách nghe lại).",
      "required_fields": []
    },
    {
      "code": "hangup",
      "label": "Cúp máy",
      "description": "Kết thúc cuộc gọi ngay lập tức.",
      "required_fields": []
    }
  ]
}

Field response:

FieldTypeMô tả
codestringGiá trị enum dùng cho dtmf_actions[].action_type khi POST/PUT /scripts
labelstringNhãn tiếng Việt hiển thị trong dropdown UI
descriptionstringMô tả ngắn để giải thích thêm cho user end
required_fieldsstring[]Danh sách field bắt buộc đi kèm khi pick action này (vd playback -> bắt buộc kèm target_audio_url)

List queue cho action_type=queue

http
GET /api/autocall/queues

Mục đích: trả về danh sách queue active để client dùng làm target_queue_name khi pick action_type=queue. Build dropdown "Chọn hàng đợi" trong UI.

Response 200:

json
{
  "data": [
    {
      "id": 1,
      "name": "support_q1",
      "queue_number": "2001",
      "strategy": "ring_all"
    },
    {
      "id": 2,
      "name": "sales_outbound",
      "queue_number": "2002",
      "strategy": "longest_idle_agent"
    }
  ]
}

Field response:

FieldTypeMô tả
idintegerID queue trong DB (tham khảo)
namestringTên kỹ thuật của queue — DÙNG làm target_queue_name khi POST/PUT script với DTMF queue action
queue_numberstringSố extension queue (vd 2001) — dùng hiển thị cho user end
strategystringChiến lược phân phối: ring_all / longest_idle_agent / round_robin / ...

Lưu ý quan trọng

Field target_queue_name trong dtmf_actions[] PHẢI khớp name của queue (không phải queue_number hay id). Nếu sai, dial sẽ fail vì hệ thống không nhận diện được queue.

Response 201 mẫu sau khi tạo script

json
{
  "data": {
    "name": "Chăm sóc chuẩn",
    "description": "Template cho campaign chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
    "template_text": "Xin chào {{tong_can_thanh_toan}}, chúng tôi xin nhắc bạn về lịch hẹn ngày {{ngay_den_han}}. Vui lòng bấm phím 1 để xác nhận tham gia, phím 2 để chuyển nhân viên hỗ trợ, phím 9 để bỏ qua.",
    "voice_provider": "tts_provider_01",
    "voice_id": "evln-vi-nature",
    "tts_tier": "A",
    "max_repeat_per_attempt": 1,
    "status": "draft",
    "created_by": 3,
    "tenant_id": 1,
    "updated_at": "2026-06-25T04:37:49.000000Z",
    "created_at": "2026-06-25T04:37:49.000000Z",
    "id": 4,
    "dtmf_actions": [
      {
        "id": 1,
        "tenant_id": 1,
        "script_id": 4,
        "digit": "1",
        "label": "Xác nhận thanh toán",
        "action_type": "playback_then_hangup",
        "target_audio_url": "/media/thank_you.wav",
        "target_script_id": null,
        "target_queue_name": null,
        "sort_order": 0,
        "created_at": "2026-06-25T04:37:49.000000Z",
        "updated_at": "2026-06-25T04:37:49.000000Z"
      },
      {
        "id": 2,
        "tenant_id": 1,
        "script_id": 4,
        "digit": "2",
        "label": "Chuyển nhân viên",
        "action_type": "queue",
        "target_audio_url": null,
        "target_script_id": null,
        "target_queue_name": "support@default",
        "sort_order": 1,
        "created_at": "2026-06-25T04:37:49.000000Z",
        "updated_at": "2026-06-25T04:37:49.000000Z"
      },
      {
        "id": 3,
        "tenant_id": 1,
        "script_id": 4,
        "digit": "9",
        "label": "Bỏ qua",
        "action_type": "hangup",
        "target_audio_url": null,
        "target_script_id": null,
        "target_queue_name": null,
        "sort_order": 2,
        "created_at": "2026-06-25T04:37:49.000000Z",
        "updated_at": "2026-06-25T04:37:49.000000Z"
      }
    ]
  }
}

Field order + tenant_id

Hệ thống serialize field theo thứ tự insert/create + auto-injected (created_at/updated_at/id) ở cuối. Client KHÔNG dựa thứ tự key — parse theo key name. tenant_id xuất hiện trong cả script level và mỗi dtmf_actions[] item — redundant nhưng đúng vì cả 2 entity đều có tenant_id.

List / show / update / delete

EndpointMục đích
GET /api/autocall/scripts?status=activeList scripts (filter optional)
GET /api/autocall/scripts/{id}Chi tiết kèm dtmfActions
PUT /api/autocall/scripts/{id}Cập nhật. Nếu gửi dtmf_actions -> replace toàn bộ
DELETE /api/autocall/scripts/{id}Soft delete (nếu đang dùng cho campaign -> 422)

GET /api/autocall/scripts — list

Query params:

FieldTypeRequiredMô tảGiá trị hợp lệ
statusstringKhôngLọc theo trạng thái scriptdraft / active / archived
json
{
  "data": [
    {
      "id": 2,
      "tenant_id": 1,
      "name": "Nhắc lịch hẹn",
      "description": null,
      "template_text": "Công ty ABC trân trọng thông báo lịch hẹn dịch vụ {{ngay_den_han}}  xác nhận tham gia trước 18 giờ {{ngay_den_han}}. Trường hợp đã xác nhận, vui lòng bỏ qua cuộc gọi này. Liên hệ tổng đài ấn phím 3. Xin cảm ơn",
      "voice_provider": "tts_provider_01",
      "voice_id": "voice_id_premium_02",
      "tts_tier": "B",
      "audio_fixed_paths": null,
      "max_repeat_per_attempt": 1,
      "status": "active",
      "created_by": 1,
      "created_at": "2026-06-22T03:34:01.000000Z",
      "updated_at": "2026-06-22T08:11:49.000000Z",
      "deleted_at": null
    }
  ]
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data[].idintegerID scriptSố nguyên dương
data[].tenant_idintegerID khách hàng sở hữu script
data[].namestringTên scriptTối đa 255 ký tự
data[].descriptionstring | nullMô tảTối đa 2000 ký tự
data[].template_texttextTemplate TTS với placeholder
data[].voice_providerstringProvider TTS (read-only, derive từ voice_id)tts_provider_01 / local / ...
data[].voice_idstringID giọng đọc TTSLấy từ GET /api/autocall/voices
data[].tts_tierstringTier TTSA (premium realtime) / B (local pre-rendered)
data[].audio_fixed_pathsobject | nullCache các đoạn audio cố định (template không-biến) đã render. Không bắt buộc xử lý khi tích hợp
data[].max_repeat_per_attemptintegerSố lần phát lại audio nếu user không bấm DTMF1-5
data[].statusstringTrạng thái scriptdraft / active / archived
data[].created_byintegerUser ID người tạo
data[].created_atdatetime ISO 8601Thời điểm tạo (UTC, microseconds)
data[].updated_atdatetime ISO 8601Thời điểm cập nhật gần nhất (UTC, microseconds)
data[].deleted_atdatetime ISO 8601 | nullSoft delete timestampnull nếu chưa xoá

Format timestamp

API trả ISO 8601 với microseconds YYYY-MM-DDTHH:mm:ss.uuuuuuZ (vd 2026-06-22T03:34:01.000000Z). Client parse theo ISO 8601 chuẩn (microseconds optional).

GET /api/autocall/scripts/{id} — chi tiết

Object đầy đủ kèm dtmf_actions[]:

json
{
  "data": {
    "id": 2,
    "tenant_id": 1,
    "name": "Nhắc lịch hẹn",
    "description": null,
    "template_text": "Công ty ABC trân trọng thông báo lịch hẹn dịch vụ {{ngay_den_han}}, với số tiền là {{tong_can_thanh_toan}}. ... Xin cảm ơn",
    "voice_provider": "tts_provider_01",
    "voice_id": "voice_id_premium_02",
    "tts_tier": "B",
    "audio_fixed_paths": null,
    "max_repeat_per_attempt": 1,
    "status": "active",
    "created_by": 1,
    "created_at": "2026-06-22T03:34:01.000000Z",
    "updated_at": "2026-06-22T08:11:49.000000Z",
    "deleted_at": null,
    "dtmf_actions": [
      {
        "id": 7,
        "script_id": 2,
        "digit": "1",
        "label": "Xác nhận thanh toán",
        "action_type": "playback_then_hangup",
        "target_audio_url": "/media/thank_you.wav",
        "target_script_id": null,
        "target_queue_name": null,
        "sort_order": 0,
        "created_at": "2026-06-22T03:34:01.000000Z",
        "updated_at": "2026-06-22T03:34:01.000000Z"
      },
      {
        "id": 8,
        "script_id": 2,
        "digit": "3",
        "label": "Chuyển tổng đài",
        "action_type": "queue",
        "target_audio_url": null,
        "target_script_id": null,
        "target_queue_name": "support_queue",
        "sort_order": 1,
        "created_at": "2026-06-22T03:34:01.000000Z",
        "updated_at": "2026-06-22T03:34:01.000000Z"
      }
    ]
  }
}

PUT /api/autocall/scripts/{id} — cập nhật

Request:

http
PUT /api/autocall/scripts/2
Content-Type: application/json
Authorization: Bearer <token>

Body (partial update — chỉ gửi field cần đổi; nếu gửi dtmf_actions thì hệ thống xoá toàn bộ DTMF cũ + tạo lại theo array mới):

json
{
  "name": "Nhắc nợ đến hạn — v2",
  "description": "Cập nhật template cho Q3-2026",
  "voice_id": "voice_id_premium_01",
  "max_repeat_per_attempt": 2,
  "dtmf_actions": [
    {
      "digit": "1",
      "label": "Xác nhận thanh toán",
      "action_type": "playback_then_hangup",
      "target_audio_url": "/media/thank_you.wav"
    },
    {
      "digit": "3",
      "label": "Chuyển tổng đài",
      "action_type": "queue",
      "target_queue_name": "support@default"
    }
  ]
}

Response 200 (full script đã update + dtmf_actions[] mới):

json
{
  "data": {
    "id": 2,
    "tenant_id": 1,
    "name": "Nhắc nợ đến hạn — v2",
    "description": "Cập nhật template cho Q3-2026",
    "template_text": "Công ty ABC trân trọng thông báo lịch hẹn dịch vụ {{ngay_den_han}}, với số tiền là {{tong_can_thanh_toan}}. ... Xin cảm ơn",
    "voice_provider": "tts_provider_01",
    "voice_id": "voice_id_premium_01",
    "tts_tier": "B",
    "audio_fixed_paths": null,
    "max_repeat_per_attempt": 2,
    "status": "active",
    "created_by": 1,
    "created_at": "2026-06-22T03:34:01.000000Z",
    "updated_at": "2026-06-25T04:49:00.000000Z",
    "deleted_at": null,
    "dtmf_actions": [
      {
        "id": 9,
        "tenant_id": 1,
        "script_id": 2,
        "digit": "1",
        "label": "Xác nhận thanh toán",
        "action_type": "playback_then_hangup",
        "target_audio_url": "/media/thank_you.wav",
        "target_script_id": null,
        "target_queue_name": null,
        "sort_order": 0,
        "created_at": "2026-06-25T04:49:00.000000Z",
        "updated_at": "2026-06-25T04:49:00.000000Z"
      },
      {
        "id": 10,
        "tenant_id": 1,
        "script_id": 2,
        "digit": "3",
        "label": "Chuyển tổng đài",
        "action_type": "queue",
        "target_audio_url": null,
        "target_script_id": null,
        "target_queue_name": "support@default",
        "sort_order": 1,
        "created_at": "2026-06-25T04:49:00.000000Z",
        "updated_at": "2026-06-25T04:49:00.000000Z"
      }
    ]
  }
}

PUT replace DTMF

Nếu body có gửi dtmf_actions[], hệ thống xoá toàn bộ DTMF cũ của script + tạo lại theo array mới. Nếu KHÔNG gửi dtmf_actions -> giữ nguyên DTMF cũ. -> IDs của DTMF mới sẽ KHÁC với IDs cũ (cũ đã bị delete).

DELETE /api/autocall/scripts/{id}

json
{ "data": { "deleted": true } }

Response 422 (script đang dùng cho campaign active):

json
{ "message": "Không thể xoá script đang được campaign sử dụng. Hãy archive campaign trước." }

Preview TTS — render demo

http
POST /api/autocall/scripts/{id}/preview

Path params:

FieldTypeRequiredMô tảGiá trị hợp lệ
idintegerID script cần previewSố nguyên dương tồn tại trong tài khoản

Body (optional):

json
{
  "variables": {
    "salutation_name": "anh An",
    "amount": "500000",
    "callback_date": "2026-06-30"
  }
}

Body fields:

FieldTypeRequiredMô tảGiá trị hợp lệ
variablesobjectKhôngMap {variable_code: value} để substitute placeholder trong template. Bỏ trống -> hệ thống dùng giá trị demo mặc định theo data_type của mỗi biếnKey phải khớp variable_code đã định nghĩa

Response 200:

json
{
  "data": {
    "audio_url": "/api/autocall/scripts/12/preview-audio/preview_abc123.wav",
    "duration_sec": 12.4,
    "chars_used": 187
  }
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data.audio_urlstringĐường dẫn nội bộ để stream file wav preview. GET với Authorization header trả binary audio/wavPath relative cho app.zorio.vn
data.duration_secnumberThời lượng audio đã render (giây)> 0, làm tròn 1 chữ số thập phân
data.chars_usedintegerSố ký tự đã sử dụng (tính phí với Tier A)>= 0

Rate limit preview

TTS preview giới hạn 10 req/phút/token (vì charge TTS provider tier A).

Cấp phép theo điều khoản sử dụng của Zorio.