Skip to content

Campaigns

Quản lý chiến dịch AutoCall — CRUD + lifecycle controls (launch / pause / resume / archive / unarchive) + thống kê.

Tạo chiến dịch

http
POST /api/autocall/campaigns

Body:

json
{
  "name": "Chăm sóc khách hàng Q3-2026",
  "description": "Chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
  "script_id": 12,
  "caller_id_group_id": 3,
  "max_retries": 3,
  "retry_interval_minutes": 60,
  "retry_only_when": "no_answer",
  "max_concurrent": 10,
  "originate_timeout": 40,
  "write_to_main_cdr": false,
  "start_at": "2026-06-25T08:00:00Z",
  "end_at": "2026-06-30T17:30:00Z"
}

Fields:

FieldRequiredTypeMô tả
namestring, max:255Tên chiến dịch
descriptionKhôngstring, max:2000Mô tả
script_idintegerID script TTS (xem Scripts). PHẢI tồn tại status='active'. Truyền script đang draft/archived -> 422.
caller_id_group_idKhôngintegerID pool đầu số. Bỏ trống -> engine fallback default trunk của khách hàng. PHẢI tồn tại + status='active'.
max_retriesKhônginteger (0-10, default 3)Số lần thử lại tối đa nếu attempt fail
retry_interval_minutesKhônginteger (1-43200, default 60)Khoảng cách giữa các attempt (phút)
retry_only_whenKhôngenum (no_answer/busy/failed/any, default no_answer)Chỉ retry khi attempt result match
max_concurrentKhônginteger (1-200, default 10)Số cuộc đồng thời tối đa
originate_timeoutKhônginteger (10-180, default 40)Timeout chờ B-leg nhận máy (giây)
write_to_main_cdrKhôngboolean (default false)Có ghi vào CDR chung hệ thống không. False = chỉ ghi log nội bộ AutoCall.
start_atKhôngdatetime UTCThời điểm bắt đầu được dial. Bỏ trống = launch ngay
end_atKhôngdatetime UTCThời điểm dừng (phải > start_at)

Response 201 (response trả đầy đủ các field):

json
{
  "data": {
    "id": 19,
    "tenant_id": 1,
    "name": "Chăm sóc khách hàng Q3-2026",
    "description": "Chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
    "script_id": 12,
    "status": "draft",
    "gateway_name": null,
    "caller_id": null,
    "caller_id_group_id": 8,
    "max_retries": 3,
    "retry_interval_minutes": 60,
    "retry_only_when": "no_answer",
    "write_to_main_cdr": false,
    "max_concurrent": 10,
    "originate_timeout": 40,
    "start_at": "2026-06-25T08:00:00.000000Z",
    "end_at": "2026-06-30T17:30:00.000000Z",
    "total_leads": 0,
    "total_calls": 0,
    "total_connected": 0,
    "total_failed": 0,
    "created_by": 3,
    "created_at": "2026-06-25T09:53:47.000000Z",
    "updated_at": "2026-06-25T09:53:47.000000Z",
    "deleted_at": null
  }
}

Ghi chú

Response trả tất cả field cấu hình của campaign. Stats fields (total_*) = 0 lúc tạo (chưa có lead/call activity). Field gateway_name, caller_id (single override) null nếu dùng pool theo caller_id_group_id. Field deleted_at null nếu chưa archive (soft delete).

Response 422 (validation):

json
{
  "message": "The given data was invalid.",
  "errors": {
    "script_id": ["The selected script id is invalid."],
    "retry_only_when": ["The selected retry only when is invalid."]
  }
}

Lifecycle controls

EndpointActionTừ -> Sang
POST /api/autocall/campaigns/{id}/launchKích hoạtdraft/paused -> active
POST /api/autocall/campaigns/{id}/pauseTạm dừngactive -> paused
POST /api/autocall/campaigns/{id}/resumeTiếp tụcpaused -> active (alias của launch)
POST /api/autocall/campaigns/{id}/archiveLưu trữdraft/paused/done -> archived (yêu cầu KHÔNG đang active)
POST /api/autocall/campaigns/{id}/unarchiveKhôi phụcarchived -> paused
PUT /api/autocall/campaigns/{id}Cập nhật metadatagiữ status
DELETE /api/autocall/campaigns/{id}Xoá VĨNH VIỄNyêu cầu archived trước (hard delete cascade: campaign + leads + attempts + dtmf_events)

Quy tắc bắt buộc

  • Phải pause trước khi archive (campaign đang chạy không thể archive).
  • Phải archive trước khi delete (xoá vĩnh viễn -> mất data báo cáo).

