Skip to content

Script + Outcome

Nhóm endpoint cấu hình kịch bản hội thoại (Call Scripts) và bộ mã kết quả cuộc gọi (Disposition Codes). Đây là 2 thành phần cốt lõi mà agent dùng trong mỗi cuộc gọi: script hướng dẫn cách nói, disposition phân loại kết quả khi kết thúc.

5. Call Scripts

EndpointMục đích
GET /api/telesales/scriptsDanh sách script của khách hàng
GET /api/telesales/scripts/{id}Chi tiết script kèm các placeholder được phát hiện
POST /api/telesales/scriptsTạo script mới
PUT /api/telesales/scripts/{id}Cập nhật script
DELETE /api/telesales/scripts/{id}Xoá script
POST /api/telesales/scripts/{id}/previewPreview nội dung với lead + agent thật

Script dùng cú pháp Markdown. Biến được tự động phát hiện từ nội dung — không cần khai báo riêng. Tại thời điểm agent mở lead, biến được thay thế bằng giá trị thực từ custom_fields của lead + profile của agent. Các placeholder chuyên ngành (số hợp đồng, account ID, mã đơn hàng, ...) được khai báo qua custom_fields của từng khách hàng.

GET /api/telesales/scripts — danh sách

Query parameters

FieldTypeRequiredMô tảGiá trị hợp lệ
qstringoptionalTìm theo name (LIKE %keyword%)
campaign_idintegeroptionalLọc các script đang được chiến dịch này dùng
pageintegeroptionalSố trang≥ 1 (default 1)
per_pageintegeroptionalBản ghi mỗi trang1-200 (default 50)

Response fields (mỗi item trong data[])

FieldTypeMô tảGiá trị hợp lệ
idintegerID script
namestringTên script (unique trong tài khoản)max 150 ký tự
content_previewstringNội dung Markdown — cắt còn 200 ký tự đầu
placeholders_detected[]array<string>Danh sách biến phát hiện trong nội dung
used_by_campaigns[]array<integer>Mảng ID chiến dịch đang gắn script này
created_atdatetimeThời điểm tạo (ISO 8601 UTC)
updated_atdatetimeLần cập nhật cuối (ISO 8601 UTC)

Response 200:

json
{
  "data": [
    {
      "id": 12,
      "name": "Default Outbound Script",
      "content_preview": "## Opening\nHello {{customer_name}}...",
      "placeholders_detected": ["customer_name", "agent_name", "brand_name"],
      "used_by_campaigns": [36, 42],
      "created_at": "2026-06-06T06:00:00Z",
      "updated_at": "2026-06-06T06:00:00Z"
    }
  ],
  "meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 3 }
}

content_preview được cắt còn 200 ký tự đầu. Dùng GET /api/telesales/scripts/{id} để lấy nội dung đầy đủ.

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

Path parameter

FieldTypeMô tả
idintegerID script

Response fields

FieldTypeMô tảGiá trị hợp lệ
idintegerID script
namestringTên scriptmax 150 ký tự
contentstringToàn bộ nội dung Markdownmax 32 KB
placeholders_detected[]array<string>Biến được parse từ content
used_by_campaigns[]array<object>Mảng object {id, name} của chiến dịch đang gắn script
created_atdatetimeThời điểm tạo (ISO 8601 UTC)
updated_atdatetimeLần cập nhật cuối (ISO 8601 UTC)

Response 200:

json
{
  "data": {
    "id": 12,
    "name": "Default Outbound Script",
    "content": "## Opening\nHello {{customer_name}}, this is {{agent_name}} from {{brand_name}}...\n\n## Body\n... talk track ...\n\n## Closing\nThank you.",
    "placeholders_detected": ["customer_name", "agent_name", "brand_name"],
    "used_by_campaigns": [
      { "id": 36, "name": "Summer Promo" },
      { "id": 42, "name": "Retention Q3" }
    ],
    "created_at": "2026-06-06T06:00:00Z",
    "updated_at": "2026-06-06T06:00:00Z"
  }
}

