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ả
nameCóstring, max:255Tên script
template_textCótextTemplate với placeholder
voice_idCóstringID 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ông truyền trong request. Hệ thống tự routing theo voice_id -> tier tương ứng (A premium / B local). Response 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_idChuyển sang script khác và phát lại từ đầu.
queuetarget_queue_name (= name định danh queue)Chuyển vào queue, agent live tiếp nhận
hangup—Ngắt máy ngay
repeat_script—Phát lại toàn bộ script hiện tại.

Lấy enum động qua API

Thay vì khai báo cố định 6 action type trong ứng dụng tích hợp, 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 để dùng để tạo danh sách lựa chọn "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. Dùng để tạo danh sách lựa chọn "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, cuộc gọi sẽ thất bại 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. Ứng dụng tích hợp không nên phụ thuộc vào thứ tự key. — parse theo key name. tenant_id xuất hiện trong cả script level và mỗi dtmf_actions[] item — Lặp lại ở cả hai entity, đây là hành vi thiết kế của hệ thống. vì cả 2 entity đều có tenant_id.

List / show / update / xoá ​

EndpointMục đích
GET /api/autocall/scripts?status=activeList scripts (bộ lọc (tuỳ chọn))
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 -> thay thế toàn bộ
DELETE /api/autocall/scripts/{id}Soft xoá (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 đã render sẵn. đã render sẵn. (template không-biến) đã render. Có thể bỏ qua khi tích hợp nếu không sử dụng.—
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). Ứng dụng tích hợp parse theo chuẩn ISO 8601. (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 (cập nhật từng phần — 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ệ
idintegerCóID 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} để thay thế placeholder bằng giá trị tương ứng 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ộ để phát trực tiếp 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.