Chiến dịchQuản lý chiến dịch

Quản lý chiến dịch

Tạ Quốc Thắng·4/23/2026

API Campaign

Authentication chung: Header X-Api-Key (scope: campaign) bắt buộc với tất cả endpoint. Kong dual-auth: ưu tiên X-Tenant-ID header, fallback query DB theo Authorization key. Không dùng JWT (x-access-token).

Base URL: Dev: https://xapi-dev.alohub.vn  |  Prod: https://xapi.alohub.vn


GET /v1/campaigns

Lấy danh sách campaigns của tenant. Hỗ trợ filter theo status, loại campaign và phân trang.

Authentication: Header X-Api-Key (scope: campaign). Kong dual-auth: ưu tiên X-Tenant-ID header, fallback query DB theo Authorization key.

Base URL: Dev: https://xapi-dev.alohub.vn  |  Prod: https://xapi.alohub.vn

Tham số

Tham số

Vị trí

Bắt buộc

Kiểu

Mô tả

Ví dụ

userName

query

string

Username đang đăng nhập

AloHub

status

query

Không

number

Filter: 1=inprocess, 2=running, 3=paused, 4=stopped

2

type

query

Không

string

Filter loại campaign (phân biệt hoa thường)

CAMPAIGN_CALL_AUTO

page

query

Không

number

Trang (default: 0)

0

limit

query

Không

number

Số record/trang (default: 20)

20

Code mẫu

curl -X GET "https://xapi.alohub.vn/v1/campaigns?userName=AloHub" \
  -H "X-Api-Key: sk_live_xxx"

# Filter đang chạy
curl -X GET "https://xapi.alohub.vn/v1/campaigns?userName=AloHub&status=2&page=0&limit=20" \
  -H "X-Api-Key: sk_live_xxx"