Response 404: { "message": "Script not found." }

POST /api/telesales/scripts — tạo

Request body fields

FieldTypeRequiredMô tảGiá trị hợp lệ
namestringTên script — unique trong tài khoảnmax 150 ký tự
contentstringNội dung Markdown, biến tự phát hiệnmax 32 KB

Body:

json
{
  "name": "Default Outbound Script",
  "content": "## Opening\nHello {{customer_name}}, this is {{agent_name}} from {{brand_name}}...\n\n## Body\n... talk track for the campaign ...\n\n## Closing\nThank you for your time."
}

Response fields

FieldTypeMô tảGiá trị hợp lệ
idintegerID script vừa tạo
namestringTên script
contentstringNội dung Markdown
placeholders_detected[]array<string>Biến parse được từ content
created_atdatetimeThời điểm tạo (ISO 8601 UTC)

Response 201:

json
{
  "data": {
    "id": 12,
    "name": "Default Outbound Script",
    "content": "## Opening\nHello {{customer_name}}...",
    "placeholders_detected": ["customer_name", "agent_name", "brand_name"],
    "created_at": "2026-06-06T06:00:00Z"
  }
}

Response 422 — các trường hợp validate sai:

json
// Trùng tên script
{
  "message": "The given data was invalid.",
  "errors": { "name": ["The name has already been taken."] }
}

// Nội dung quá dài
{
  "message": "The given data was invalid.",
  "errors": { "content": ["The content may not be greater than 32768 characters."] }
}

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

Body: giống POST.

Response 200: object script đã cập nhật, kèm placeholders_detected được tính lại từ nội dung mới.

Response 422 — xung đột:

json
// Script đang được chiến dịch active dùng — vẫn cho cập nhật nhưng kèm cảnh báo
{
  "data": { "id": 12, ... },
  "warnings": ["Script is currently used by 2 active campaigns. Agents will see new content on next call."]
}

DELETE /api/telesales/scripts/{id}

Response 204 No Content (thành công).

Response 422 — script đang được chiến dịch active dùng:

json
{
  "message": "Cannot delete a script that is in use by a campaign.",
  "errors": { "id": ["Script 12 is bound to campaign(s): 36, 42. Detach from campaign first."] }
}

POST /api/telesales/scripts/{id}/preview — render preview

Path parameter

FieldTypeMô tả
idintegerID script cần render preview

Request body fields

FieldTypeRequiredMô tảGiá trị hợp lệ
lead_idintegerID lead — lấy giá trị cho từ custom_fieldsPhải tồn tại
agent_idintegerID user agent — lấy giá trị cho từ profilePhải tồn tại

Body:

json
{
  "lead_id":   78912,
  "agent_id":  25
}

Render nội dung với từng được thay bằng giá trị thực từ lead.custom_fields + profile của agent + brand của khách hàng.

Response fields

FieldTypeMô tảGiá trị hợp lệ
idintegerID script
rendered_contentstringNội dung Markdown đã thay placeholder
missing_placeholders[]array<string>Biến không resolve được từ custom_fields / profile — UI có thể cảnh báo agent

Response 200:

json
{
  "data": {
    "id": 12,
    "rendered_content": "## Opening\nHello Anh Hung, this is Nguyen Van A from Zorio...\n\n## Body\n...",
    "missing_placeholders": ["policy_number"]
  }
}

6. Call Outcomes (Disposition Codes)

6.1 Quản lý catalog disposition

EndpointMục đích
GET /api/telesales/dispositionsDanh sách disposition của khách hàng
GET /api/telesales/dispositions/{id}Chi tiết một disposition
POST /api/telesales/dispositionsTạo disposition (admin)
PUT /api/telesales/dispositions/{id}Cập nhật disposition (admin)
DELETE /api/telesales/dispositions/{id}Xoá disposition (admin)

