Authentication chung: Tất cả API sử dụng Kong dual-auth: Header
Authorization(API key) bắt buộc. HeaderX-Tenant-IDdo Kong inject — nếu test internal không qua Kong thì bỏ header này, backend tự resolve tenantId theo Authorization key. Không dùng JWT (x-access-token) trong luồng này.
/v1/campaigns/{id}/contact-listsLấy tất cả contact list thuộc 1 campaign.
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 |
curl -X GET "https://xapi.alohub.vn/v1/campaigns/1042/contact-lists" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310"const axios = require('axios');
const response = await axios.get(
'{{host}}/api/v1/campaigns/{{id}}/contact-lists',
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.get(
'{{host}}/api/v1/campaigns/{{id}}/contact-lists',
headers=headers
)
print(response.json())
{
"success": "1",
"totalRecord": 2,
"data": [
{
"listId": 501,
"listName": "Danh sach thang 4",
"contactCount": 150
},
{
"listId": 502,
"listName": "Default List",
"contactCount": 30
}
]
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| Tổng số list |
|
| ID contact list — dùng làm {listId} |
|
| Tên list |
|
| Số contact trong list |
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}/contact-lists/{listId}/contactsLấy danh sách contacts trong 1 contact list — có 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ụ |
|---|---|---|---|---|---|
| path | Có |
| campaignId | 1042 |
| path | Có |
| listId (từ GET /contact-lists) | 501 |
| 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/1042/contact-lists/501/contacts?page=0&size=20" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310"const axios = require('axios');
const response = await axios.get(
'{{host}}/api/v1/campaigns/{{id}}/contact-lists/{{listId}}/contacts?page=0&size=20',
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
params = {"page": "0", "size": "20"}
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.get(
'{{host}}/api/v1/campaigns/{{id}}/contact-lists/{{listId}}/contacts',
headers=headers, params=params
)
print(response.json())
{
"success": "1",
"totalRecord": 150,
"data": [
{
"contactId": 88001,
"customerId": 55001,
"phoneNumber": "0901234567",
"name": "Nguyen Van A",
"processStatus": 0,
"callStatus": null
}
]
}
Field | Kiểu | Mô tả | |
|---|---|---|---|
|
| "1" = thành công | |
|
| Tổng số contact | |
|
| ID contact — dùng làm {contactId} khi xoá | |
|
| ID khách hàng | |
|
| Số điện thoại | |
|
| null | Tên khách hàng |
|
| 0=pending, 1=in_call, 2=processed | |
|
| null | Trạng thái cuộc gọ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 |
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}/contactsThêm 1 contact vào campaign. Không truyền listId → dùng list đầu tiên. Chưa có list nào → tự tạo Default List.
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 |
{
"phoneNumber": "0901234567",
"name": "Nguyen Van A",
"listId": 501
}
Field | Bắt buộc | Mô tả |
|---|---|---|
| Có | Số điện thoại — bắt buộc |
| Không | Tên khách hàng |
| Không | ID list đích. Bỏ qua = dùng list đầu tiên / tạo Default List |
curl -X POST "https://xapi.alohub.vn/v1/campaigns/1042/contacts" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310" \
-H "Content-Type: application/json" \
-d '{"phoneNumber":"0901234567","name":"KH A"}'
# Truyền listId cụ thể
curl -X POST "https://xapi.alohub.vn/v1/campaigns/1042/contacts" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310" \
-H "Content-Type: application/json" \
-d '{"phoneNumber":"0901234567","listId":501}'const axios = require('axios');
const response = await axios.post(
'{{host}}/api/v1/campaigns/{{id}}/contacts',
{
"phoneNumber": "0901234567",
"name": "Nguyen Van A",
"listId": 501
},
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
payload = {
"phoneNumber": "0901234567",
"name": "Nguyen Van A",
"listId": 501
}
response = requests.post(
'{{host}}/api/v1/campaigns/{{id}}/contacts',
json=payload,
headers=headers
)
print(response.json())
{
"success": "1",
"error_code": "SUCCESS",
"error_message": "Contact added to list 501"
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| SUCCESS |
|
| "Contact added to list {listId}" |
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 |
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}/contacts/bulkImport hàng loạt contacts (async). Trả job_id ngay — FE phải poll GET /contacts/import/{job_id}. Contact trùng phone bị bỏ qua (đếm duplicateRows, không phải lỗi).
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 |
{
"listName": "Danh sach thang 4",
"contacts": [
{
"phoneNumber": "0901000001",
"name": "KH 1"
},
{
"phoneNumber": "0901000002"
}
]
}
Field | Bắt buộc | Mô tả |
|---|---|---|
| Không | Tên list. Bỏ qua = tự đặt tên |
| Có | Mảng contact — tối thiểu 1 item có phoneNumber |
| Có | Số điện thoại |
| Không | Tên khách hàng |
curl -X POST "https://xapi.alohub.vn/v1/campaigns/1042/contacts/bulk" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310" \
-H "Content-Type: application/json" \
-d '{"listName":"DS thang 4","contacts":[{"phoneNumber":"0901000001"},{"phoneNumber":"0901000002"}]}'const axios = require('axios');
const response = await axios.post(
'{{host}}/api/v1/campaigns/{{id}}/contacts/bulk',
{
"listName": "Danh sach thang 4",
"contacts": [
{
"phoneNumber": "0901000001",
"name": "KH 1"
},
{
"phoneNumber": "0901000002"
}
]
},
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
payload = {
"listName": "Danh sach thang 4",
"contacts": [
{
"phoneNumber": "0901000001",
"name": "KH 1"
},
{
"phoneNumber": "0901000002"
}
]
}
response = requests.post(
'{{host}}/api/v1/campaigns/{{id}}/contacts/bulk',
json=payload,
headers=headers
)
print(response.json())
{
"success": "1",
"error_code": "SUCCESS",
"error_message": "Bulk import started",
"job_id": 5,
"total_rows": 2
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| SUCCESS |
|
| "Bulk import started" |
|
| ID job — dùng để poll trạng thái |
|
| Tổng số dòng nhận được |
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 |
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}/contacts/import/{job_id}Poll trạng thái bulk import job. FE poll mỗi 2-3 giây cho đến khi status = done hoặc failed. job_id tồn tại trong RAM — server restart sẽ mất.
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 |
| path | Có |
| job_id nhận từ POST /contacts/bulk | 5 |
# Poll mỗi 2-3 giây
curl -X GET "https://xapi.alohub.vn/v1/campaigns/1042/contacts/import/5" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310"const axios = require('axios');
const response = await axios.get(
'{{host}}/api/v1/campaigns/{{id}}/contacts/import/{{job_id}}',
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.get(
'{{host}}/api/v1/campaigns/{{id}}/contacts/import/{{job_id}}',
headers=headers
)
print(response.json())
{
"jobId": 5,
"status": "processing",
"totalRows": 100,
"processedRows": 42,
"failedRows": 0,
"duplicateRows": 1
}
Field | Kiểu | Mô tả | |
|---|---|---|---|
|
| ID job | |
|
| pending / processing / done / failed | |
|
| Tổng số dòng | |
|
| Số dòng đã xử lý | |
|
| Số dòng lỗi | |
|
| Số dòng trùng bị bỏ qua | |
|
| null | Mô tả lỗi khi status=failed |
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}/contacts/{contactId}Xoá 1 contact khỏi campaign. Chỉ xoá được contact có processStatus=0 (pending). Contact đang gọi (1) hoặc đã xử lý (2) không thể xoá.
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 |
| path | Có |
| contactId (từ GET /contacts — field contactId) | 88001 |
curl -X DELETE "https://xapi.alohub.vn/v1/campaigns/1042/contacts/88001" \
-H "X-Api-Key: sk_live_xxx" \
-H "X-Tenant-ID: 20260310"const axios = require('axios');
const response = await axios.delete(
'{{host}}/api/v1/campaigns/{{id}}/contacts/{{contactId}}',
{ headers: { 'Authorization': '{{api-key}}', 'X-Tenant-ID': '{{tenant-id}}', 'Content-Type': 'application/json' } }
);
console.log(response.data);import requests
headers = {"Authorization": "{{api-key}}", "X-Tenant-ID": "{{tenant-id}}", "Content-Type": "application/json"}
response = requests.delete(
'{{host}}/api/v1/campaigns/{{id}}/contacts/{{contactId}}',
headers=headers
)
print(response.json())
{
"success": "1",
"error_code": "SUCCESS",
"error_message": "Contact deleted successfully"
}
Field | Kiểu | Mô tả |
|---|---|---|
|
| "1" = thành công |
|
| SUCCESS |
|
| "Contact deleted successfully" |
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 |
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 |