Tài liệu API v1 · sẽ ngừng
API v1 (cũ)
v1 vẫn chạy ít nhất 6 tháng để phần mềm cũ không bị gián đoạn, nhưng không thêm thao tác mới. Mọi phản hồi v1 có header Deprecation và liên kết sang v2. Dùng API v2 và MCP cho tích hợp mới — đủ mọi thao tác của ứng dụng.
Xác thực & quyền
Chủ hoặc quản trị workspace tạo khoá trong Cài đặt → Kết nối AI & API. Khoá chỉ hiện một lần; WA Auto chỉ lưu bản băm. Gửi khoá trong header:
curl /api/public/v1/contacts \
-H "Authorization: Bearer waa_live_…"Mỗi khoá chỉ làm được những gì đã chọn khi tạo:
| Quyền | Cho phép |
|---|---|
contacts.read | Đọc khách hàng — Xem danh sách và hồ sơ khách |
contacts.write | Ghi khách hàng — Tạo, sửa khách, gắn/bỏ nhãn |
conversations.read | Đọc hội thoại — Xem hội thoại và tin nhắn |
messages.send | Gửi tin — Gửi tin qua cổng giao việc, chịu cùng hạn mức |
campaigns | Chiến dịch — Xem chiến dịch, thêm khách vào chiến dịch |
API có ở mọi gói. Khoá bị thu hồi hoặc gói hết hạn thì mọi yêu cầu bị từ chối ngay. Khoá tạo trước Build 1.3 là “khoá cũ”: giữ đúng các quyền trên.
Giới hạn & phân trang
- Tối đa 60 yêu cầu mỗi phút cho một khoá; quá thì trả
429kèmRetry-After. Mọi phản hồi cóRateLimit-Limit,RateLimit-Remaining,RateLimit-Reset. - Mọi lệnh
POSTcần headerIdempotency-Key(1–255 ký tự, mỗi lệnh một giá trị mới). Gửi lại cùng khoá + cùng nội dung trong 24 giờ nhận đúng kết quả cũ kèmIdempotent-Replayed: true— không có việc đôi. Cùng khoá mà nội dung khác →422; yêu cầu đầu còn chạy →409. Lỗi máy chủ (5xx) không được nhớ, gửi lại sẽ chạy thật. - Thân yêu cầu tối đa 64 KiB, tính theo byte.
- Danh sách trả
{ data, nextCursor }. Gửi lạicursor=<nextCursor>để lấy trang sau;limittối đa 100. - Thời gian theo chuẩn ISO 8601 (UTC). Số điện thoại trả về dạng +84…
Mã lỗi
Lỗi luôn có dạng { "error": { "code": "…", "message": "…" } }.
| HTTP | code | Ý nghĩa |
|---|---|---|
| 400 | invalid_json | Nội dung không phải JSON. |
| 400 | idempotency_key_required / invalid_idempotency_key | Lệnh POST thiếu tiêu đề Idempotency-Key, hoặc khoá không phải 1–255 ký tự in được. |
| 401 | missing_key / invalid_key | Thiếu khoá, sai khoá hoặc khoá đã thu hồi. |
| 403 | missing_scope | Khoá không có quyền cho thao tác này. |
| 403 | plan_no_api / plan_expired | Gói chưa có API hoặc đã hết hạn. |
| 404 | not_found | Không có đối tượng này trong workspace của khoá. |
| 409 | duplicate_phone / do_not_contact / campaign_closed | Trùng số, khách đã xin dừng nhận tin, hoặc chiến dịch đã kết thúc. |
| 409 | request_in_progress | Yêu cầu đầu với cùng Idempotency-Key còn đang chạy; thử lại theo Retry-After. |
| 413 | body_too_large | Nội dung quá 64 KiB (tính theo byte). |
| 422 | invalid_input | Dữ liệu chưa hợp lệ; xem details. |
| 422 | idempotency_key_reused | Idempotency-Key đã dùng cho một yêu cầu có nội dung khác. |
| 429 | rate_limited | Quá 60 yêu cầu/phút cho khoá; chờ theo header Retry-After. |
| 500 | server_error | Lỗi máy chủ; thử lại sau (cùng Idempotency-Key — lỗi máy chủ không được nhớ). |
| 503 | rate_limit_unavailable / idempotency_unavailable | Tạm thời không kiểm được giới hạn hoặc khoá chống trùng; lệnh không chạy, thử lại theo Retry-After. |
Khách hàng
/api/public/v1/contactsquyền contacts.readDanh sách khách
Mới nhất trước. Lọc theo từ khoá, nhãn hoặc số điện thoại.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
q | query | Tìm theo tên, số, email, công ty. |
tag | query | Tên nhãn. |
phone | query | Số điện thoại (0901…, +84901…). |
limit | query | Số dòng mỗi trang, 1–100 (mặc định 50). |
cursor | query | Giá trị nextCursor của trang trước. |
curl -X GET "/api/public/v1/contacts" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": [
{
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "+84901234567",
"name": "Chị Lan",
"whatsappName": "Lan Spa Hoa",
"email": "lan@example.com",
"company": "Spa Hoa",
"notes": null,
"stageId": null,
"dealValue": 2500000,
"doNotContact": false,
"tags": [
"VIP"
],
"createdAt": "2026-10-17T03:15:00.000Z",
"lastInboundAt": "2026-10-17T07:28:00.000Z",
"lastOutboundAt": "2026-10-17T07:29:00.000Z"
}
],
"nextCursor": "MjAyNi0xMC0xN1QwMzoxNTowMC4wMDBafDdiMDI0ZTA2…"
}/api/public/v1/contactsquyền contacts.writeTạo khách
Số được chuẩn hoá theo quốc gia mặc định của workspace. Trùng số trả 409 kèm contactId có sẵn.
curl -X POST "/api/public/v1/contacts" \
-H "Authorization: Bearer waa_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"phone":"0901234567","name":"Chị Lan","email":"lan@example.com","company":"Spa Hoa","tags":["Website"]}'Trả về 201
{
"data": {
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "+84901234567",
"name": "Chị Lan",
"whatsappName": "Lan Spa Hoa",
"email": "lan@example.com",
"company": "Spa Hoa",
"notes": null,
"stageId": null,
"dealValue": 2500000,
"doNotContact": false,
"tags": [
"VIP"
],
"createdAt": "2026-10-17T03:15:00.000Z",
"lastInboundAt": "2026-10-17T07:28:00.000Z",
"lastOutboundAt": "2026-10-17T07:29:00.000Z"
}
}/api/public/v1/contacts/{contactId}quyền contacts.readXem một khách
| Tham số | Ở đâu | Mô tả |
|---|---|---|
contactId | đường dẫn | Mã khách. |
curl -X GET "/api/public/v1/contacts/{contactId}" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": {
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "+84901234567",
"name": "Chị Lan",
"whatsappName": "Lan Spa Hoa",
"email": "lan@example.com",
"company": "Spa Hoa",
"notes": null,
"stageId": null,
"dealValue": 2500000,
"doNotContact": false,
"tags": [
"VIP"
],
"createdAt": "2026-10-17T03:15:00.000Z",
"lastInboundAt": "2026-10-17T07:28:00.000Z",
"lastOutboundAt": "2026-10-17T07:29:00.000Z"
}
}/api/public/v1/contacts/{contactId}quyền contacts.writeSửa khách
Chỉ gửi trường cần đổi. Không đổi được số điện thoại qua API.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
contactId | đường dẫn | Mã khách. |
curl -X PATCH "/api/public/v1/contacts/{contactId}" \
-H "Authorization: Bearer waa_live_…" \
-H "Content-Type: application/json" \
-d '{"name":"Chị Lan (Spa Hoa Q3)","dealValue":3000000}'Trả về 200
{
"data": {
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "+84901234567",
"name": "Chị Lan",
"whatsappName": "Lan Spa Hoa",
"email": "lan@example.com",
"company": "Spa Hoa",
"notes": null,
"stageId": null,
"dealValue": 2500000,
"doNotContact": false,
"tags": [
"VIP"
],
"createdAt": "2026-10-17T03:15:00.000Z",
"lastInboundAt": "2026-10-17T07:28:00.000Z",
"lastOutboundAt": "2026-10-17T07:29:00.000Z"
}
}/api/public/v1/contacts/{contactId}/tagsquyền contacts.writeGắn / bỏ nhãn
Nhãn chưa có sẽ được tạo. Gắn nhãn kích hoạt luật tự động “Khách được gắn nhãn”.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
contactId | đường dẫn | Mã khách. |
curl -X POST "/api/public/v1/contacts/{contactId}/tags" \
-H "Authorization: Bearer waa_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"add":["VIP"],"remove":["Mới"]}'Trả về 200
{
"data": {
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"tags": [
"VIP"
]
}
}Hội thoại
/api/public/v1/conversationsquyền conversations.readDanh sách hội thoại
Mới có tin trước.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
accountId | query | Chỉ hội thoại của một số WhatsApp. |
status | query | open | pending | closed |
unanswered | query | true: chỉ hội thoại khách đang chờ trả lời. |
limit | query | Số dòng mỗi trang, 1–100 (mặc định 50). |
cursor | query | Giá trị nextCursor của trang trước. |
curl -X GET "/api/public/v1/conversations" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": [
{
"id": "1cc08299-a808-4936-a102-c4045341130f",
"accountId": "a3e6f771-6ab9-4614-9165-7be3d240ffcf",
"type": "direct",
"contactId": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"title": "Chị Lan",
"status": "open",
"assigneeMemberId": null,
"unread": 2,
"waitingSince": "2026-10-17T07:28:00.000Z",
"archived": false,
"lastMessageAt": "2026-10-17T07:28:00.000Z",
"lastMessagePreview": "Cho mình hỏi giá combo"
}
],
"nextCursor": null
}/api/public/v1/conversations/{conversationId}quyền conversations.readXem một hội thoại
| Tham số | Ở đâu | Mô tả |
|---|---|---|
conversationId | đường dẫn | Mã hội thoại. |
curl -X GET "/api/public/v1/conversations/{conversationId}" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": {
"id": "1cc08299-a808-4936-a102-c4045341130f",
"type": "direct",
"status": "open"
}
}/api/public/v1/conversations/{conversationId}/messagesquyền conversations.readTin nhắn trong hội thoại
Mới nhất trước. Tệp đính kèm chỉ có tên và loại tệp.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
conversationId | đường dẫn | Mã hội thoại. |
limit | query | Số dòng mỗi trang, 1–100 (mặc định 50). |
cursor | query | Giá trị nextCursor của trang trước. |
curl -X GET "/api/public/v1/conversations/{conversationId}/messages" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": [
{
"id": "4f0c…",
"direction": "in",
"kind": "text",
"text": "Cho mình hỏi giá combo",
"status": "sent",
"at": "2026-10-17T07:28:00.000Z",
"sentBy": "contact",
"contactId": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"campaignId": null,
"attachments": []
}
],
"nextCursor": null
}Gửi tin
/api/public/v1/messagesquyền messages.sendGửi tin
Tin được xếp vào cổng giao việc như mọi tin khác: luôn chịu danh sách loại trừ (STOP), và chịu hạn mức, làm nóng, giờ gửi nếu chủ số đã bật. Gửi cho người đã nhắn tới số này tính là tin tự động; gửi cho người mới đi làn chiến dịch (chậm hơn, có thể bị hoãn). Trả 202 khi đã xếp hàng — theo dõi kết quả bằng webhook message.status. Gửi lại cùng Idempotency-Key không gửi trùng (trường idempotencyKey trong thân vẫn nhận như tên cũ của tiêu đề, sẽ bỏ).
curl -X POST "/api/public/v1/messages" \
-H "Authorization: Bearer waa_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"accountId":"a3e6f771-6ab9-4614-9165-7be3d240ffcf","phone":"0901234567","text":"Chào chị Lan, lịch hẹn của chị là 10h sáng thứ 7 ạ."}'Trả về 202
{
"data": {
"messageId": "9d2e…",
"jobId": "c51a…",
"conversationId": "1cc08299-a808-4936-a102-c4045341130f",
"status": "sending",
"duplicate": false,
"lane": "reply"
}
}Chiến dịch
/api/public/v1/campaignsquyền campaignsDanh sách chiến dịch
| Tham số | Ở đâu | Mô tả |
|---|---|---|
limit | query | Số dòng mỗi trang, 1–100 (mặc định 50). |
curl -X GET "/api/public/v1/campaigns" \
-H "Authorization: Bearer waa_live_…"Trả về 200
{
"data": [
{
"id": "e1b0…",
"name": "Chăm sóc khách tháng 10",
"kind": "sequence",
"status": "running",
"targets": 120,
"startedAt": "2026-10-15T02:00:00.000Z",
"finishedAt": null,
"createdAt": "2026-10-14T09:00:00.000Z"
}
]
}/api/public/v1/campaigns/{campaignId}/enrollquyền campaignsThêm khách vào chiến dịch
Bản nháp: khách được thêm vào danh sách, lọc an toàn khi bắt đầu. Đã hẹn giờ: chỉ thêm người qua bộ lọc an toàn ngay bây giờ (`excluded` đếm theo lý do), lọc lại lúc bắt đầu. Đang chạy hoặc tạm dừng: lọc an toàn ngay, người không đạt được ghi lý do.
| Tham số | Ở đâu | Mô tả |
|---|---|---|
campaignId | đường dẫn | Mã chiến dịch. |
curl -X POST "/api/public/v1/campaigns/{campaignId}/enroll" \
-H "Authorization: Bearer waa_live_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"contactIds":["7b024e06-36ae-46d0-91d1-2185cd71837f"]}'Trả về 200
{
"data": {
"added": 1,
"enrolled": 1,
"alreadyIn": 0,
"excluded": {},
"unknown": []
}
}Webhook — cách nhận
Thêm địa chỉ https trong Cài đặt → Kết nối AI & API → Webhook và chọn sự kiện. WA Auto gửi POST JSON:
{
"specversion": "1.0",
"id": "b6f1c2d0-6c55-4c1e-9a43-5d1d3b1e7a10",
"type": "message.received",
"version": 1,
"time": "2026-10-17T07:28:01.000Z",
"source": "wa-auto/<workspace-id>",
"datacontenttype": "application/json",
"data": {
"…": "…"
},
"event": "message.received",
"at": "2026-10-17T07:28:01.000Z"
}Thân theo CloudEvents 1.0. version là phiên bản hình dạng data của loại sự kiện — đổi hình dạng thì tăng phiên bản. event và at là tên cũ của type và time, vẫn gửi trong bản này và sẽ bỏ.
- Trả mã
2xxtrong 8 giây. Lỗi thì thử lại sau 1 phút, 5 phút, 30 phút, 2 giờ, 6 giờ. - Giao ít nhất một lần: có thể nhận trùng khi mạng chập chờn — lọc theo
id(cũng ở headerwebhook-idvàx-wa-auto-delivery). - Không bảo đảm thứ tự: sắp theo
timenếu cần. - Hết lượt thử, hoặc webhook bị tắt, sự kiện chuyển sang Không gửi được (giữ 30 ngày) trong Cài đặt → Kết nối AI & API. Sửa máy nhận rồi bấm Gửi lại: sự kiện đi lại với đúng
idcũ. - Sau 20 lần lỗi liên tiếp webhook tự tắt và chủ workspace nhận thông báo; bật lại thì được hỏi có gửi lại các sự kiện bị dừng không.
Kiểm chữ ký
Header x-wa-auto-signature = sha256= + HMAC-SHA256 của chuỗi <timestamp>.<body gốc> với bí mật ký; timestamp nằm ở header x-wa-auto-timestamp. Từ chối yêu cầu lệch quá 5 phút.
Cùng bí mật ký, WA Auto gửi thêm chữ ký chuẩn Standard Webhooks (webhook-id, webhook-timestamp, webhook-signature) — kiểm bằng thư viện có sẵn:
// npm i standardwebhooks — dùng đúng bí mật ký whsec_… của webhook
import { Webhook } from "standardwebhooks";
const wh = new Webhook(secret);
const event = wh.verify(rawBody, {
"webhook-id": req.header("webhook-id"),
"webhook-timestamp": req.header("webhook-timestamp"),
"webhook-signature": req.header("webhook-signature"),
}); // ném lỗi nếu sai chữ ký hoặc quá cũTự kiểm — Node.js
import crypto from "node:crypto";
// Express: app.post("/wa-auto", express.raw({ type: "application/json" }), handler)
function verify(req, secret) {
const ts = req.header("x-wa-auto-timestamp");
const sig = req.header("x-wa-auto-signature"); // "sha256=<hex>"
if (!ts || !sig || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(ts + "." + req.body).digest("hex");
return sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Tự kiểm — Python
import hmac, hashlib, time
def verify(headers, raw_body: bytes, secret: str) -> bool:
ts = headers.get("x-wa-auto-timestamp")
sig = headers.get("x-wa-auto-signature", "")
if not ts or abs(time.time() - int(ts)) > 300:
return False
expected = "sha256=" + hmac.new(secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(sig, expected)Sự kiện
message.receivedversion 1Có tin nhắn đến
{
"id": "4f0c…",
"conversationId": "1cc08299-a808-4936-a102-c4045341130f",
"accountId": "a3e6f771-6ab9-4614-9165-7be3d240ffcf",
"contactId": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"kind": "text",
"text": "Cho mình hỏi giá combo",
"at": "2026-10-17T07:28:00.000Z"
}message.statusversion 1Tin đi đổi trạng thái (đã gửi / đã tới / đã xem / lỗi)
{
"id": "9d2e…",
"conversationId": "1cc08299-a808-4936-a102-c4045341130f",
"accountId": "a3e6f771-6ab9-4614-9165-7be3d240ffcf",
"contactId": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"status": "read",
"sentBy": "api",
"campaignId": null,
"at": "2026-10-17T07:31:12.000Z"
}contact.createdversion 1Khách mới được tạo
{
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "84901234567",
"name": "Chị Lan",
"email": null,
"company": null,
"source": "inbound",
"createdAt": "2026-10-17T03:15:00.000Z"
}contact.updatedversion 1Hồ sơ khách thay đổi
{
"id": "7b024e06-36ae-46d0-91d1-2185cd71837f",
"phone": "84901234567",
"name": "Chị Lan",
"email": "lan@example.com",
"company": "Spa Hoa",
"stageId": null,
"dealValue": 2500000,
"doNotContact": false,
"deleted": false,
"mergedInto": null
}campaign.finishedversion 1Chiến dịch kết thúc
{
"id": "e1b0…",
"name": "Chăm sóc khách tháng 10",
"status": "completed"
}account.state_changedversion 1Số WhatsApp đổi trạng thái
{
"id": "a3e6f771-6ab9-4614-9165-7be3d240ffcf",
"phone": "84909203723",
"label": "Sale 01",
"sessionState": "logged_out",
"safetyState": "normal",
"previousSessionState": "online",
"previousSafetyState": "normal"
}