const axios = require('axios');
const response = await axios.get(
  '{{host}}/api/v1/campaigns?userName=AloHub&status=2&type=CAMPAIGN_CALL_AUTO&page=0&limit=20',
  { headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);
import requests

params = {"userName": "AloHub", "status": "2", "type": "CAMPAIGN_CALL_AUTO", "page": "0", "limit": "20"}
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.get(
    '{{host}}/api/v1/campaigns',
    headers=headers, params=params
)
print(response.json())

Response 200

{
  "success": "1",
  "error_code": "SUCCESS",
  "error_message": "SUCCESS",
  "totalRecord": 25,
  "data": [
    {
      "campaignId": 1042,
      "campaignName": "Chiến dịch tháng 4",
      "campaignType": "CAMPAIGN_CALL_AUTO",
      "status": 2,
      "statusLabel": "running",
      "totalContacts": 500,
      "processedContacts": 123,
      "startTime": "2026-04-01T08:00:00",
      "endTime": "2026-04-30T20:00:00",
      "createBy": "admin",
      "createTime": "2026-03-28T10:00:00",
      "updateTime": "2026-04-01T08:00:05"
    }
  ]
}

Response Fields

Field

Kiểu

Mô tả

success

string

"1" = thành công

totalRecord

number

Tổng số campaign

data[].campaignId

number

ID campaign — dùng làm {id}

data[].campaignName

string

Tên campaign

data[].campaignType

string

Loại campaign

data[].status

number

1=inprocess, 2=running, 3=paused, 4=stopped

data[].statusLabel

string

inprocess / running / paused / stopped

data[].totalContacts

number

Tổng số contact

data[].processedContacts

number

Số contact đã xử lý

data[].startTime

string

Thời điểm bắt đầu (ISO 8601)

data[].endTime

string

Thời điểm kết thúc (ISO 8601)

data[].createBy

string

Username người tạo

data[].createTime

string

Thời điểm tạo

data[].updateTime

string

Thời điểm cập nhật gần nhất

Error Codes

HTTP

error_code

Mô tả

FE xử lý

401

UNAUTHORIZED

Thiếu hoặc sai API key

Redirect nhập lại key

403

INSUFFICIENT_SCOPE

Key không có scope campaign

Hiện thông báo

404

NOT_FOUND

Không tìm thấy

Hiện thông báo

429

RATE_LIMIT_EXCEEDED

Vượt giới hạn request

Retry sau Retry-After giây

500

FAIL

Lỗi hệ thống

Toast lỗi chung

Rate Limit Headers

Header

Mô tả

X-RateLimit-Limit-Tenant

Giới hạn tenant/10s

X-RateLimit-Remaining-Tenant

Còn lại tenant/10s

X-RateLimit-Limit-Route

Giới hạn route/10s

X-RateLimit-Remaining-Route

Còn lại route/10s

Retry-After

Giây cần chờ khi bị 429


GET /v1/campaigns/{id}

Lấy chi tiết đầy đủ 1 campaign bao gồm cấu hình concurrent call, max recall, webhook URL.

Authentication: Header X-Api-Key (scope: campaign). Kong dual-auth: ưu tiên X-Tenant-ID header, fallback query DB theo Authorization key.

Base URL: Dev: https://xapi-dev.alohub.vn  |  Prod: https://xapi.alohub.vn

Tham số

Tham số

Vị trí

Bắt buộc

Kiểu

Mô tả

Ví dụ

{id}

path

number

campaignId (lấy từ GET /v1/campaigns)

1042

userName

query

string

Username đang đăng nhập

AloHub

Code mẫu

curl -X GET "https://xapi.alohub.vn/v1/campaigns/1042?userName=AloHub" \
  -H "X-Api-Key: sk_live_xxx"
const axios = require('axios');
const response = await axios.get(
  '{{host}}/api/v1/campaigns/{{id}}?userName=AloHub',
  { headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);
import requests

params = {"userName": "AloHub"}
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.get(
    '{{host}}/api/v1/campaigns/{{id}}',
    headers=headers, params=params
)
print(response.json())

Response 200

{
  "campaignId": 1042,
  "campaignName": "Chiến dịch tháng 4",
  "campaignType": "CAMPAIGN_CALL_AUTO",
  "status": 2,
  "statusLabel": "running",
  "totalContacts": 500,
  "processedContacts": 123,
  "startTime": "2026-04-01T08:00:00",
  "endTime": "2026-04-30T20:00:00",
  "concurrentCall": 10,
  "maxRecall": 3,
  "callNumber": "0287661234",
  "webhookUrl": "https://example.com/webhook",
  "createBy": "admin",
  "createTime": "2026-03-28T10:00:00",
  "updateTime": "2026-04-01T08:00:05",
  "tenantId": 20263010
}

Response Fields

Field

Kiểu

Mô tả

campaignId

number

ID campaign

campaignName

string

Tên campaign

campaignType

string

Loại campaign

status

number

1=inprocess, 2=running, 3=paused, 4=stopped

statusLabel

string

Nhãn text

totalContacts

number

Tổng số contact

processedContacts

number

Số contact đã xử lý

startTime

string

Thời điểm bắt đầu

endTime

string

Thời điểm kết thúc

concurrentCall

number

Số cuộc gọi đồng thời

maxRecall

number

Số lần gọi lại tối đa

callNumber

string

Số điện thoại gọi ra

webhookUrl

string

URL webhook

createBy

string

Username tạo

createTime

string

Thời điểm tạo

updateTime

string

Thời điểm cập nhật

tenantId

number

ID tenant

Error Codes

HTTP

error_code

Mô tả

FE xử lý

401

UNAUTHORIZED

Thiếu hoặc sai API key

Redirect nhập lại key

403

INSUFFICIENT_SCOPE

Key không có scope campaign

Hiện thông báo

404

NOT_FOUND

Không tìm thấy

Hiện thông báo

429

RATE_LIMIT_EXCEEDED

Vượt giới hạn request

Retry sau Retry-After giây

500

FAIL

Lỗi hệ thống

Toast lỗi chung

Rate Limit Headers

Header

Mô tả

X-RateLimit-Limit-Tenant

Giới hạn tenant/10s

X-RateLimit-Remaining-Tenant

Còn lại tenant/10s

X-RateLimit-Limit-Route

Giới hạn route/10s

X-RateLimit-Remaining-Route

Còn lại route/10s

Retry-After

Giây cần chờ khi bị 429


PUT /v1/campaigns/{id}

Điều khiển trạng thái campaign: start / pause / stop. Chuyển trạng thái theo state machine: start(1→2, 3→2), pause(2→3), stop(2→4, 3→4).

Authentication: Header X-Api-Key (scope: campaign). Kong dual-auth: ưu tiên X-Tenant-ID header, fallback query DB theo Authorization key.

Base URL: Dev: https://xapi-dev.alohub.vn  |  Prod: https://xapi.alohub.vn

Tham số

Tham số

Vị trí

Bắt buộc

Kiểu

Mô tả

Ví dụ

{id}

path

number

campaignId

1042

userName

query

string

Username đang đăng nhập

AloHub

Request Body

{
  "action": "start"
}

Field

Bắt buộc

Mô tả

action

"start"

"stop"

"pause" — giá trị khác → INVALID_INPUT

Code mẫu

curl -X PUT "https://xapi.alohub.vn/v1/campaigns/1042?userName=AloHub" \
  -H "X-Api-Key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"action":"start"}'

# Pause
curl -X PUT "https://xapi.alohub.vn/v1/campaigns/1042?userName=AloHub" \
  -H "X-Api-Key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"action":"pause"}'

# Stop
curl -X PUT "https://xapi.alohub.vn/v1/campaigns/1042?userName=AloHub" \
  -H "X-Api-Key: sk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"action":"stop"}'
const axios = require('axios');
const response = await axios.put(
  '{{host}}/api/v1/campaigns/{{id}}?userName=AloHub',
  {
  "action": "start"
},
  { headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);
import requests

params = {"userName": "AloHub"}
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
payload = {
    "action": "start"
}
response = requests.put(
    '{{host}}/api/v1/campaigns/{{id}}',
    json=payload,
    headers=headers, params=params
)
print(response.json())

Response 200

{
  "success": "1",
  "error_code": "SUCCESS",
  "error_message": "Campaign started successfully. New status: running"
}

Response Fields

Field

Kiểu

Mô tả

success

string

"1" = thành công

error_code

string

SUCCESS khi điều khiển thành công

error_message

string

Mô tả kết quả và trạng thái mới

Error Codes

HTTP

error_code

Mô tả

FE xử lý

401

UNAUTHORIZED

Thiếu hoặc sai API key

Redirect nhập lại key

403

INSUFFICIENT_SCOPE

Key không có scope campaign

Hiện thông báo

400

INVALID_INPUT

Sai input

Hiện lỗi cụ thể

404

NOT_FOUND

Không tìm thấy

Hiện thông báo

400

INVALID_STATE

Sai trạng thái (xem state machine)

Disable nút theo state

429

RATE_LIMIT_EXCEEDED

Vượt giới hạn request

Retry sau Retry-After giây

500

FAIL

Lỗi hệ thống

Toast lỗi chung

Rate Limit Headers

Header

Mô tả

X-RateLimit-Limit-Tenant

Giới hạn tenant/10s

X-RateLimit-Remaining-Tenant

Còn lại tenant/10s

X-RateLimit-Limit-Route

Giới hạn route/10s

X-RateLimit-Remaining-Route

Còn lại route/10s

Retry-After

Giây cần chờ khi bị 429


GET /v1/campaigns/{campaignId}/results

Lấy thống kê kết quả thực thi của một campaign — số khách hàng đã xử lý, số call thành công/thất bại, chi phí thực tế, v.v.

Lưu ý response: Endpoint này trả object dữ liệu trực tiếp khi thành công (không có envelope {{success, data, ...}}). Lỗi mới có envelope. FE parse theo HTTP status code: 200 → object trực tiếp, 4xx/5xx → parse envelope.

Path Parameters

Tham số

Bắt buộc

Kiểu

Mô tả

Ví dụ

campaignId

number (integer)

ID campaign cần lấy kết quả

502

Request Body

Không có. Method GET không dùng body.

Code mẫu

curl -X GET "https://xapi.alohub.vn/v1/campaigns/502/results" \
  -H "X-Api-Key: sk_live_xxx" \
  -H "Accept: application/json" 
const axios = require('axios');
const response = await axios.get(
  '{{host}}/api/v1/campaigns/{{campaignId}}/results',
  { headers: { 'X-Api-Key': '{{api-key}}' } }
);
// ⚠️ response.data là object trực tiếp, không có .data.data
console.log(response.data);
import requests
response = requests.get(
    '{{host}}/api/v1/campaigns/{{campaignId}}/results',
    headers={"X-Api-Key": "{{api-key}}"}
)
# ⚠️ Khi 200: response.json() là object trực tiếp
# Khi 4xx: response.json() có envelope {success, error_code, ...}
print(response.json())

Response 200 — Object trực tiếp (không có envelope)

{
  "campaignId": 502,
  "campaignCode": null,
  "campaignName": "loilv-test",
  "status": 4,
  "campaignType": "CAMPAIGN_SMS_NORMAL",
  "totalCustomer": 4,
  "processedCustomer": 0,
  "successCall": 0,
  "failCall": 0,
  "actualCost": 0,
  "refundedAmount": 0,
  "znsSuccessCount": 0
}

Response Fields

Field

Kiểu

Mô tả

campaignId

number

ID campaign

campaignCode

string | null

Mã code tùy chỉnh (có thể null)

campaignName

string

Tên campaign

status

number

Trạng thái: 0=Draft, 1=Pending, 2=Running, 3=Paused, 4=Completed, 5=Cancelled

campaignType

string

Loại campaign: CAMPAIGN_SMS_NORMAL, CAMPAIGN_VOICE, CAMPAIGN_ZNS, ...

totalCustomer

number

Tổng số khách hàng trong campaign

processedCustomer

number

Số khách hàng đã được xử lý

successCall

number

Số cuộc gọi thành công (chỉ campaign voice)

failCall

number

Số cuộc gọi thất bại (chỉ campaign voice)

actualCost

number

Chi phí thực tế (VND)

refundedAmount

number

Số tiền đã hoàn trả (VND)

znsSuccessCount

number

Số ZNS gửi thành công (chỉ campaign ZNS)

Response 404 — Khi lỗi (có envelope)

{
  "success": "0",
  "error_code": "NOT_FOUND",
  "error_message": "Campaign not found or access denied",
  "totalRecord": 0,
  "data": []
}

Error Codes

HTTP

error_code

Mô tả

FE xử lý

401

UNAUTHORIZED

Thiếu hoặc sai X-Api-Key

Redirect nhập lại key

403

INSUFFICIENT_SCOPE

Key không có scope campaign

Thông báo liên hệ admin

404

NOT_FOUND

Campaign không tồn tại hoặc thuộc tenant khác (gộp 2 case để tránh leak)

Thông báo "Không tìm thấy campaign"

400

INVALID_INPUT

campaignId không phải số

Validate trước khi gọi API

429

RATE_LIMIT_EXCEEDED

Vượt giới hạn request

Retry sau Retry-After giây

500

FAIL

Lỗi hệ thống

Toast lỗi chung


GET /v1/campaigns/{campaignId}/results/export

Export toàn bộ kết quả của một campaign ra file CSV để download. Dùng cho báo cáo, phân tích offline hoặc import vào Excel / BI tools.

Response Type: Binary CSV file (text/csv; charset=UTF-8), không phải JSON. Khi lỗi thì Content-Type là application/json với envelope chuẩn.

Timeout: Route này có read_timeout / write_timeout = 120s (gấp đôi default) vì export có thể chậm với campaign lớn.

Path Parameters

Tham số

Bắt buộc

Kiểu

Mô tả

Ví dụ

campaignId

number (long)

ID campaign cần export

502

Request Body

Không có. Chỉ cần campaignId trên URL.

Code mẫu

# Export và lưu file
curl -X GET "https://xapi.alohub.vn/v1/campaigns/502/results/export" \
  -H "X-Api-Key: sk_live_xxx" \
  -o "CampaignResults_502.csv"

# Xem headers (lấy filename từ Content-Disposition)
curl -I "https://xapi.alohub.vn/v1/campaigns/502/results/export" \
  -H "X-Api-Key: sk_live_xxx" 
const axios = require('axios');
const response = await axios.get(
  '{{host}}/api/v1/campaigns/{{campaignId}}/results/export',
  {
    headers: { 'X-Api-Key': '{{api-key}}' },
    responseType: 'blob'     // ⭐ bắt buộc để nhận binary
  }
);
// Lấy filename từ Content-Disposition
const cd = response.headers['content-disposition'] || '';
const filename = cd.match(/filename="?([^"]+)"?/)?.[1]
  ?? `CampaignResults_{{campaignId}}.csv`;
// Trigger download
const url = window.URL.createObjectURL(response.data);
const a = document.createElement('a');
a.href = url; a.download = filename; a.click();
window.URL.revokeObjectURL(url);
import requests
response = requests.get(
    '{{host}}/api/v1/campaigns/{{campaignId}}/results/export',
    headers={"X-Api-Key": "{{api-key}}"},
    stream=True
)
if response.headers.get('Content-Type', '').startswith('text/csv'):
    with open('CampaignResults.csv', 'wb') as f:
        for chunk in response.iter_content(chunk_size=8192):
            f.write(chunk)
else:
    # Lỗi trả JSON
    print(response.json())

CSV Columns

Cột

Kiểu

Mô tả

Campaign ID

number

ID campaign

Campaign Code

string

Mã code tùy chỉnh (rỗng nếu không có)

Campaign Name

string

Tên campaign

Status

number

Trạng thái (0–5, xem bảng ở API trên)

Campaign Type

string

Loại campaign

Total Customer

number

Tổng số khách hàng

Processed Customer

number

Số khách hàng đã xử lý

Success Call

number

Số call thành công (chỉ campaign voice)

Fail Call

number

Số call thất bại (chỉ campaign voice)

Actual Cost

number

Chi phí thực tế (VND)

Refunded Amount

number

Số tiền hoàn trả (VND)

ZNS Success Count

number

Số ZNS thành công (chỉ campaign ZNS)

CORS & Content-Disposition: FE đọc được header Content-Disposition để lấy filename vì Kong đã expose trong CORS: Access-Control-Expose-Headers: Content-Disposition, Content-Type. Nếu vẫn undefined → fallback về CampaignResults_{{campaignId}}.csv.

Error Codes

HTTP

error_code

Mô tả

FE xử lý

401

UNAUTHORIZED

Thiếu hoặc sai X-Api-Key

Redirect nhập lại key

403

INSUFFICIENT_SCOPE

Key không có scope campaign

Thông báo liên hệ admin

404

NOT_FOUND

Campaign không tồn tại hoặc không có quyền

Thông báo lỗi

429

RATE_LIMIT_EXCEEDED

Vượt giới hạn request

Retry sau Retry-After giây

500

FAIL

Lỗi hệ thống

Toast lỗi chung

Rate Limit Headers

Header

Mô tả

X-RateLimit-Limit-Tenant

Giới hạn tenant/10s

X-RateLimit-Remaining-Tenant

Còn lại tenant/10s

X-RateLimit-Limit-Route

Giới hạn route/10s

X-RateLimit-Remaining-Route

Còn lại route/10s

Retry-After

Giây cần chờ khi bị 429


Related Articles

Was this article helpful?
Updated: 4/23/2026
để chuyển bài