Response 200 (áp dụng cho mọi action launch/pause/resume/archive/unarchive):

json
{
  "data": {
    "id": 21,
    "tenant_id": 1,
    "name": "Chăm sóc Q3-2026 2",
    "description": "Chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
    "script_id": 12,
    "status": "active",
    "gateway_name": null,
    "caller_id": null,
    "caller_id_group_id": 8,
    "max_retries": 3,
    "retry_interval_minutes": 60,
    "retry_only_when": "no_answer",
    "write_to_main_cdr": false,
    "max_concurrent": 10,
    "originate_timeout": 40,
    "start_at": "2026-06-25T08:00:00.000000Z",
    "end_at": "2026-06-30T17:30:00.000000Z",
    "total_leads": 0,
    "total_calls": 0,
    "total_connected": 0,
    "total_failed": 0,
    "created_by": 3,
    "created_at": "2026-06-25T14:26:16.000000Z",
    "updated_at": "2026-06-25T14:27:41.000000Z",
    "deleted_at": null
  }
}

Field bổ sung (không có ở body POST):

FieldTypeMô tả
gateway_namestring | nullOverride gateway dial cụ thể (mặc định null = dùng default routing)
caller_idstring | nullOverride caller ID đơn (mặc định null = dùng pool theo caller_id_group_id)
total_leadsintegerTổng lead đã import vào campaign
total_callsintegerTổng cuộc gọi đã dial
total_connectedintegerTổng cuộc connect thành công
total_failedintegerTổng cuộc fail
deleted_attimestamp | nullSoft delete timestamp (null nếu chưa archive)

Timestamps format

ISO 8601 với microseconds .000000Z. Khách hàng parse phải hỗ trợ format này (vd new Date("2026-06-25T14:26:16.000000Z") trong JS hoạt động đúng).

Response 422 (state machine fail):

json
{ "message": "Chỉ campaign draft/paused mới launch được" }
{ "message": "Chỉ campaign active mới pause được" }
{ "message": "Phải tạm dừng chiến dịch trước khi lưu trữ" }
{ "message": "Chỉ chiến dịch đã lưu trữ mới có thể khôi phục" }
{ "message": "Phải lưu trữ chiến dịch trước khi xoá vĩnh viễn (Lưu trữ -> Xoá)" }

Response 200 (PUT /api/autocall/campaigns/{id} — cập nhật metadata): trả campaign object đã update, shape giống Response 201.

Response 200 (DELETE /api/autocall/campaigns/{id}):

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

List / show details / stats

GET /api/autocall/campaigns?status=active

Query params:

FieldTypeRequiredMô tảGiá trị hợp lệ
statusstringKhôngLọc theo trạng thái chiến dịchdraft / active / paused / done / archived
archivedintegerKhôngLấy danh sách chiến dịch đã lưu trữ. Mặc định ẨN archived1 = chỉ archived; bỏ trống = không archived
qstringKhôngTìm kiếm gần đúng theo tên chiến dịch (LIKE %keyword%)Chuỗi text bất kỳ

Response 200:

json
{
  "data": [
    {
      "id": 36,
      "name": "Chăm sóc khách hàng Q3-2026",
      "status": "active",
      "script": { "id": 12, "name": "Chăm sóc chuẩn", "voice_id": "evln-vi-nature" },
      "total_leads": 487,
      "total_calls": 312,
      "total_connected": 198,
      "total_failed": 89,
      "created_at": "2026-06-24T07:30:00Z"
    }
  ]
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data[].idintegerID chiến dịchSố nguyên dương
data[].namestringTên chiến dịchTối đa 255 ký tự
data[].statusstringTrạng thái lifecycle hiện tạidraft / active / paused / done / archived
data[].scriptobjectScript TTS đính kèm (eager-load)Object con với id, name, voice_id
data[].script.idintegerID scriptSố nguyên dương
data[].script.namestringTên script
data[].script.voice_idstringID giọng đọc TTS được script dùng
data[].total_leadsintegerTổng lead đã import vào chiến dịch>= 0
data[].total_callsintegerTổng cuộc gọi đã dial>= 0
data[].total_connectedintegerTổng cuộc gọi connect thành công (B-leg answer)>= 0
data[].total_failedintegerTổng cuộc gọi thất bại>= 0
data[].created_atdatetime ISO 8601Thời điểm tạo chiến dịch (UTC)

GET /api/autocall/campaigns/{id}

Trả về chi tiết đầy đủ kèm script.dtmfActionsagents.

Path params:

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

Response 200:

json
{
  "data": {
    "id": 36,
    "name": "Chăm sóc khách hàng Q3-2026",
    "description": "Chăm sóc khách hàng kích hoạt lại sau 30-60 ngày",
    "status": "active",
    "script_id": 12,
    "caller_id_group_id": 3,
    "max_retries": 3,
    "retry_interval_minutes": 60,
    "retry_only_when": "no_answer",
    "max_concurrent": 10,
    "originate_timeout": 40,
    "write_to_main_cdr": false,
    "start_at": "2026-06-25T08:00:00Z",
    "end_at": "2026-06-30T17:30:00Z",
    "total_leads": 487,
    "total_calls": 312,
    "total_connected": 198,
    "total_failed": 89,
    "tenant_id": 1,
    "created_by": 7,
    "created_at": "2026-06-24T07:30:00Z",
    "updated_at": "2026-06-25T02:34:00Z",
    "script": {
      "id": 12,
      "name": "Chăm sóc chuẩn",
      "voice_id": "evln-vi-nature",
      "dtmf_actions": [
        { "id": 45, "digit": "1", "label": "Xác nhận", "action_type": "playback_then_hangup", "target_audio_url": "/media/thanks.wav" },
        { "id": 46, "digit": "2", "label": "Chuyển NV", "action_type": "queue", "target_queue_name": "support_queue" }
      ]
    },
    "agents": []
  }
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data.idintegerID chiến dịchSố nguyên dương
data.namestringTên chiến dịch
data.descriptionstring | nullMô tả
data.statusstringTrạng thái lifecycledraft / active / paused / done / archived
data.script_idintegerID script TTS đang dùng
data.caller_id_group_idinteger | nullID pool đầu số rotation
data.max_retriesintegerSố lần thử lại tối đa nếu attempt fail0-10
data.retry_interval_minutesintegerKhoảng cách giữa các attempt (phút)1-43200
data.retry_only_whenstringĐiều kiện chỉ retry khi result matchno_answer / busy / failed / any
data.max_concurrentintegerSố cuộc đồng thời tối đa1-200
data.originate_timeoutintegerTimeout chờ B-leg nhận máy (giây)10-180
data.write_to_main_cdrbooleanCó ghi CDR chung hệ thống khôngtrue / false
data.start_atdatetime ISO 8601 | nullThời điểm bắt đầu được dial (UTC)
data.end_atdatetime ISO 8601 | nullThời điểm dừng dial (UTC)
data.total_leadsintegerTổng lead đã import>= 0
data.total_callsintegerTổng cuộc đã dial>= 0
data.total_connectedintegerTổng cuộc connect thành công>= 0
data.total_failedintegerTổng cuộc fail>= 0
data.tenant_idintegerID khách hàng sở hữu
data.created_byintegerUser ID người tạo
data.created_atdatetime ISO 8601Thời điểm tạo (UTC)
data.updated_atdatetime ISO 8601Thời điểm cập nhật gần nhất (UTC)
data.scriptobjectScript TTS đính kèm + danh sách phím DTMF
data.script.dtmf_actions[]arrayCấu hình các phím bấm DTMF của script (xem Scripts)
data.agents[]arrayDanh sách agent gán riêng vào chiến dịch (rỗng nếu không gán)

Response 404 (campaign không tồn tại hoặc thuộc khách hàng khác):

json
{ "message": "Campaign không tồn tại" }

GET /api/autocall/campaigns/{id}/stats

Path params:

FieldTypeRequiredMô tảGiá trị hợp lệ
idintegerID chiến dịchSố nguyên dương

Response 200:

json
{
  "data": {
    "total_leads": 487,
    "total_calls": 312,
    "total_connected": 198,
    "total_failed": 89,
    "total_no_answer": 65,
    "total_busy": 18,
    "connection_rate": 63.5
  }
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data.total_leadsintegerTổng lead đã import vào chiến dịch>= 0
data.total_callsintegerTổng cuộc đã dial>= 0
data.total_connectedintegerTổng cuộc connect thành công (B-leg answer)>= 0
data.total_failedintegerTổng cuộc fail (đếm gộp failed + no_answer + busy)>= 0
data.total_no_answerintegerTổng cuộc không bắt máy>= 0
data.total_busyintegerTổng cuộc máy bận>= 0
data.connection_ratenumberTỷ lệ kết nối thành công (total_connected / total_calls * 100)0-100, làm tròn 1 chữ số thập phân

GET /api/autocall/campaigns/{id}/attempts?per_page=50

Danh sách cuộc gọi của campaign (phân trang chuẩn page/per_page, default 50, max 200).

Path params:

FieldTypeRequiredMô tảGiá trị hợp lệ
idintegerID chiến dịchSố nguyên dương

Query params:

FieldTypeRequiredMô tảGiá trị hợp lệ
pageintegerKhôngTrang phân trang (1-indexed)>= 1, default 1
per_pageintegerKhôngSố attempt mỗi trang1-200, default 50

Response 200:

json
{
  "data": [
    {
      "id": 8721,
      "uuid": "093e1024-1234-5678-9abc-def012345678",
      "campaign_id": 36,
      "lead_id": 78912,
      "lead_phone_number": "0912345678",
      "attempt_number": 1,
      "started_at": "2026-06-24T08:32:00Z",
      "answered_at": "2026-06-24T08:32:05Z",
      "ended_at": "2026-06-24T08:32:47Z",
      "duration_sec": 42,
      "result": "connected",
      "hangup_cause": "NORMAL_CLEARING",
      "dtmf_pressed": "2",
      "final_action": "queue:support_queue"
    }
  ],
  "current_page": 1,
  "last_page": 7,
  "per_page": 50,
  "total": 312
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data[].idintegerID attemptSố nguyên dương
data[].uuidstringUUID cuộc gọi (dùng để hangup hoặc tra cứu)UUID v4
data[].campaign_idintegerID chiến dịch
data[].lead_idintegerID lead
data[].lead_phone_numberstringSố điện thoại lead (format E.164 84xxx)
data[].attempt_numberintegerLần thử thứ mấy1-N
data[].started_atdatetime ISO 8601Thời điểm bắt đầu originate (UTC)
data[].answered_atdatetime ISO 8601 | nullThời điểm B-leg answer (UTC)null nếu không answer
data[].ended_atdatetime ISO 8601Thời điểm cuộc gọi kết thúc (UTC)
data[].duration_secintegerTổng thời lượng cuộc gọi (giây)>= 0
data[].resultstringKết quả cuộc gọiconnected / no_answer / busy / failed / abandoned
data[].hangup_causestringMã hangup từ Zorio PBXNORMAL_CLEARING / NO_ANSWER / USER_BUSY / ...
data[].dtmf_pressedstring | nullPhím DTMF khách hàng bấm (nếu có)0-9, *, #, null
data[].final_actionstring | nullAction cuối cùng đã thực thiqueue:<name> / playback:<url> / hangup / switch:<id>
current_pageintegerTrang hiện tại>= 1
last_pageintegerSố trang cuối>= 1
per_pageintegerSố item mỗi trang1-200
totalintegerTổng số attempt khớp filter>= 0

Caller-ID Groups (read-only picker)

GET /api/autocall/caller-id-pools

Lấy danh sách pool đầu số để dùng làm caller_id_group_id khi tạo campaign.

Response 200:

json
{
  "data": [
    {
      "id": 3,
      "name": "Pool VNPT 028",
      "label": "Pool VNPT 028",
      "description": "5 số 028xxxx, rotation Round Robin"
    }
  ]
}

Field response:

FieldTypeMô tảGiá trị hợp lệ
data[].idintegerID pool — dùng làm caller_id_group_id khi POST /api/autocall/campaignsSố nguyên dương
data[].namestringTên pool (định danh kỹ thuật)Tối đa 255 ký tự
data[].labelstringTên hiển thị friendly cho UITối đa 255 ký tự
data[].descriptionstring | nullMô tả ngắn về pool (cách rotation, danh sách số...)

Hangup cuộc gọi đang chạy

Hangup cuộc gọi đang chạy cho campaign AutoCall — AutoCall và Telesales chia sẻ endpoint hangup chung.

http
POST /api/telesales/calls/{uuid}/hangup

Path params:

FieldTypeRequiredMô tảGiá trị hợp lệ
uuidstringUUID cuộc gọi (lấy từ webhook call.answered event hoặc field uuid trong attempt)UUID v4 thuộc tài khoản hiện tại

Request mẫu:

bash
curl -X POST https://app.zorio.vn/api/telesales/calls/abc-def-123/hangup \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json"

Response 200:

json
{ "success": true }

Field response:

FieldTypeMô tảGiá trị hợp lệ
successbooleanĐã gửi lệnh hangup tới Zorio PBX thành côngtrue

Error 404 (uuid không tồn tại hoặc không thuộc tài khoản):

json
{ "error": "Cuộc gọi không tồn tại" }

Permission

Cần manage_telesales_campaigns hoặc role admin. Cuộc gọi sẽ kết thúc với hangup cause NORMAL_CLEARING.

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