Theo dõi lỗi UnifyPort API bằng request_id và X-Request-Id
Để điều tra lỗi UnifyPort API, hãy lưu request_id từ JSON phản hồi hoặc X-Request-Id từ header phản hồi. Bạn có thể gửi X-Request-Id riêng trong header yêu cầu; JSON sẽ trả lại giá trị đó qua client_request_id để đối chiếu với log ứng dụng. Các ID này phục vụ truy vết, không phải ID tin nhắn, ID sự kiện webhook hay cam kết rằng gọi lại sẽ không thực hiện thao tác lần nữa.
Điểm chính
- Tách thao tác nghiệp vụ, từng lần gọi HTTP và ID yêu cầu phía máy chủ.
- Đọc header ngay cả khi không có JSON, bao gồm phản hồi xóa thành công
204. - Timeout có thể không cung cấp ID máy chủ và để lại kết quả chưa xác định.
- Ghi mã lỗi có cấu trúc, không ghi thông tin xác thực hoặc toàn bộ nội dung tin nhắn.
Cần lưu ID yêu cầu nào?
Tài liệu giới thiệu API xác định cơ chế truy vết. Cùng tên header nhưng vai trò khác nhau tùy chiều truyền:
| Giá trị | Nguồn | Mục đích |
|---|---|---|
X-Request-Id của bạn | Header yêu cầu phía máy khách | Tìm lần gọi HTTP trong log của bạn |
client_request_id | Giá trị máy khách được trả lại trong JSON | Ghép phản hồi với lần gọi tương ứng |
request_id | JSON phản hồi của máy chủ | Cung cấp mã tham chiếu phía máy chủ cho hỗ trợ |
X-Request-Id phản hồi | Header máy chủ | Giữ thông tin truy vết khi không có body |
data.message_id | Kết quả gửi thành công, nếu được trả về | Nhận diện tin nhắn, không phải yêu cầu HTTP |
Bản cập nhật API tháng 6 đã giới thiệu các trường này. Bài viết hiện tại tập trung vào việc giữ chúng trong mọi nhánh kết quả, thay vì chỉ biết tên trường.
Nên tạo ID nội bộ cho một thao tác nghiệp vụ, chẳng hạn một phản hồi đã được duyệt. Sau đó tạo ID riêng cho từng lần gọi HTTP và lưu quan hệ giữa chúng. Đây là thiết kế ứng dụng được đề xuất, không phải trường yêu cầu bổ sung của UnifyPort. Không đưa tên khách hàng, số điện thoại, nội dung tin nhắn hoặc khóa bí mật vào ID.
Ghi một lần gọi mà không thêm retry tự động
Bắt đầu bằng thao tác chỉ đọc workspace hiện tại. Ví dụ Node.js dưới đây dùng fetch tích hợp và lấy khóa từ biến môi trường phía backend UNIFYPORT_API_KEY. Log chỉ chứa những trường chẩn đoán được chọn, không chứa header yêu cầu hay toàn bộ body phản hồi.
Thời gian timeout là ví dụ về chính sách máy khách, không phải giới hạn dịch vụ. Hàm chỉ thực hiện một lần gọi ở tầng ứng dụng và trả Response gốc cho bên gọi xử lý tiếp.
import { randomUUID } from 'node:crypto';
async function tracedWorkspaceRead(apiKey, operationId) {
const clientAttemptId = randomUUID();
const startedAt = new Date().toISOString();
const startedMs = Date.now();
let response;
try {
response = await fetch('https://api.unifyport.ai/v1/workspace', {
headers: {
'X-Api-Key': apiKey,
'X-Request-Id': clientAttemptId
},
signal: AbortSignal.timeout(10000)
});
} catch {
console.info({
operation_id: operationId,
client_attempt_id: clientAttemptId,
started_at: startedAt,
elapsed_ms: Date.now() - startedMs,
outcome: 'no_http_response'
});
throw new Error('No HTTP response; inspect the local attempt record');
}
const headerId = response.headers.get('X-Request-Id');
const body = response.status === 204
? null
: await response.clone().json().catch(() => null);
console.info({
operation_id: operationId,
client_attempt_id: clientAttemptId,
started_at: startedAt,
elapsed_ms: Date.now() - startedMs,
http_status: response.status,
server_request_id_header: headerId,
server_request_id_body: body?.request_id ?? null,
echoed_client_request_id: body?.client_request_id ?? null,
error_code: body?.error?.code ?? null,
numeric_code: body?.error?.numeric_code ?? null
});
return response;
}
const apiKey = process.env.UNIFYPORT_API_KEY;
if (!apiKey) throw new Error('Configure UNIFYPORT_API_KEY');
await tracedWorkspaceRead(apiKey, randomUUID());
Các tên như operation_id và outcome là trường log nội bộ, không phải schema phản hồi API. Nhánh 204 giúp tái sử dụng cách xử lý cho những thao tác được tài liệu xác định là không có body; riêng việc đọc workspace thành công trả về 200. Hãy dùng mock cục bộ để kiểm tra phản hồi rỗng, không xóa tài khoản nhắn tin chỉ để thử log.
Giữ cả giá trị trong header và body giúp phát hiện dữ liệu thiếu hoặc không nhất quán. Phản hồi từ tầng trung gian có thể không theo định dạng API. Hãy giữ trạng thái HTTP và bản ghi lần gọi nội bộ, không tự tạo ID rồi coi đó là bằng chứng từ máy chủ. Trong production, cần giới hạn kích thước dữ liệu phân tích và log, kiểm soát quyền truy cập và thời gian lưu.
Biến bản ghi thành báo cáo lỗi hữu ích
Tài liệu lỗi định nghĩa error.code, error.numeric_code và error.message. Rẽ nhánh theo code hoặc numeric_code, không theo câu mô tả cho người đọc. Mã số có thể làm rõ nguyên nhân từ nhà cung cấp mà không thay đổi mã cũ hoặc trạng thái HTTP.
| Kết quả quan sát | Bằng chứng cần giữ | Quyết định tiếp theo |
|---|---|---|
| JSON thành công hoặc lỗi | Trạng thái, ID máy chủ, giá trị máy khách trả lại, mã lỗi | Diễn giải theo endpoint cụ thể |
204 không có body | Trạng thái và header phản hồi X-Request-Id | Không coi thiếu JSON là API thất bại |
| Body không đọc được hoặc sai định dạng | Trạng thái, ID header nếu có, ID lần gọi nội bộ | Kiểm tra đường đi của phản hồi |
| Không có phản hồi HTTP | ID nội bộ, thời điểm bắt đầu, thao tác | Giữ trạng thái chưa rõ cho đến khi đối soát |
Báo cáo hỗ trợ nên gồm môi trường, thời điểm UTC, phương thức HTTP và mẫu đường dẫn, ID máy chủ nếu nhận được, ID lần gọi nội bộ, trạng thái, mã lỗi và mô tả ngắn về kết quả mong đợi so với thực tế. Chỉ chia sẻ ID tài khoản hoặc hội thoại qua kênh phù hợp có hạn chế truy cập. Loại bỏ API key, dữ liệu phiên, signing secret, reply token và URL media có chữ ký truy cập.
Hướng dẫn xử lý sự cố X Chat minh họa cách các bản ghi này giúp phân biệt lỗi tài khoản với lỗi của một hội thoại. ID yêu cầu giúp tìm bằng chứng, không tự xác định nguyên nhân.
Truy vết không đồng nghĩa với quyền gửi lại
Dùng lại cùng ID máy khách cho POST /v1/messages không phải cơ chế idempotency được tài liệu cam kết. Nếu gửi bị timeout, thao tác vẫn có thể đã diễn ra dù máy khách không nhận được phản hồi. Ghi nhận sự không chắc chắn trước khi quyết định gửi thêm lần nữa.
Điều này khác với cơ chế retry key của LINE. LINE quy định x-line-accepted-request-id trong phản hồi khi yêu cầu đã được chấp nhận trước đó. Không suy ra cùng hành vi chỉ vì header truy vết của UnifyPort có tên tương tự. Xem quy trình riêng trong hướng dẫn LINE retry key.
Liên kết webhook cũng là một tầng riêng. Tài liệu chuyển phát định nghĩa X-Device-Event-Id và X-Device-Delivery-Id; giá trị sau có thể dùng event ID làm giá trị dự phòng nên không đảm bảo duy nhất cho từng lần gọi HTTP. Không ghép REST với sự kiện chỉ vì cả hai đều có ID. Nếu cần mã duy nhất cho từng lần nhận, hãy tạo mã nội bộ. Quy tắc khử trùng lặp phải theo từng loại sự kiện, bao gồm ngoại lệ HistorySync, độc lập với log. Với hệ thống dùng cả WhatsApp và Zalo, cùng cách ghi log không có nghĩa là mọi sự kiện hay khả năng của hai kênh đều giống nhau.
Câu hỏi thường gặp
Vì sao xóa thành công nhưng không có request_id?
Phản hồi 204 không có JSON body. Hãy đọc header phản hồi X-Request-Id.
Có thể dùng request_id để tra trạng thái gửi không?
Cơ chế truy vết không định nghĩa endpoint tra trạng thái yêu cầu. Lưu kết quả thực tế và đối soát thao tác chưa rõ qua bằng chứng được hỗ trợ, không tự ghép URL mới từ ID.
client_request_id có phải idempotency key không?
Tài liệu không cam kết như vậy. Trường này trả lại giá trị máy khách để đối chiếu, không ngăn gửi trùng.
Bước tiếp theo và nguồn
Thêm bản tóm tắt chẩn đoán vào một API client hiện có. Dùng mock cục bộ để thử lỗi JSON, phản hồi rỗng, body sai định dạng và lỗi mạng. Dựa vào tài liệu lỗi để viết các nhánh xử lý.
Đã kiểm tra ngày 2026-10-01:
Biến tích hợp nhắn tin thành một pipeline sản phẩm ổn định.
Bắt đầu bằng cách gửi qua một API, rồi đưa mọi tin nhắn inbound trở lại hệ thống kinh doanh bằng sự kiện chuẩn.