GET /api/telesales/dispositions — danh sách

Query parameters
FieldTypeRequiredMô tảGiá trị hợp lệ
categorystringoptionalLọc theo nhóm kết quảcontact / no_contact / callback / remove
is_activebooloptionalCó bao gồm cả disposition đã ẩn khôngtrue (default) / false
Response fields (mỗi item trong data[])
FieldTypeMô tảGiá trị hợp lệ
idintegerID disposition
codestringMã immutable — agent / API dùng để gắn cuộc gọi[A-Za-z0-9_-], max 32 ký tự
labelstringNhãn hiển thịmax 100 ký tự
descriptionstring|nullMô tả chi tiếtmax 500 ký tự
categorystringNhóm kết quảcontact / no_contact / callback / remove
is_finalboolLà kết quả cuối hay còn thử lạitrue / false
trigger_callbackboolTự tạo callback khi agent chọn mã nàytrue / false (chỉ true khi category=callback)
trigger_dncboolTự thêm số vào DNC khi agent chọn mã nàytrue / false
default_retry_delay_minutesinteger|nullThời gian chờ trước khi quay lại lead nếu không phải kết quả final1-43200 (phút)
colorstringMã màu hex cho UI badge#RRGGBB
is_activeboolCòn hiển thị trong dropdown của agent hay khôngtrue / false
usage_count_30dintegerSố cuộc gọi đã dùng mã này trong 30 ngày gần nhất≥ 0
created_atdatetimeThời điểm tạo (ISO 8601 UTC)

Response 200:

json
{
  "data": [
    {
      "id": 5,
      "code": "SALE-OK",
      "label": "Sale Success",
      "description": "Customer agreed and committed",
      "category": "contact",
      "is_final": true,
      "trigger_callback": false,
      "trigger_dnc": false,
      "default_retry_delay_minutes": null,
      "color": "#10b981",
      "is_active": true,
      "usage_count_30d": 78,
      "created_at": "2026-06-01T06:00:00Z"
    }
  ]
}

GET /api/telesales/dispositions/{id} — chi tiết

Path parameter
FieldTypeMô tả
idintegerID disposition

Response 200: object disposition (shape giống một phần tử trong mảng list).

Response 404: { "message": "Disposition not found." }

POST /api/telesales/dispositions — tạo (admin)

Request body fields
FieldTypeRequiredMô tảGiá trị hợp lệ
codestringMã immutable — unique trong tài khoản[A-Za-z0-9_-], max 32 ký tự
labelstringNhãn hiển thịmax 100 ký tự
descriptionstringoptionalMô tả chi tiếtmax 500 ký tự
categorystringNhóm kết quảcontact / no_contact / callback / remove
is_finalbooloptionalLà kết quả cuối (không thử lại)true / false (default false)
trigger_callbackbooloptionalTự tạo callback — yêu cầu category=callbacktrue / false (default false)
trigger_dncbooloptionalTự thêm số vào DNCtrue / false (default false)
default_retry_delay_minutesintegeroptionalPhút chờ trước khi quay lại lead1-43200
colorstringoptionalMã màu UI#RRGGBB

Body:

json
{
  "code": "SALE-OK",
  "label": "Sale Success",
  "description": "Customer agreed and committed",
  "category": "contact",
  "is_final": true,
  "trigger_callback": false,
  "trigger_dnc": false,
  "default_retry_delay_minutes": null,
  "color": "#10b981"
}
Response fields
FieldTypeMô tảGiá trị hợp lệ
idintegerID disposition vừa tạo
codestringMã immutable
labelstringNhãn hiển thị
categorystringNhóm kết quảcontact / no_contact / callback / remove
is_finalboolLà kết quả cuối
trigger_callbackboolTự tạo callback
trigger_dncboolTự thêm DNC
colorstringMã màu#RRGGBB
is_activeboolHiển thị trong dropdown agent (default true)
created_atdatetimeThời điểm tạo (ISO 8601 UTC)

