Authentication chung: Header
X-Api-Key(scope:campaign) bắt buộc với tất cả endpoint. Kong dual-auth: ưu tiênX-Tenant-IDheader, 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
/v1/campaignsLấ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ênX-Tenant-IDheader, fallback query DB theo Authorization key.
Base URL: Dev:
https://xapi-dev.alohub.vn| Prod:https://xapi.alohub.vn
Tham số | Vị trí | Bắt buộc | Kiểu | Mô tả | Ví dụ |
|---|---|---|---|---|---|
| query | Có |
| Username đang đăng nhập | AloHub |
| query | Không |
| Filter: 1=inprocess, 2=running, 3=paused, 4=stopped | 2 |
| query | Không |
| Filter loại campaign (phân biệt hoa thường) | CAMPAIGN_CALL_AUTO |
| query | Không |
| Trang (default: 0) | 0 |
| query | Không |
| Số record/trang (default: 20) | 20 |
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())
{
"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"
}
]
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| Tổng số campaign |
|
| ID campaign — dùng làm {id} |
|
| Tên campaign |
|
| Loại campaign |
|
| 1=inprocess, 2=running, 3=paused, 4=stopped |
|
| inprocess / running / paused / stopped |
|
| Tổng số contact |
|
| Số contact đã xử lý |
|
| Thời điểm bắt đầu (ISO 8601) |
|
| Thời điểm kết thúc (ISO 8601) |
|
| Username người tạo |
|
| Thời điểm tạo |
|
| Thời điểm cập nhật gần nhất |
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 | 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 |
Header | Mô tả |
|---|---|
| Giới hạn tenant/10s |
| Còn lại tenant/10s |
| Giới hạn route/10s |
| Còn lại route/10s |
| Giây cần chờ khi bị 429 |
/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ênX-Tenant-IDheader, fallback query DB theo Authorization key.
Base URL: Dev:
https://xapi-dev.alohub.vn| Prod:https://xapi.alohub.vn
Tham số | Vị trí | Bắt buộc | Kiểu | Mô tả | Ví dụ |
|---|---|---|---|---|---|
| path | Có |
| campaignId (lấy từ GET /v1/campaigns) | 1042 |
| query | Có |
| Username đang đăng nhập | AloHub |
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())
{
"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
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| ID campaign |
|
| Tên campaign |
|
| Loại campaign |
|
| 1=inprocess, 2=running, 3=paused, 4=stopped |
|
| Nhãn text |
|
| Tổng số contact |
|
| Số contact đã xử lý |
|
| Thời điểm bắt đầu |
|
| Thời điểm kết thúc |
|
| Số cuộc gọi đồng thời |
|
| Số lần gọi lại tối đa |
|
| Số điện thoại gọi ra |
|
| URL webhook |
|
| Username tạo |
|
| Thời điểm tạo |
|
| Thời điểm cập nhật |
|
| ID tenant |
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 | 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 |
Header | Mô tả |
|---|---|
| Giới hạn tenant/10s |
| Còn lại tenant/10s |
| Giới hạn route/10s |
| Còn lại route/10s |
| Giây cần chờ khi bị 429 |
/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ênX-Tenant-IDheader, fallback query DB theo Authorization key.
Base URL: Dev:
https://xapi-dev.alohub.vn| Prod:https://xapi.alohub.vn
Tham số | Vị trí | Bắt buộc | Kiểu | Mô tả | Ví dụ |
|---|---|---|---|---|---|
| path | Có |
| campaignId | 1042 |
| query | Có |
| Username đang đăng nhập | AloHub |
{
"action": "start"
}
Field | Bắt buộc | Mô tả | ||
|---|---|---|---|---|
| Có | "start" | "stop" | "pause" — giá trị khác → INVALID_INPUT |
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())
{
"success": "1",
"error_code": "SUCCESS",
"error_message": "Campaign started successfully. New status: running"
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| SUCCESS khi điều khiển thành công |
|
| Mô tả kết quả và trạng thái mới |
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 | 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 |
Header | Mô tả |
|---|---|
| Giới hạn tenant/10s |
| Còn lại tenant/10s |
| Giới hạn route/10s |
| Còn lại route/10s |
| Giây cần chờ khi bị 429 |
/v1/campaigns/{campaignId}/resultsLấ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.
Tham số | Bắt buộc | Kiểu | Mô tả | Ví dụ |
|---|---|---|---|---|
| Có | number (integer) | ID campaign cần lấy kết quả |
|
Không có. Method GET không dùng body.
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())
{
"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
}
Field | Kiểu | Mô tả |
|---|---|---|
| number | ID campaign |
| string | null | Mã code tùy chỉnh (có thể null) |
| string | Tên campaign |
| number | Trạng thái: 0=Draft, 1=Pending, 2=Running, 3=Paused, 4=Completed, 5=Cancelled |
| string | Loại campaign: |
| number | Tổng số khách hàng trong campaign |
| number | Số khách hàng đã được xử lý |
| number | Số cuộc gọi thành công (chỉ campaign voice) |
| number | Số cuộc gọi thất bại (chỉ campaign voice) |
| number | Chi phí thực tế (VND) |
| number | Số tiền đã hoàn trả (VND) |
| number | Số ZNS gửi thành công (chỉ campaign ZNS) |
{
"success": "0",
"error_code": "NOT_FOUND",
"error_message": "Campaign not found or access denied",
"totalRecord": 0,
"data": []
}
HTTP | error_code | Mô tả | FE xử lý |
|---|---|---|---|
401 | UNAUTHORIZED | Thiếu hoặc sai | Redirect nhập lại key |
403 | INSUFFICIENT_SCOPE | Key không có scope | 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 |
| Validate trước khi gọi API |
429 | RATE_LIMIT_EXCEEDED | Vượt giới hạn request | Retry sau |
500 | FAIL | Lỗi hệ thống | Toast lỗi chung |
/v1/campaigns/{campaignId}/results/exportExport 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/jsonvớ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.
Tham số | Bắt buộc | Kiểu | Mô tả | Ví dụ |
|---|---|---|---|---|
| Có | number (long) | ID campaign cần export |
|
Không có. Chỉ cần campaignId trên URL.
# 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())
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.
HTTP | error_code | Mô tả | FE xử lý |
|---|---|---|---|
401 | UNAUTHORIZED | Thiếu hoặc sai | Redirect nhập lại key |
403 | INSUFFICIENT_SCOPE | Key không có scope | 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 |
500 | FAIL | Lỗi hệ thống | Toast lỗi chung |
Header | Mô tả |
|---|---|
| Giới hạn tenant/10s |
| Còn lại tenant/10s |
| Giới hạn route/10s |
| Còn lại route/10s |
| Giây cần chờ khi bị 429 |