Response 201:

json
{
  "data": {
    "id": 5,
    "code": "SALE-OK",
    "label": "Sale Success",
    "category": "contact",
    "is_final": true,
    "trigger_callback": false,
    "trigger_dnc": false,
    "color": "#10b981",
    "is_active": true,
    "created_at": "2026-06-06T06:00:00Z"
  }
}

Response 422 — các trường hợp validate sai:

json
// Trùng code
{
  "message": "The given data was invalid.",
  "errors": { "code": ["The code has already been taken."] }
}

// Category không hợp lệ
{
  "message": "The given data was invalid.",
  "errors": { "category": ["The selected category is invalid."] }
}

// trigger_callback=true nhưng category không phải 'callback'
{
  "message": "The given data was invalid.",
  "errors": { "trigger_callback": ["trigger_callback only applies when category='callback'."] }
}

Response 403 — caller không phải admin:

json
{ "message": "You are not allowed to create dispositions. The admin role is required." }

PUT /api/telesales/dispositions/{id} — cập nhật (admin)

Path parameter
FieldTypeMô tả
idintegerID disposition
Request body fields

Giống POST nhưng mọi field optional — chỉ truyền field cần đổi. code không thể đổi sau khi tạo (immutable identifier); truyền code mới sẽ trả 422.

FieldTypeMô tảGiá trị hợp lệ
labelstringNhãn hiển thịmax 100 ký tự
descriptionstring|nullMô tả chi tiếtmax 500 ký tự
categorystringNhóm kết quảcontact / no_contact / callback / remove
is_finalboolLà kết quả cuối
trigger_callbackboolTự tạo callback (chỉ true khi category=callback)
trigger_dncboolTự thêm DNC
default_retry_delay_minutesinteger|nullPhút chờ retry1-43200
colorstringMã màu#RRGGBB
is_activeboolBật/tắt hiển thịtrue / false

Response 200: object đã cập nhật.

Response 422:

json
// Cố ý đổi code
{
  "message": "The given data was invalid.",
  "errors": { "code": ["Code is immutable. Create a new disposition if a different code is needed."] }
}

DELETE /api/telesales/dispositions/{id} — xoá (admin)

Path parameter
FieldTypeMô tả
idintegerID disposition cần xoá

Response 204 No Content (thành công — soft delete; set is_active=false).

Response 422 — disposition đang được dùng:

json
{
  "message": "Cannot delete a disposition with associated call history.",
  "errors": { "id": ["Disposition 5 has 78 calls in last 30 days. Hide instead of deleting (set is_active=false)."] }
}

Ẩn vs Xoá

Để giữ lịch sử báo cáo, delete mặc định là soft delete (set is_active=false). Disposition cũ vẫn xuất hiện trong báo cáo nhưng bị ẩn khỏi dropdown của agent. Hard delete chỉ được phép khi usage_count = 0.

6.2 Catalog khởi tạo gợi ý (8 mã chung)

Mỗi tài khoản mới sẽ được seed sẵn một bộ catalog tổng quát, không gắn với ngành cụ thể. Khách hàng có thể mở rộng hoặc thay thế qua API hoặc UI quản trị.

CodeLabelCategoryis_finaltrigger_callbacktrigger_dnc
SUCCESSSuccessful Contactcontactx
CALLBACK_REQCustomer Asked for Callbackcallbackx
REFUSEDCustomer Refusedcontactx
NO_ANSWERNo Answerno_contact
BUSYBusyno_contact
VOICEMAILVoicemail Leftno_contact
WRONG_NUMBERWrong Numberremovex
DNC_REQUESTDNC Requestremovexx

Đối với catalog chuyên ngành (chăm sóc khách hàng, lịch hẹn dịch vụ, khảo sát, lịch hẹn y tế, ...), tham khảo gói cấu hình riêng riêng cho khách hàng — sẽ seed sẵn bộ code mở rộng phù hợp workflow nghiệp vụ.

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